From 9e2b2a32fd0e967ad3184e9a5d091a29953acb91 Mon Sep 17 00:00:00 2001 From: Frederic Guillot Date: Wed, 25 Oct 2017 16:22:10 -0700 Subject: [PATCH] Include composer dependencies in repo --- .gitattributes | 21 +- .gitignore | 1 - .travis.yml | 2 +- Dockerfile | 1 - Makefile | 2 +- doc/en_US/installation.markdown | 5 +- doc/en_US/update.markdown | 3 +- doc/es_ES/installation.markdown | 6 +- doc/es_ES/update.markdown | 3 +- doc/fr_FR/installation.markdown | 5 +- doc/fr_FR/update.markdown | 3 +- doc/ru_RU/installation.markdown | 10 +- doc/ru_RU/update.markdown | 5 +- doc/tr_TR/installation.markdown | 5 +- doc/tr_TR/update.markdown | 3 +- vendor/aferrandini/phpqrcode/.gitignore | 1 + vendor/aferrandini/phpqrcode/LICENSE | 165 ++ vendor/aferrandini/phpqrcode/VERSION | 2 + .../aferrandini/phpqrcode/cache/frame_1.dat | 2 + .../aferrandini/phpqrcode/cache/frame_1.png | Bin 0 -> 126 bytes .../aferrandini/phpqrcode/cache/frame_10.dat | Bin 0 -> 204 bytes .../aferrandini/phpqrcode/cache/frame_10.png | Bin 0 -> 202 bytes .../aferrandini/phpqrcode/cache/frame_11.dat | Bin 0 -> 210 bytes .../aferrandini/phpqrcode/cache/frame_11.png | Bin 0 -> 205 bytes .../aferrandini/phpqrcode/cache/frame_12.dat | Bin 0 -> 222 bytes .../aferrandini/phpqrcode/cache/frame_12.png | Bin 0 -> 216 bytes .../aferrandini/phpqrcode/cache/frame_13.dat | Bin 0 -> 223 bytes .../aferrandini/phpqrcode/cache/frame_13.png | Bin 0 -> 210 bytes .../aferrandini/phpqrcode/cache/frame_14.dat | Bin 0 -> 227 bytes .../aferrandini/phpqrcode/cache/frame_14.png | Bin 0 -> 213 bytes .../aferrandini/phpqrcode/cache/frame_15.dat | Bin 0 -> 242 bytes .../aferrandini/phpqrcode/cache/frame_15.png | Bin 0 -> 219 bytes .../aferrandini/phpqrcode/cache/frame_16.dat | 1 + .../aferrandini/phpqrcode/cache/frame_16.png | Bin 0 -> 211 bytes .../aferrandini/phpqrcode/cache/frame_17.dat | Bin 0 -> 237 bytes .../aferrandini/phpqrcode/cache/frame_17.png | Bin 0 -> 211 bytes .../aferrandini/phpqrcode/cache/frame_18.dat | 2 + .../aferrandini/phpqrcode/cache/frame_18.png | Bin 0 -> 228 bytes .../aferrandini/phpqrcode/cache/frame_19.dat | 3 + .../aferrandini/phpqrcode/cache/frame_19.png | Bin 0 -> 225 bytes .../aferrandini/phpqrcode/cache/frame_2.dat | 1 + .../aferrandini/phpqrcode/cache/frame_2.png | Bin 0 -> 144 bytes .../aferrandini/phpqrcode/cache/frame_20.dat | Bin 0 -> 250 bytes .../aferrandini/phpqrcode/cache/frame_20.png | Bin 0 -> 225 bytes .../aferrandini/phpqrcode/cache/frame_21.dat | 1 + .../aferrandini/phpqrcode/cache/frame_21.png | Bin 0 -> 235 bytes .../aferrandini/phpqrcode/cache/frame_22.dat | 3 + .../aferrandini/phpqrcode/cache/frame_22.png | Bin 0 -> 226 bytes .../aferrandini/phpqrcode/cache/frame_23.dat | 3 + .../aferrandini/phpqrcode/cache/frame_23.png | Bin 0 -> 220 bytes .../aferrandini/phpqrcode/cache/frame_24.dat | 1 + .../aferrandini/phpqrcode/cache/frame_24.png | Bin 0 -> 242 bytes .../aferrandini/phpqrcode/cache/frame_25.dat | 3 + .../aferrandini/phpqrcode/cache/frame_25.png | Bin 0 -> 242 bytes .../aferrandini/phpqrcode/cache/frame_26.dat | 2 + .../aferrandini/phpqrcode/cache/frame_26.png | Bin 0 -> 244 bytes .../aferrandini/phpqrcode/cache/frame_27.dat | Bin 0 -> 284 bytes .../aferrandini/phpqrcode/cache/frame_27.png | Bin 0 -> 237 bytes .../aferrandini/phpqrcode/cache/frame_28.dat | Bin 0 -> 318 bytes .../aferrandini/phpqrcode/cache/frame_28.png | Bin 0 -> 234 bytes .../aferrandini/phpqrcode/cache/frame_29.dat | 2 + .../aferrandini/phpqrcode/cache/frame_29.png | Bin 0 -> 232 bytes .../aferrandini/phpqrcode/cache/frame_3.dat | 1 + .../aferrandini/phpqrcode/cache/frame_3.png | Bin 0 -> 147 bytes .../aferrandini/phpqrcode/cache/frame_30.dat | Bin 0 -> 324 bytes .../aferrandini/phpqrcode/cache/frame_30.png | Bin 0 -> 255 bytes .../aferrandini/phpqrcode/cache/frame_31.dat | 1 + .../aferrandini/phpqrcode/cache/frame_31.png | Bin 0 -> 260 bytes .../aferrandini/phpqrcode/cache/frame_32.dat | 2 + .../aferrandini/phpqrcode/cache/frame_32.png | Bin 0 -> 262 bytes .../aferrandini/phpqrcode/cache/frame_33.dat | 14 + .../aferrandini/phpqrcode/cache/frame_33.png | Bin 0 -> 253 bytes .../aferrandini/phpqrcode/cache/frame_34.dat | Bin 0 -> 331 bytes .../aferrandini/phpqrcode/cache/frame_34.png | Bin 0 -> 256 bytes .../aferrandini/phpqrcode/cache/frame_35.dat | Bin 0 -> 342 bytes .../aferrandini/phpqrcode/cache/frame_35.png | Bin 0 -> 243 bytes .../aferrandini/phpqrcode/cache/frame_36.dat | Bin 0 -> 370 bytes .../aferrandini/phpqrcode/cache/frame_36.png | Bin 0 -> 272 bytes .../aferrandini/phpqrcode/cache/frame_37.dat | Bin 0 -> 376 bytes .../aferrandini/phpqrcode/cache/frame_37.png | Bin 0 -> 279 bytes .../aferrandini/phpqrcode/cache/frame_38.dat | 1 + .../aferrandini/phpqrcode/cache/frame_38.png | Bin 0 -> 279 bytes .../aferrandini/phpqrcode/cache/frame_39.dat | Bin 0 -> 404 bytes .../aferrandini/phpqrcode/cache/frame_39.png | Bin 0 -> 264 bytes .../aferrandini/phpqrcode/cache/frame_4.dat | 1 + .../aferrandini/phpqrcode/cache/frame_4.png | Bin 0 -> 149 bytes .../aferrandini/phpqrcode/cache/frame_40.dat | 2 + .../aferrandini/phpqrcode/cache/frame_40.png | Bin 0 -> 267 bytes .../aferrandini/phpqrcode/cache/frame_5.dat | 1 + .../aferrandini/phpqrcode/cache/frame_5.png | Bin 0 -> 150 bytes .../aferrandini/phpqrcode/cache/frame_6.dat | Bin 0 -> 132 bytes .../aferrandini/phpqrcode/cache/frame_6.png | Bin 0 -> 151 bytes .../aferrandini/phpqrcode/cache/frame_7.dat | Bin 0 -> 196 bytes .../aferrandini/phpqrcode/cache/frame_7.png | Bin 0 -> 189 bytes .../aferrandini/phpqrcode/cache/frame_8.dat | Bin 0 -> 201 bytes .../aferrandini/phpqrcode/cache/frame_8.png | Bin 0 -> 204 bytes .../aferrandini/phpqrcode/cache/frame_9.dat | Bin 0 -> 206 bytes .../aferrandini/phpqrcode/cache/frame_9.png | Bin 0 -> 199 bytes .../phpqrcode/cache/mask_0/mask_101_0.dat | Bin 0 -> 157 bytes .../phpqrcode/cache/mask_0/mask_105_0.dat | Bin 0 -> 162 bytes .../phpqrcode/cache/mask_0/mask_109_0.dat | 2 + .../phpqrcode/cache/mask_0/mask_113_0.dat | 2 + .../phpqrcode/cache/mask_0/mask_117_0.dat | 2 + .../phpqrcode/cache/mask_0/mask_121_0.dat | 1 + .../phpqrcode/cache/mask_0/mask_125_0.dat | 2 + .../phpqrcode/cache/mask_0/mask_129_0.dat | 2 + .../phpqrcode/cache/mask_0/mask_133_0.dat | 2 + .../phpqrcode/cache/mask_0/mask_137_0.dat | 1 + .../phpqrcode/cache/mask_0/mask_141_0.dat | 2 + .../phpqrcode/cache/mask_0/mask_145_0.dat | 2 + .../phpqrcode/cache/mask_0/mask_149_0.dat | 3 + .../phpqrcode/cache/mask_0/mask_153_0.dat | 1 + .../phpqrcode/cache/mask_0/mask_157_0.dat | 2 + .../phpqrcode/cache/mask_0/mask_161_0.dat | Bin 0 -> 241 bytes .../phpqrcode/cache/mask_0/mask_165_0.dat | 2 + .../phpqrcode/cache/mask_0/mask_169_0.dat | 2 + .../phpqrcode/cache/mask_0/mask_173_0.dat | 1 + .../phpqrcode/cache/mask_0/mask_177_0.dat | 2 + .../phpqrcode/cache/mask_0/mask_21_0.dat | Bin 0 -> 48 bytes .../phpqrcode/cache/mask_0/mask_25_0.dat | Bin 0 -> 57 bytes .../phpqrcode/cache/mask_0/mask_29_0.dat | Bin 0 -> 59 bytes .../phpqrcode/cache/mask_0/mask_33_0.dat | Bin 0 -> 62 bytes .../phpqrcode/cache/mask_0/mask_37_0.dat | Bin 0 -> 65 bytes .../phpqrcode/cache/mask_0/mask_41_0.dat | Bin 0 -> 68 bytes .../phpqrcode/cache/mask_0/mask_45_0.dat | Bin 0 -> 106 bytes .../phpqrcode/cache/mask_0/mask_49_0.dat | 2 + .../phpqrcode/cache/mask_0/mask_53_0.dat | 2 + .../phpqrcode/cache/mask_0/mask_57_0.dat | 4 + .../phpqrcode/cache/mask_0/mask_61_0.dat | Bin 0 -> 119 bytes .../phpqrcode/cache/mask_0/mask_65_0.dat | Bin 0 -> 123 bytes .../phpqrcode/cache/mask_0/mask_69_0.dat | 1 + .../phpqrcode/cache/mask_0/mask_73_0.dat | 1 + .../phpqrcode/cache/mask_0/mask_77_0.dat | 2 + .../phpqrcode/cache/mask_0/mask_81_0.dat | 2 + .../phpqrcode/cache/mask_0/mask_85_0.dat | 2 + .../phpqrcode/cache/mask_0/mask_89_0.dat | 1 + .../phpqrcode/cache/mask_0/mask_93_0.dat | 3 + .../phpqrcode/cache/mask_0/mask_97_0.dat | Bin 0 -> 150 bytes .../phpqrcode/cache/mask_1/mask_101_1.dat | 2 + .../phpqrcode/cache/mask_1/mask_105_1.dat | 1 + .../phpqrcode/cache/mask_1/mask_109_1.dat | 1 + .../phpqrcode/cache/mask_1/mask_113_1.dat | 1 + .../phpqrcode/cache/mask_1/mask_117_1.dat | 2 + .../phpqrcode/cache/mask_1/mask_121_1.dat | 2 + .../phpqrcode/cache/mask_1/mask_125_1.dat | 2 + .../phpqrcode/cache/mask_1/mask_129_1.dat | Bin 0 -> 164 bytes .../phpqrcode/cache/mask_1/mask_133_1.dat | 1 + .../phpqrcode/cache/mask_1/mask_137_1.dat | 3 + .../phpqrcode/cache/mask_1/mask_141_1.dat | 2 + .../phpqrcode/cache/mask_1/mask_145_1.dat | 1 + .../phpqrcode/cache/mask_1/mask_149_1.dat | 1 + .../phpqrcode/cache/mask_1/mask_153_1.dat | 2 + .../phpqrcode/cache/mask_1/mask_157_1.dat | 2 + .../phpqrcode/cache/mask_1/mask_161_1.dat | 1 + .../phpqrcode/cache/mask_1/mask_165_1.dat | 1 + .../phpqrcode/cache/mask_1/mask_169_1.dat | 1 + .../phpqrcode/cache/mask_1/mask_173_1.dat | 1 + .../phpqrcode/cache/mask_1/mask_177_1.dat | 1 + .../phpqrcode/cache/mask_1/mask_21_1.dat | Bin 0 -> 42 bytes .../phpqrcode/cache/mask_1/mask_25_1.dat | Bin 0 -> 48 bytes .../phpqrcode/cache/mask_1/mask_29_1.dat | Bin 0 -> 50 bytes .../phpqrcode/cache/mask_1/mask_33_1.dat | Bin 0 -> 53 bytes .../phpqrcode/cache/mask_1/mask_37_1.dat | Bin 0 -> 56 bytes .../phpqrcode/cache/mask_1/mask_41_1.dat | Bin 0 -> 58 bytes .../phpqrcode/cache/mask_1/mask_45_1.dat | Bin 0 -> 82 bytes .../phpqrcode/cache/mask_1/mask_49_1.dat | Bin 0 -> 84 bytes .../phpqrcode/cache/mask_1/mask_53_1.dat | Bin 0 -> 87 bytes .../phpqrcode/cache/mask_1/mask_57_1.dat | Bin 0 -> 92 bytes .../phpqrcode/cache/mask_1/mask_61_1.dat | 1 + .../phpqrcode/cache/mask_1/mask_65_1.dat | Bin 0 -> 99 bytes .../phpqrcode/cache/mask_1/mask_69_1.dat | Bin 0 -> 102 bytes .../phpqrcode/cache/mask_1/mask_73_1.dat | Bin 0 -> 104 bytes .../phpqrcode/cache/mask_1/mask_77_1.dat | Bin 0 -> 110 bytes .../phpqrcode/cache/mask_1/mask_81_1.dat | Bin 0 -> 114 bytes .../phpqrcode/cache/mask_1/mask_85_1.dat | 2 + .../phpqrcode/cache/mask_1/mask_89_1.dat | 1 + .../phpqrcode/cache/mask_1/mask_93_1.dat | 2 + .../phpqrcode/cache/mask_1/mask_97_1.dat | 2 + .../phpqrcode/cache/mask_2/mask_101_2.dat | 3 + .../phpqrcode/cache/mask_2/mask_105_2.dat | 1 + .../phpqrcode/cache/mask_2/mask_109_2.dat | 2 + .../phpqrcode/cache/mask_2/mask_113_2.dat | 1 + .../phpqrcode/cache/mask_2/mask_117_2.dat | 2 + .../phpqrcode/cache/mask_2/mask_121_2.dat | Bin 0 -> 127 bytes .../phpqrcode/cache/mask_2/mask_125_2.dat | 1 + .../phpqrcode/cache/mask_2/mask_129_2.dat | 2 + .../phpqrcode/cache/mask_2/mask_133_2.dat | 10 + .../phpqrcode/cache/mask_2/mask_137_2.dat | 2 + .../phpqrcode/cache/mask_2/mask_141_2.dat | 2 + .../phpqrcode/cache/mask_2/mask_145_2.dat | 4 + .../phpqrcode/cache/mask_2/mask_149_2.dat | 1 + .../phpqrcode/cache/mask_2/mask_153_2.dat | 2 + .../phpqrcode/cache/mask_2/mask_157_2.dat | 3 + .../phpqrcode/cache/mask_2/mask_161_2.dat | Bin 0 -> 190 bytes .../phpqrcode/cache/mask_2/mask_165_2.dat | 2 + .../phpqrcode/cache/mask_2/mask_169_2.dat | Bin 0 -> 196 bytes .../phpqrcode/cache/mask_2/mask_173_2.dat | 1 + .../phpqrcode/cache/mask_2/mask_177_2.dat | 2 + .../phpqrcode/cache/mask_2/mask_21_2.dat | Bin 0 -> 35 bytes .../phpqrcode/cache/mask_2/mask_25_2.dat | Bin 0 -> 41 bytes .../phpqrcode/cache/mask_2/mask_29_2.dat | Bin 0 -> 45 bytes .../phpqrcode/cache/mask_2/mask_33_2.dat | Bin 0 -> 47 bytes .../phpqrcode/cache/mask_2/mask_37_2.dat | Bin 0 -> 47 bytes .../phpqrcode/cache/mask_2/mask_41_2.dat | 1 + .../phpqrcode/cache/mask_2/mask_45_2.dat | Bin 0 -> 68 bytes .../phpqrcode/cache/mask_2/mask_49_2.dat | Bin 0 -> 70 bytes .../phpqrcode/cache/mask_2/mask_53_2.dat | Bin 0 -> 73 bytes .../phpqrcode/cache/mask_2/mask_57_2.dat | Bin 0 -> 76 bytes .../phpqrcode/cache/mask_2/mask_61_2.dat | Bin 0 -> 78 bytes .../phpqrcode/cache/mask_2/mask_65_2.dat | Bin 0 -> 89 bytes .../phpqrcode/cache/mask_2/mask_69_2.dat | Bin 0 -> 88 bytes .../phpqrcode/cache/mask_2/mask_73_2.dat | Bin 0 -> 94 bytes .../phpqrcode/cache/mask_2/mask_77_2.dat | 1 + .../phpqrcode/cache/mask_2/mask_81_2.dat | 2 + .../phpqrcode/cache/mask_2/mask_85_2.dat | 2 + .../phpqrcode/cache/mask_2/mask_89_2.dat | 1 + .../phpqrcode/cache/mask_2/mask_93_2.dat | Bin 0 -> 103 bytes .../phpqrcode/cache/mask_2/mask_97_2.dat | 2 + .../phpqrcode/cache/mask_3/mask_101_3.dat | 1 + .../phpqrcode/cache/mask_3/mask_105_3.dat | 1 + .../phpqrcode/cache/mask_3/mask_109_3.dat | 1 + .../phpqrcode/cache/mask_3/mask_113_3.dat | 2 + .../phpqrcode/cache/mask_3/mask_117_3.dat | 4 + .../phpqrcode/cache/mask_3/mask_121_3.dat | Bin 0 -> 212 bytes .../phpqrcode/cache/mask_3/mask_125_3.dat | 2 + .../phpqrcode/cache/mask_3/mask_129_3.dat | 8 + .../phpqrcode/cache/mask_3/mask_133_3.dat | Bin 0 -> 216 bytes .../phpqrcode/cache/mask_3/mask_137_3.dat | 2 + .../phpqrcode/cache/mask_3/mask_141_3.dat | 2 + .../phpqrcode/cache/mask_3/mask_145_3.dat | 3 + .../phpqrcode/cache/mask_3/mask_149_3.dat | 1 + .../phpqrcode/cache/mask_3/mask_153_3.dat | 2 + .../phpqrcode/cache/mask_3/mask_157_3.dat | Bin 0 -> 248 bytes .../phpqrcode/cache/mask_3/mask_161_3.dat | 3 + .../phpqrcode/cache/mask_3/mask_165_3.dat | 2 + .../phpqrcode/cache/mask_3/mask_169_3.dat | 1 + .../phpqrcode/cache/mask_3/mask_173_3.dat | 1 + .../phpqrcode/cache/mask_3/mask_177_3.dat | Bin 0 -> 312 bytes .../phpqrcode/cache/mask_3/mask_21_3.dat | Bin 0 -> 60 bytes .../phpqrcode/cache/mask_3/mask_25_3.dat | Bin 0 -> 75 bytes .../phpqrcode/cache/mask_3/mask_29_3.dat | Bin 0 -> 75 bytes .../phpqrcode/cache/mask_3/mask_33_3.dat | Bin 0 -> 79 bytes .../phpqrcode/cache/mask_3/mask_37_3.dat | Bin 0 -> 83 bytes .../phpqrcode/cache/mask_3/mask_41_3.dat | Bin 0 -> 85 bytes .../phpqrcode/cache/mask_3/mask_45_3.dat | 2 + .../phpqrcode/cache/mask_3/mask_49_3.dat | Bin 0 -> 127 bytes .../phpqrcode/cache/mask_3/mask_53_3.dat | 2 + .../phpqrcode/cache/mask_3/mask_57_3.dat | Bin 0 -> 126 bytes .../phpqrcode/cache/mask_3/mask_61_3.dat | 2 + .../phpqrcode/cache/mask_3/mask_65_3.dat | 2 + .../phpqrcode/cache/mask_3/mask_69_3.dat | 2 + .../phpqrcode/cache/mask_3/mask_73_3.dat | 2 + .../phpqrcode/cache/mask_3/mask_77_3.dat | 2 + .../phpqrcode/cache/mask_3/mask_81_3.dat | 2 + .../phpqrcode/cache/mask_3/mask_85_3.dat | Bin 0 -> 160 bytes .../phpqrcode/cache/mask_3/mask_89_3.dat | 2 + .../phpqrcode/cache/mask_3/mask_93_3.dat | 2 + .../phpqrcode/cache/mask_3/mask_97_3.dat | Bin 0 -> 175 bytes .../phpqrcode/cache/mask_4/mask_101_4.dat | 2 + .../phpqrcode/cache/mask_4/mask_105_4.dat | 2 + .../phpqrcode/cache/mask_4/mask_109_4.dat | Bin 0 -> 182 bytes .../phpqrcode/cache/mask_4/mask_113_4.dat | 2 + .../phpqrcode/cache/mask_4/mask_117_4.dat | 2 + .../phpqrcode/cache/mask_4/mask_121_4.dat | Bin 0 -> 208 bytes .../phpqrcode/cache/mask_4/mask_125_4.dat | Bin 0 -> 213 bytes .../phpqrcode/cache/mask_4/mask_129_4.dat | Bin 0 -> 220 bytes .../phpqrcode/cache/mask_4/mask_133_4.dat | 3 + .../phpqrcode/cache/mask_4/mask_137_4.dat | Bin 0 -> 248 bytes .../phpqrcode/cache/mask_4/mask_141_4.dat | Bin 0 -> 254 bytes .../phpqrcode/cache/mask_4/mask_145_4.dat | Bin 0 -> 255 bytes .../phpqrcode/cache/mask_4/mask_149_4.dat | 2 + .../phpqrcode/cache/mask_4/mask_153_4.dat | 2 + .../phpqrcode/cache/mask_4/mask_157_4.dat | 1 + .../phpqrcode/cache/mask_4/mask_161_4.dat | 1 + .../phpqrcode/cache/mask_4/mask_165_4.dat | 3 + .../phpqrcode/cache/mask_4/mask_169_4.dat | Bin 0 -> 297 bytes .../phpqrcode/cache/mask_4/mask_173_4.dat | 2 + .../phpqrcode/cache/mask_4/mask_177_4.dat | 2 + .../phpqrcode/cache/mask_4/mask_21_4.dat | Bin 0 -> 57 bytes .../phpqrcode/cache/mask_4/mask_25_4.dat | Bin 0 -> 76 bytes .../phpqrcode/cache/mask_4/mask_29_4.dat | Bin 0 -> 78 bytes .../phpqrcode/cache/mask_4/mask_33_4.dat | Bin 0 -> 89 bytes .../phpqrcode/cache/mask_4/mask_37_4.dat | Bin 0 -> 86 bytes .../phpqrcode/cache/mask_4/mask_41_4.dat | Bin 0 -> 89 bytes .../phpqrcode/cache/mask_4/mask_45_4.dat | Bin 0 -> 120 bytes .../phpqrcode/cache/mask_4/mask_49_4.dat | Bin 0 -> 124 bytes .../phpqrcode/cache/mask_4/mask_53_4.dat | Bin 0 -> 128 bytes .../phpqrcode/cache/mask_4/mask_57_4.dat | Bin 0 -> 130 bytes .../phpqrcode/cache/mask_4/mask_61_4.dat | Bin 0 -> 132 bytes .../phpqrcode/cache/mask_4/mask_65_4.dat | 2 + .../phpqrcode/cache/mask_4/mask_69_4.dat | 1 + .../phpqrcode/cache/mask_4/mask_73_4.dat | 3 + .../phpqrcode/cache/mask_4/mask_77_4.dat | 2 + .../phpqrcode/cache/mask_4/mask_81_4.dat | 3 + .../phpqrcode/cache/mask_4/mask_85_4.dat | Bin 0 -> 154 bytes .../phpqrcode/cache/mask_4/mask_89_4.dat | 2 + .../phpqrcode/cache/mask_4/mask_93_4.dat | 2 + .../phpqrcode/cache/mask_4/mask_97_4.dat | Bin 0 -> 176 bytes .../phpqrcode/cache/mask_5/mask_101_5.dat | 2 + .../phpqrcode/cache/mask_5/mask_105_5.dat | Bin 0 -> 224 bytes .../phpqrcode/cache/mask_5/mask_109_5.dat | Bin 0 -> 211 bytes .../phpqrcode/cache/mask_5/mask_113_5.dat | 9 + .../phpqrcode/cache/mask_5/mask_117_5.dat | 1 + .../phpqrcode/cache/mask_5/mask_121_5.dat | Bin 0 -> 256 bytes .../phpqrcode/cache/mask_5/mask_125_5.dat | 2 + .../phpqrcode/cache/mask_5/mask_129_5.dat | Bin 0 -> 259 bytes .../phpqrcode/cache/mask_5/mask_133_5.dat | 2 + .../phpqrcode/cache/mask_5/mask_137_5.dat | 3 + .../phpqrcode/cache/mask_5/mask_141_5.dat | Bin 0 -> 297 bytes .../phpqrcode/cache/mask_5/mask_145_5.dat | Bin 0 -> 300 bytes .../phpqrcode/cache/mask_5/mask_149_5.dat | 3 + .../phpqrcode/cache/mask_5/mask_153_5.dat | 2 + .../phpqrcode/cache/mask_5/mask_157_5.dat | 1 + .../phpqrcode/cache/mask_5/mask_161_5.dat | 2 + .../phpqrcode/cache/mask_5/mask_165_5.dat | Bin 0 -> 332 bytes .../phpqrcode/cache/mask_5/mask_169_5.dat | 1 + .../phpqrcode/cache/mask_5/mask_173_5.dat | 4 + .../phpqrcode/cache/mask_5/mask_177_5.dat | 11 + .../phpqrcode/cache/mask_5/mask_21_5.dat | Bin 0 -> 74 bytes .../phpqrcode/cache/mask_5/mask_25_5.dat | 2 + .../phpqrcode/cache/mask_5/mask_29_5.dat | 2 + .../phpqrcode/cache/mask_5/mask_33_5.dat | Bin 0 -> 106 bytes .../phpqrcode/cache/mask_5/mask_37_5.dat | Bin 0 -> 103 bytes .../phpqrcode/cache/mask_5/mask_41_5.dat | 2 + .../phpqrcode/cache/mask_5/mask_45_5.dat | 1 + .../phpqrcode/cache/mask_5/mask_49_5.dat | Bin 0 -> 146 bytes .../phpqrcode/cache/mask_5/mask_53_5.dat | 1 + .../phpqrcode/cache/mask_5/mask_57_5.dat | 2 + .../phpqrcode/cache/mask_5/mask_61_5.dat | 1 + .../phpqrcode/cache/mask_5/mask_65_5.dat | Bin 0 -> 163 bytes .../phpqrcode/cache/mask_5/mask_69_5.dat | Bin 0 -> 167 bytes .../phpqrcode/cache/mask_5/mask_73_5.dat | Bin 0 -> 184 bytes .../phpqrcode/cache/mask_5/mask_77_5.dat | 1 + .../phpqrcode/cache/mask_5/mask_81_5.dat | 3 + .../phpqrcode/cache/mask_5/mask_85_5.dat | Bin 0 -> 186 bytes .../phpqrcode/cache/mask_5/mask_89_5.dat | 2 + .../phpqrcode/cache/mask_5/mask_93_5.dat | 2 + .../phpqrcode/cache/mask_5/mask_97_5.dat | 1 + .../phpqrcode/cache/mask_6/mask_101_6.dat | 2 + .../phpqrcode/cache/mask_6/mask_105_6.dat | 3 + .../phpqrcode/cache/mask_6/mask_109_6.dat | 1 + .../phpqrcode/cache/mask_6/mask_113_6.dat | 3 + .../phpqrcode/cache/mask_6/mask_117_6.dat | 1 + .../phpqrcode/cache/mask_6/mask_121_6.dat | Bin 0 -> 309 bytes .../phpqrcode/cache/mask_6/mask_125_6.dat | 1 + .../phpqrcode/cache/mask_6/mask_129_6.dat | Bin 0 -> 310 bytes .../phpqrcode/cache/mask_6/mask_133_6.dat | Bin 0 -> 296 bytes .../phpqrcode/cache/mask_6/mask_137_6.dat | 2 + .../phpqrcode/cache/mask_6/mask_141_6.dat | 10 + .../phpqrcode/cache/mask_6/mask_145_6.dat | Bin 0 -> 357 bytes .../phpqrcode/cache/mask_6/mask_149_6.dat | 2 + .../phpqrcode/cache/mask_6/mask_153_6.dat | Bin 0 -> 367 bytes .../phpqrcode/cache/mask_6/mask_157_6.dat | 1 + .../phpqrcode/cache/mask_6/mask_161_6.dat | Bin 0 -> 399 bytes .../phpqrcode/cache/mask_6/mask_165_6.dat | Bin 0 -> 400 bytes .../phpqrcode/cache/mask_6/mask_169_6.dat | 1 + .../phpqrcode/cache/mask_6/mask_173_6.dat | 1 + .../phpqrcode/cache/mask_6/mask_177_6.dat | 14 + .../phpqrcode/cache/mask_6/mask_21_6.dat | 1 + .../phpqrcode/cache/mask_6/mask_25_6.dat | 1 + .../phpqrcode/cache/mask_6/mask_29_6.dat | 3 + .../phpqrcode/cache/mask_6/mask_33_6.dat | Bin 0 -> 124 bytes .../phpqrcode/cache/mask_6/mask_37_6.dat | 1 + .../phpqrcode/cache/mask_6/mask_41_6.dat | Bin 0 -> 132 bytes .../phpqrcode/cache/mask_6/mask_45_6.dat | Bin 0 -> 189 bytes .../phpqrcode/cache/mask_6/mask_49_6.dat | 2 + .../phpqrcode/cache/mask_6/mask_53_6.dat | Bin 0 -> 195 bytes .../phpqrcode/cache/mask_6/mask_57_6.dat | 2 + .../phpqrcode/cache/mask_6/mask_61_6.dat | 2 + .../phpqrcode/cache/mask_6/mask_65_6.dat | 1 + .../phpqrcode/cache/mask_6/mask_69_6.dat | 1 + .../phpqrcode/cache/mask_6/mask_73_6.dat | Bin 0 -> 230 bytes .../phpqrcode/cache/mask_6/mask_77_6.dat | 1 + .../phpqrcode/cache/mask_6/mask_81_6.dat | 3 + .../phpqrcode/cache/mask_6/mask_85_6.dat | Bin 0 -> 229 bytes .../phpqrcode/cache/mask_6/mask_89_6.dat | Bin 0 -> 263 bytes .../phpqrcode/cache/mask_6/mask_93_6.dat | Bin 0 -> 276 bytes .../phpqrcode/cache/mask_6/mask_97_6.dat | 2 + .../phpqrcode/cache/mask_7/mask_101_7.dat | 1 + .../phpqrcode/cache/mask_7/mask_105_7.dat | 2 + .../phpqrcode/cache/mask_7/mask_109_7.dat | 2 + .../phpqrcode/cache/mask_7/mask_113_7.dat | 11 + .../phpqrcode/cache/mask_7/mask_117_7.dat | 2 + .../phpqrcode/cache/mask_7/mask_121_7.dat | 2 + .../phpqrcode/cache/mask_7/mask_125_7.dat | Bin 0 -> 288 bytes .../phpqrcode/cache/mask_7/mask_129_7.dat | Bin 0 -> 282 bytes .../phpqrcode/cache/mask_7/mask_133_7.dat | Bin 0 -> 281 bytes .../phpqrcode/cache/mask_7/mask_137_7.dat | 5 + .../phpqrcode/cache/mask_7/mask_141_7.dat | 1 + .../phpqrcode/cache/mask_7/mask_145_7.dat | 2 + .../phpqrcode/cache/mask_7/mask_149_7.dat | 1 + .../phpqrcode/cache/mask_7/mask_153_7.dat | 2 + .../phpqrcode/cache/mask_7/mask_157_7.dat | 2 + .../phpqrcode/cache/mask_7/mask_161_7.dat | 1 + .../phpqrcode/cache/mask_7/mask_165_7.dat | 1 + .../phpqrcode/cache/mask_7/mask_169_7.dat | Bin 0 -> 383 bytes .../phpqrcode/cache/mask_7/mask_173_7.dat | 1 + .../phpqrcode/cache/mask_7/mask_177_7.dat | Bin 0 -> 407 bytes .../phpqrcode/cache/mask_7/mask_21_7.dat | 4 + .../phpqrcode/cache/mask_7/mask_25_7.dat | 1 + .../phpqrcode/cache/mask_7/mask_29_7.dat | 2 + .../phpqrcode/cache/mask_7/mask_33_7.dat | 1 + .../phpqrcode/cache/mask_7/mask_37_7.dat | Bin 0 -> 122 bytes .../phpqrcode/cache/mask_7/mask_41_7.dat | 1 + .../phpqrcode/cache/mask_7/mask_45_7.dat | Bin 0 -> 173 bytes .../phpqrcode/cache/mask_7/mask_49_7.dat | 1 + .../phpqrcode/cache/mask_7/mask_53_7.dat | 1 + .../phpqrcode/cache/mask_7/mask_57_7.dat | 1 + .../phpqrcode/cache/mask_7/mask_61_7.dat | 2 + .../phpqrcode/cache/mask_7/mask_65_7.dat | 1 + .../phpqrcode/cache/mask_7/mask_69_7.dat | Bin 0 -> 202 bytes .../phpqrcode/cache/mask_7/mask_73_7.dat | Bin 0 -> 221 bytes .../phpqrcode/cache/mask_7/mask_77_7.dat | Bin 0 -> 226 bytes .../phpqrcode/cache/mask_7/mask_81_7.dat | 1 + .../phpqrcode/cache/mask_7/mask_85_7.dat | Bin 0 -> 213 bytes .../phpqrcode/cache/mask_7/mask_89_7.dat | Bin 0 -> 244 bytes .../phpqrcode/cache/mask_7/mask_93_7.dat | Bin 0 -> 248 bytes .../phpqrcode/cache/mask_7/mask_97_7.dat | 2 + vendor/aferrandini/phpqrcode/composer.json | 21 + .../aferrandini/phpqrcode/lib/PHPQRCode.php | 42 + .../phpqrcode/lib/PHPQRCode/Autoloader.php | 48 + .../phpqrcode/lib/PHPQRCode/Constants.php | 58 + .../phpqrcode/lib/PHPQRCode/FrameFiller.php | 96 ++ .../phpqrcode/lib/PHPQRCode/QRbitstream.php | 182 ++ .../phpqrcode/lib/PHPQRCode/QRcode.php | 158 ++ .../phpqrcode/lib/PHPQRCode/QRencode.php | 137 ++ .../phpqrcode/lib/PHPQRCode/QRimage.php | 95 + .../phpqrcode/lib/PHPQRCode/QRinput.php | 486 ++++++ .../phpqrcode/lib/PHPQRCode/QRinputItem.php | 246 +++ .../phpqrcode/lib/PHPQRCode/QRmask.php | 325 ++++ .../phpqrcode/lib/PHPQRCode/QRrawcode.php | 117 ++ .../phpqrcode/lib/PHPQRCode/QRrs.php | 56 + .../phpqrcode/lib/PHPQRCode/QRrsItem.php | 162 ++ .../phpqrcode/lib/PHPQRCode/QRrsblock.php | 25 + .../phpqrcode/lib/PHPQRCode/QRspec.php | 586 +++++++ .../phpqrcode/lib/PHPQRCode/QRsplit.php | 316 ++++ .../phpqrcode/lib/PHPQRCode/QRstr.php | 35 + .../phpqrcode/lib/PHPQRCode/QRtools.php | 171 ++ vendor/aferrandini/phpqrcode/readme.md | 37 + vendor/autoload.php | 7 + vendor/bin/picofeed | 1 + vendor/christian-riesen/base32/.gitignore | 3 + .../christian-riesen/base32/.scrutinizer.yml | 14 + vendor/christian-riesen/base32/.travis.yml | 19 + vendor/christian-riesen/base32/LICENSE | 19 + vendor/christian-riesen/base32/README.md | 60 + vendor/christian-riesen/base32/build.xml | 130 ++ vendor/christian-riesen/base32/composer.json | 33 + .../christian-riesen/base32/phpunit.xml.dist | 32 + vendor/christian-riesen/base32/src/Base32.php | 146 ++ .../base32/tests/Base32Test.php | 51 + .../base32/tests/bootstrap.php | 5 + vendor/christian-riesen/otp/.gitignore | 5 + vendor/christian-riesen/otp/.travis.yml | 11 + vendor/christian-riesen/otp/LICENSE | 20 + vendor/christian-riesen/otp/README.md | 105 ++ vendor/christian-riesen/otp/composer.json | 28 + vendor/christian-riesen/otp/example/index.php | 100 ++ vendor/christian-riesen/otp/phpunit.xml.dist | 15 + .../otp/src/Otp/GoogleAuthenticator.php | 189 ++ vendor/christian-riesen/otp/src/Otp/Otp.php | 310 ++++ .../otp/src/Otp/OtpInterface.php | 65 + .../otp/tests/Otp/GoogleAuthenticatorTest.php | 88 + .../otp/tests/Otp/OtpTest.php | 124 ++ vendor/composer/ClassLoader.php | 445 +++++ vendor/composer/LICENSE | 21 + vendor/composer/autoload_classmap.php | 901 ++++++++++ vendor/composer/autoload_files.php | 15 + vendor/composer/autoload_namespaces.php | 20 + vendor/composer/autoload_psr4.php | 17 + vendor/composer/autoload_real.php | 70 + vendor/composer/autoload_static.php | 1049 +++++++++++ vendor/composer/installed.json | 1051 ++++++++++++ vendor/eluceo/ical/.gitignore | 3 + vendor/eluceo/ical/.php_cs | 26 + vendor/eluceo/ical/.scrutinizer.yml | 20 + vendor/eluceo/ical/.travis.yml | 19 + vendor/eluceo/ical/CHANGELOG.md | 31 + vendor/eluceo/ical/LICENSE | 19 + vendor/eluceo/ical/README.md | 158 ++ vendor/eluceo/ical/UPGRADE.md | 9 + vendor/eluceo/ical/composer.json | 43 + vendor/eluceo/ical/examples/example1.php | 30 + vendor/eluceo/ical/examples/example2.php | 31 + vendor/eluceo/ical/examples/example3.php | 36 + vendor/eluceo/ical/examples/example4.php | 35 + vendor/eluceo/ical/examples/example5.php | 66 + vendor/eluceo/ical/examples/example6.php | 30 + vendor/eluceo/ical/examples/example7.php | 33 + vendor/eluceo/ical/phpunit.xml.dist | 21 + .../eluceo/ical/src/Eluceo/iCal/Component.php | 172 ++ .../ical/src/Eluceo/iCal/Component/Alarm.php | 151 ++ .../src/Eluceo/iCal/Component/Calendar.php | 323 ++++ .../ical/src/Eluceo/iCal/Component/Event.php | 783 +++++++++ .../src/Eluceo/iCal/Component/Timezone.php | 57 + .../Eluceo/iCal/Component/TimezoneRule.php | 215 +++ .../ical/src/Eluceo/iCal/ParameterBag.php | 108 ++ .../eluceo/ical/src/Eluceo/iCal/Property.php | 148 ++ .../src/Eluceo/iCal/Property/ArrayValue.php | 43 + .../Eluceo/iCal/Property/DateTimeProperty.php | 38 + .../iCal/Property/DateTimesProperty.php | 41 + .../Eluceo/iCal/Property/Event/Attendees.php | 102 ++ .../iCal/Property/Event/Description.php | 66 + .../Eluceo/iCal/Property/Event/Organizer.php | 39 + .../iCal/Property/Event/RecurrenceId.php | 130 ++ .../iCal/Property/Event/RecurrenceRule.php | 444 +++++ .../src/Eluceo/iCal/Property/StringValue.php | 61 + .../Eluceo/iCal/Property/ValueInterface.php | 24 + .../ical/src/Eluceo/iCal/PropertyBag.php | 79 + .../src/Eluceo/iCal/Util/ComponentUtil.php | 48 + .../ical/src/Eluceo/iCal/Util/DateUtil.php | 69 + .../Eluceo/iCal/Util/PropertyValueUtil.php | 40 + .../Component/CalendarIntegrationTest.php | 64 + .../ical/tests/Eluceo/iCal/ComponentTest.php | 45 + .../tests/Eluceo/iCal/ParameterBagTest.php | 35 + .../Eluceo/iCal/Property/ArrayValueTest.php | 26 + .../iCal/Property/Event/DescriptionTest.php | 17 + .../iCal/Property/Event/OrganizerTest.php | 63 + .../Property/Event/RecurrenceRuleTest.php | 21 + .../Eluceo/iCal/Property/StringValueTest.php | 63 + .../tests/Eluceo/iCal/PropertyBagTest.php | 18 + .../ical/tests/Eluceo/iCal/PropertyTest.php | 42 + vendor/erusev/parsedown/.travis.yml | 16 + vendor/erusev/parsedown/LICENSE.txt | 20 + vendor/erusev/parsedown/Parsedown.php | 1528 +++++++++++++++++ vendor/erusev/parsedown/README.md | 57 + vendor/erusev/parsedown/composer.json | 18 + vendor/erusev/parsedown/phpunit.xml.dist | 8 + .../erusev/parsedown/test/CommonMarkTest.php | 74 + .../erusev/parsedown/test/ParsedownTest.php | 159 ++ .../erusev/parsedown/test/TestParsedown.php | 5 + vendor/erusev/parsedown/test/bootstrap.php | 3 + .../parsedown/test/data/aesthetic_table.html | 18 + .../parsedown/test/data/aesthetic_table.md | 4 + .../parsedown/test/data/aligned_table.html | 21 + .../parsedown/test/data/aligned_table.md | 4 + .../parsedown/test/data/atx_heading.html | 9 + .../erusev/parsedown/test/data/atx_heading.md | 17 + .../parsedown/test/data/automatic_link.html | 1 + .../parsedown/test/data/automatic_link.md | 1 + .../parsedown/test/data/block-level_html.html | 12 + .../parsedown/test/data/block-level_html.md | 16 + .../parsedown/test/data/code_block.html | 8 + .../erusev/parsedown/test/data/code_block.md | 10 + .../erusev/parsedown/test/data/code_span.html | 6 + .../erusev/parsedown/test/data/code_span.md | 11 + .../test/data/compound_blockquote.html | 9 + .../test/data/compound_blockquote.md | 10 + .../test/data/compound_emphasis.html | 2 + .../parsedown/test/data/compound_emphasis.md | 4 + .../parsedown/test/data/compound_list.html | 12 + .../parsedown/test/data/compound_list.md | 7 + .../test/data/deeply_nested_list.html | 12 + .../parsedown/test/data/deeply_nested_list.md | 6 + .../erusev/parsedown/test/data/em_strong.html | 8 + .../erusev/parsedown/test/data/em_strong.md | 15 + vendor/erusev/parsedown/test/data/email.html | 1 + vendor/erusev/parsedown/test/data/email.md | 1 + .../erusev/parsedown/test/data/emphasis.html | 8 + vendor/erusev/parsedown/test/data/emphasis.md | 13 + .../erusev/parsedown/test/data/escaping.html | 6 + vendor/erusev/parsedown/test/data/escaping.md | 11 + .../test/data/fenced_code_block.html | 6 + .../parsedown/test/data/fenced_code_block.md | 14 + .../parsedown/test/data/horizontal_rule.html | 5 + .../parsedown/test/data/horizontal_rule.md | 9 + .../parsedown/test/data/html_comment.html | 5 + .../parsedown/test/data/html_comment.md | 8 + .../parsedown/test/data/html_entity.html | 1 + .../erusev/parsedown/test/data/html_entity.md | 1 + .../parsedown/test/data/image_reference.html | 2 + .../parsedown/test/data/image_reference.md | 5 + .../parsedown/test/data/image_title.html | 2 + .../erusev/parsedown/test/data/image_title.md | 3 + .../test/data/implicit_reference.html | 4 + .../parsedown/test/data/implicit_reference.md | 13 + .../parsedown/test/data/inline_link.html | 6 + .../erusev/parsedown/test/data/inline_link.md | 11 + .../test/data/inline_link_title.html | 6 + .../parsedown/test/data/inline_link_title.md | 11 + .../parsedown/test/data/inline_title.html | 1 + .../parsedown/test/data/inline_title.md | 1 + .../parsedown/test/data/lazy_blockquote.html | 6 + .../parsedown/test/data/lazy_blockquote.md | 5 + .../erusev/parsedown/test/data/lazy_list.html | 4 + .../erusev/parsedown/test/data/lazy_list.md | 2 + .../parsedown/test/data/line_break.html | 2 + .../erusev/parsedown/test/data/line_break.md | 2 + .../test/data/multiline_list_paragraph.html | 7 + .../test/data/multiline_list_paragraph.md | 4 + .../test/data/nested_block-level_html.html | 10 + .../test/data/nested_block-level_html.md | 11 + .../parsedown/test/data/ordered_list.html | 13 + .../parsedown/test/data/ordered_list.md | 11 + .../parsedown/test/data/paragraph_list.html | 12 + .../parsedown/test/data/paragraph_list.md | 9 + .../parsedown/test/data/reference_title.html | 2 + .../parsedown/test/data/reference_title.md | 6 + .../test/data/self-closing_html.html | 12 + .../parsedown/test/data/self-closing_html.md | 12 + .../test/data/separated_nested_list.html | 9 + .../test/data/separated_nested_list.md | 4 + .../parsedown/test/data/setext_header.html | 5 + .../parsedown/test/data/setext_header.md | 12 + .../test/data/simple_blockquote.html | 11 + .../parsedown/test/data/simple_blockquote.md | 7 + .../parsedown/test/data/simple_table.html | 37 + .../parsedown/test/data/simple_table.md | 11 + .../parsedown/test/data/span-level_html.html | 5 + .../parsedown/test/data/span-level_html.md | 8 + .../test/data/sparse_dense_list.html | 7 + .../parsedown/test/data/sparse_dense_list.md | 4 + .../parsedown/test/data/sparse_html.html | 8 + .../erusev/parsedown/test/data/sparse_html.md | 8 + .../parsedown/test/data/sparse_list.html | 15 + .../erusev/parsedown/test/data/sparse_list.md | 9 + .../test/data/special_characters.html | 6 + .../parsedown/test/data/special_characters.md | 13 + .../parsedown/test/data/strikethrough.html | 3 + .../parsedown/test/data/strikethrough.md | 5 + .../erusev/parsedown/test/data/strong_em.html | 6 + .../erusev/parsedown/test/data/strong_em.md | 11 + .../test/data/tab-indented_code_block.html | 6 + .../test/data/tab-indented_code_block.md | 6 + .../test/data/table_inline_markdown.html | 22 + .../test/data/table_inline_markdown.md | 5 + .../parsedown/test/data/text_reference.html | 8 + .../parsedown/test/data/text_reference.md | 21 + .../parsedown/test/data/unordered_list.html | 10 + .../parsedown/test/data/unordered_list.md | 8 + .../parsedown/test/data/untidy_table.html | 18 + .../parsedown/test/data/untidy_table.md | 4 + .../parsedown/test/data/url_autolinking.html | 3 + .../parsedown/test/data/url_autolinking.md | 5 + .../parsedown/test/data/whitespace.html | 1 + .../erusev/parsedown/test/data/whitespace.md | 5 + vendor/fguillot/json-rpc/LICENSE | 21 + .../fguillot/json-rpc/src/JsonRPC/Client.php | 194 +++ .../Exception/AccessDeniedException.php | 15 + .../AuthenticationFailureException.php | 15 + .../Exception/ConnectionFailureException.php | 15 + .../Exception/InvalidJsonFormatException.php | 15 + .../InvalidJsonRpcFormatException.php | 15 + .../ResponseEncodingFailureException.php | 15 + .../JsonRPC/Exception/ResponseException.php | 62 + .../Exception/ServerErrorException.php | 15 + .../json-rpc/src/JsonRPC/HttpClient.php | 365 ++++ .../src/JsonRPC/MiddlewareHandler.php | 114 ++ .../src/JsonRPC/MiddlewareInterface.php | 27 + .../json-rpc/src/JsonRPC/ProcedureHandler.php | 264 +++ .../JsonRPC/Request/BatchRequestParser.php | 55 + .../src/JsonRPC/Request/RequestBuilder.php | 129 ++ .../src/JsonRPC/Request/RequestParser.php | 200 +++ .../src/JsonRPC/Response/ResponseBuilder.php | 324 ++++ .../src/JsonRPC/Response/ResponseParser.php | 154 ++ .../fguillot/json-rpc/src/JsonRPC/Server.php | 386 +++++ .../src/JsonRPC/Validator/HostValidator.php | 30 + .../Validator/JsonEncodingValidator.php | 44 + .../JsonRPC/Validator/JsonFormatValidator.php | 30 + .../JsonRPC/Validator/RpcFormatValidator.php | 35 + .../src/JsonRPC/Validator/UserValidator.php | 21 + vendor/fguillot/picodb/LICENSE | 21 + .../picodb/lib/PicoDb/Builder/BaseBuilder.php | 86 + .../lib/PicoDb/Builder/ConditionBuilder.php | 377 ++++ .../lib/PicoDb/Builder/InsertBuilder.php | 36 + .../lib/PicoDb/Builder/OrConditionBuilder.php | 43 + .../lib/PicoDb/Builder/UpdateBuilder.php | 56 + .../fguillot/picodb/lib/PicoDb/Database.php | 370 ++++ .../picodb/lib/PicoDb/Driver/Base.php | 234 +++ .../picodb/lib/PicoDb/Driver/Mssql.php | 178 ++ .../picodb/lib/PicoDb/Driver/Mysql.php | 252 +++ .../picodb/lib/PicoDb/Driver/Postgres.php | 196 +++ .../picodb/lib/PicoDb/Driver/Sqlite.php | 193 +++ .../picodb/lib/PicoDb/DriverFactory.php | 45 + .../fguillot/picodb/lib/PicoDb/Hashtable.php | 112 ++ .../picodb/lib/PicoDb/LargeObject.php | 167 ++ .../picodb/lib/PicoDb/SQLException.php | 15 + vendor/fguillot/picodb/lib/PicoDb/Schema.php | 119 ++ .../picodb/lib/PicoDb/StatementHandler.php | 353 ++++ vendor/fguillot/picodb/lib/PicoDb/Table.php | 721 ++++++++ .../fguillot/picodb/lib/PicoDb/UrlParser.php | 93 + vendor/fguillot/simple-queue/LICENSE | 21 + .../src/Adapter/AmqpQueueAdapter.php | 138 ++ .../src/Adapter/AwsSqsQueueAdapter.php | 150 ++ .../src/Adapter/BeanstalkQueueAdapter.php | 120 ++ .../src/Adapter/DisqueQueueAdapter.php | 109 ++ .../src/Adapter/MemoryQueueAdapter.php | 100 ++ .../src/Exception/NotSupportedException.php | 14 + vendor/fguillot/simple-queue/src/Job.php | 98 ++ vendor/fguillot/simple-queue/src/Queue.php | 92 + .../src/QueueAdapterInterface.php | 58 + vendor/fguillot/simple-validator/LICENSE | 21 + .../src/SimpleValidator/Validator.php | 44 + .../src/SimpleValidator/Validators/Alpha.php | 15 + .../Validators/AlphaNumeric.php | 15 + .../src/SimpleValidator/Validators/Base.php | 37 + .../src/SimpleValidator/Validators/Date.php | 45 + .../src/SimpleValidator/Validators/Email.php | 67 + .../src/SimpleValidator/Validators/Equals.php | 27 + .../src/SimpleValidator/Validators/Exists.php | 38 + .../Validators/GreaterThan.php | 23 + .../SimpleValidator/Validators/InArray.php | 23 + .../SimpleValidator/Validators/Integer.php | 25 + .../src/SimpleValidator/Validators/Ip.php | 15 + .../src/SimpleValidator/Validators/Length.php | 26 + .../SimpleValidator/Validators/MaxLength.php | 24 + .../SimpleValidator/Validators/MinLength.php | 24 + .../SimpleValidator/Validators/NotEmpty.php | 15 + .../SimpleValidator/Validators/NotEquals.php | 28 + .../SimpleValidator/Validators/NotInArray.php | 15 + .../SimpleValidator/Validators/Numeric.php | 15 + .../src/SimpleValidator/Validators/Range.php | 33 + .../SimpleValidator/Validators/Required.php | 11 + .../src/SimpleValidator/Validators/Unique.php | 48 + vendor/fguillot/simpleLogger/LICENSE | 21 + .../simpleLogger/src/SimpleLogger/Base.php | 89 + .../simpleLogger/src/SimpleLogger/File.php | 48 + .../simpleLogger/src/SimpleLogger/Logger.php | 94 + .../simpleLogger/src/SimpleLogger/Stderr.php | 25 + .../simpleLogger/src/SimpleLogger/Stdout.php | 25 + .../simpleLogger/src/SimpleLogger/Syslog.php | 72 + vendor/gregwar/captcha/.gitignore | 3 + vendor/gregwar/captcha/.travis.yml | 12 + vendor/gregwar/captcha/CaptchaBuilder.php | 720 ++++++++ .../captcha/CaptchaBuilderInterface.php | 30 + vendor/gregwar/captcha/Font/captcha0.ttf | Bin 0 -> 49224 bytes vendor/gregwar/captcha/Font/captcha1.ttf | Bin 0 -> 76232 bytes vendor/gregwar/captcha/Font/captcha2.ttf | Bin 0 -> 33984 bytes vendor/gregwar/captcha/Font/captcha3.ttf | Bin 0 -> 15976 bytes vendor/gregwar/captcha/Font/captcha4.ttf | Bin 0 -> 906980 bytes vendor/gregwar/captcha/Font/captcha5.ttf | Bin 0 -> 49724 bytes vendor/gregwar/captcha/ImageFileHandler.php | 106 ++ vendor/gregwar/captcha/LICENSE | 19 + vendor/gregwar/captcha/PhraseBuilder.php | 34 + .../captcha/PhraseBuilderInterface.php | 21 + vendor/gregwar/captcha/README.md | 108 ++ vendor/gregwar/captcha/autoload.php | 16 + vendor/gregwar/captcha/composer.json | 28 + vendor/gregwar/captcha/demo/demo.php | 14 + vendor/gregwar/captcha/demo/fingerprint.php | 15 + vendor/gregwar/captcha/demo/index.php | 15 + vendor/gregwar/captcha/demo/ocr.php | 42 + vendor/gregwar/captcha/demo/output.php | 15 + vendor/miniflux/picofeed/LICENSE | 21 + .../miniflux/picofeed/lib/PicoFeed/Base.php | 38 + .../picofeed/lib/PicoFeed/Client/Client.php | 719 ++++++++ .../lib/PicoFeed/Client/ClientException.php | 14 + .../picofeed/lib/PicoFeed/Client/Curl.php | 402 +++++ .../PicoFeed/Client/ForbiddenException.php | 10 + .../lib/PicoFeed/Client/HttpHeaders.php | 79 + .../Client/InvalidCertificateException.php | 12 + .../PicoFeed/Client/InvalidUrlException.php | 12 + .../PicoFeed/Client/MaxRedirectException.php | 12 + .../lib/PicoFeed/Client/MaxSizeException.php | 12 + .../picofeed/lib/PicoFeed/Client/Stream.php | 205 +++ .../lib/PicoFeed/Client/TimeoutException.php | 12 + .../PicoFeed/Client/UnauthorizedException.php | 10 + .../picofeed/lib/PicoFeed/Client/Url.php | 290 ++++ .../lib/PicoFeed/Encoding/Encoding.php | 33 + .../lib/PicoFeed/Filter/Attribute.php | 700 ++++++++ .../picofeed/lib/PicoFeed/Filter/Filter.php | 155 ++ .../picofeed/lib/PicoFeed/Filter/Html.php | 243 +++ .../picofeed/lib/PicoFeed/Filter/Tag.php | 218 +++ .../Generator/ContentGeneratorInterface.php | 23 + .../Generator/FileContentGenerator.php | 36 + .../Generator/YoutubeContentGenerator.php | 67 + .../picofeed/lib/PicoFeed/Logging/Logger.php | 114 ++ .../picofeed/lib/PicoFeed/Parser/Atom.php | 382 +++++ .../lib/PicoFeed/Parser/DateParser.php | 128 ++ .../picofeed/lib/PicoFeed/Parser/Feed.php | 315 ++++ .../picofeed/lib/PicoFeed/Parser/Item.php | 534 ++++++ .../PicoFeed/Parser/MalformedXmlException.php | 13 + .../picofeed/lib/PicoFeed/Parser/Parser.php | 404 +++++ .../lib/PicoFeed/Parser/ParserException.php | 15 + .../lib/PicoFeed/Parser/ParserInterface.php | 182 ++ .../picofeed/lib/PicoFeed/Parser/Rss10.php | 306 ++++ .../picofeed/lib/PicoFeed/Parser/Rss20.php | 319 ++++ .../picofeed/lib/PicoFeed/Parser/Rss91.php | 13 + .../picofeed/lib/PicoFeed/Parser/Rss92.php | 13 + .../PicoFeed/Parser/XmlEntityException.php | 13 + .../lib/PicoFeed/Parser/XmlParser.php | 246 +++ .../lib/PicoFeed/PicoFeedException.php | 14 + .../Processor/ContentFilterProcessor.php | 37 + .../Processor/ContentGeneratorProcessor.php | 49 + .../PicoFeed/Processor/ItemPostProcessor.php | 106 ++ .../Processor/ItemProcessorInterface.php | 25 + .../PicoFeed/Processor/ScraperProcessor.php | 96 ++ .../picofeed/lib/PicoFeed/Reader/Favicon.php | 186 ++ .../picofeed/lib/PicoFeed/Reader/Reader.php | 190 ++ .../lib/PicoFeed/Reader/ReaderException.php | 14 + .../Reader/SubscriptionNotFoundException.php | 12 + .../Reader/UnsupportedFeedFormatException.php | 12 + .../lib/PicoFeed/Rules/.blog.lemonde.fr.php | 14 + .../lib/PicoFeed/Rules/.blogs.nytimes.com.php | 15 + .../picofeed/lib/PicoFeed/Rules/.igen.fr.php | 13 + .../lib/PicoFeed/Rules/.nytimes.com.php | 11 + .../lib/PicoFeed/Rules/.over-blog.com.php | 11 + .../lib/PicoFeed/Rules/.phoronix.com.php | 12 + .../lib/PicoFeed/Rules/.slate.com.php | 20 + .../lib/PicoFeed/Rules/.theguardian.com.php | 14 + .../lib/PicoFeed/Rules/.wikipedia.org.php | 29 + .../lib/PicoFeed/Rules/.wired.com.php | 44 + .../picofeed/lib/PicoFeed/Rules/.wsj.com.php | 15 + .../picofeed/lib/PicoFeed/Rules/01net.com.php | 18 + .../lib/PicoFeed/Rules/abstrusegoose.com.php | 8 + .../PicoFeed/Rules/adventuregamers.com.php | 23 + .../lib/PicoFeed/Rules/alainonline.net.php | 14 + .../lib/PicoFeed/Rules/aljazeera.com.php | 25 + .../lib/PicoFeed/Rules/allafrica.com.php | 20 + .../PicoFeed/Rules/allgemeine-zeitung.de.php | 23 + .../PicoFeed/Rules/amazingsuperpowers.com.php | 8 + .../lib/PicoFeed/Rules/anythingcomic.com.php | 13 + .../picofeed/lib/PicoFeed/Rules/ap.org.php | 13 + .../lib/PicoFeed/Rules/areadvd.de.php | 10 + .../lib/PicoFeed/Rules/arstechnica.com.php | 25 + .../lib/PicoFeed/Rules/awkwardzombie.com.php | 10 + .../lib/PicoFeed/Rules/backchannel.com.php | 18 + .../lib/PicoFeed/Rules/bangkokpost.com.php | 19 + .../picofeed/lib/PicoFeed/Rules/bgr.com.php | 15 + .../lib/PicoFeed/Rules/bigfootjustice.com.php | 8 + .../lib/PicoFeed/Rules/bigpicture.ru.php | 31 + .../lib/PicoFeed/Rules/bizjournals.com.php | 12 + .../lib/PicoFeed/Rules/biztimes.com.php | 22 + .../PicoFeed/Rules/bleepingcomputer.com.php | 15 + .../lib/PicoFeed/Rules/blog.fefe.de.php | 13 + .../lib/PicoFeed/Rules/blog.mapillary.com.php | 11 + .../lib/PicoFeed/Rules/brewers.mlb.com.php | 22 + .../PicoFeed/Rules/buenosairesherald.com.php | 17 + .../lib/PicoFeed/Rules/bunicomic.com.php | 13 + .../lib/PicoFeed/Rules/buttersafe.com.php | 13 + .../lib/PicoFeed/Rules/cad-comic.com.php | 12 + .../Rules/chaoslife.findchaos.com.php | 10 + .../lib/PicoFeed/Rules/chinafile.com.php | 18 + .../lib/PicoFeed/Rules/cliquerefresh.com.php | 10 + .../picofeed/lib/PicoFeed/Rules/cnet.com.php | 37 + .../lib/PicoFeed/Rules/consomac.fr.php | 13 + .../lib/PicoFeed/Rules/cowbirdsinlove.com.php | 8 + .../picofeed/lib/PicoFeed/Rules/crash.net.php | 28 + .../lib/PicoFeed/Rules/csmonitor.com.php | 19 + .../lib/PicoFeed/Rules/dailyjs.com.php | 19 + .../lib/PicoFeed/Rules/dailyreporter.com.php | 15 + .../lib/PicoFeed/Rules/dailytech.com.php | 13 + .../lib/PicoFeed/Rules/degroupnews.com.php | 14 + .../lib/PicoFeed/Rules/derstandard.at.php | 14 + .../lib/PicoFeed/Rules/dilbert.com.php | 11 + .../PicoFeed/Rules/discovermagazine.com.php | 26 + .../lib/PicoFeed/Rules/distrowatch.com.php | 13 + .../lib/PicoFeed/Rules/dozodomo.com.php | 15 + .../PicoFeed/Rules/drawingboardcomic.com.php | 15 + .../picofeed/lib/PicoFeed/Rules/e-w-e.ru.php | 22 + .../lib/PicoFeed/Rules/economist.com.php | 25 + .../Rules/encyclopedie.naheulbeuk.com.php | 13 + .../lib/PicoFeed/Rules/endlessorigami.com.php | 8 + .../lib/PicoFeed/Rules/engadget.com.php | 10 + .../PicoFeed/Rules/escapistmagazine.com.php | 45 + .../lib/PicoFeed/Rules/espn.go.com.php | 11 + .../lib/PicoFeed/Rules/exocomics.com.php | 15 + .../lib/PicoFeed/Rules/explosm.net.php | 13 + .../Rules/extrafabulouscomics.com.php | 8 + .../lib/PicoFeed/Rules/factroom.ru.php | 27 + .../lib/PicoFeed/Rules/fastcodesign.com.php | 13 + .../lib/PicoFeed/Rules/fastcoexist.com.php | 13 + .../lib/PicoFeed/Rules/fastcompany.com.php | 13 + .../lib/PicoFeed/Rules/ffworld.com.php | 13 + .../lib/PicoFeed/Rules/foreignpolicy.com.php | 21 + .../lib/PicoFeed/Rules/fossbytes.com.php | 18 + .../lib/PicoFeed/Rules/fototelegraf.ru.php | 19 + .../PicoFeed/Rules/fowllanguagecomics.com.php | 10 + .../picofeed/lib/PicoFeed/Rules/geek.com.php | 17 + .../lib/PicoFeed/Rules/geektimes.ru.php | 12 + .../PicoFeed/Rules/gerbilwithajetpack.com.php | 12 + .../lib/PicoFeed/Rules/giantitp.com.php | 12 + .../lib/PicoFeed/Rules/github.com.php | 14 + .../lib/PicoFeed/Rules/gocomics.com.php | 12 + .../picofeed/lib/PicoFeed/Rules/golem.de.php | 20 + .../lib/PicoFeed/Rules/gorabbit.ru.php | 19 + .../lib/PicoFeed/Rules/habrahabr.ru.php | 12 + .../lib/PicoFeed/Rules/happletea.com.php | 18 + .../lib/PicoFeed/Rules/hardware.fr.php | 11 + .../picofeed/lib/PicoFeed/Rules/heise.de.php | 12 + .../lib/PicoFeed/Rules/hotshowlife.com.php | 23 + .../lib/PicoFeed/Rules/huffingtonpost.com.php | 13 + .../lib/PicoFeed/Rules/imogenquest.net.php | 8 + .../lib/PicoFeed/Rules/indiehaven.com.php | 11 + .../picofeed/lib/PicoFeed/Rules/ing.dk.php | 12 + .../lib/PicoFeed/Rules/invisiblebread.com.php | 8 + .../lib/PicoFeed/Rules/ir.amd.com.php | 10 + .../lib/PicoFeed/Rules/japantimes.co.jp.php | 21 + .../lib/PicoFeed/Rules/japantoday.com.php | 15 + .../lib/PicoFeed/Rules/journaldugeek.com.php | 11 + .../lib/PicoFeed/Rules/jsonline.com.php | 37 + .../lib/PicoFeed/Rules/justcoolidea.ru.php | 19 + .../picofeed/lib/PicoFeed/Rules/kanpai.fr.php | 13 + .../PicoFeed/Rules/karriere.jobfinder.dk.php | 12 + .../picofeed/lib/PicoFeed/Rules/kodi.tv.php | 11 + .../lib/PicoFeed/Rules/koreaherald.com.php | 11 + .../lib/PicoFeed/Rules/koreatimes.php | 14 + .../PicoFeed/Rules/lastplacecomics.com.php | 8 + .../lib/PicoFeed/Rules/legorafi.fr.php | 22 + .../lib/PicoFeed/Rules/lejapon.fr.php | 17 + .../lib/PicoFeed/Rules/lesjoiesducode.fr.php | 13 + .../picofeed/lib/PicoFeed/Rules/lfg.co.php | 12 + .../lib/PicoFeed/Rules/lifehacker.com.php | 18 + .../lib/PicoFeed/Rules/lifehacker.ru.php | 22 + .../picofeed/lib/PicoFeed/Rules/linux.org.php | 14 + .../lib/PicoFeed/Rules/linux.org.ru.php | 13 + .../lib/PicoFeed/Rules/linuxinsider.com.php | 20 + .../picofeed/lib/PicoFeed/Rules/lists.php | 13 + .../lib/PicoFeed/Rules/loadingartist.com.php | 8 + .../lib/PicoFeed/Rules/loldwell.com.php | 10 + .../lib/PicoFeed/Rules/lukesurl.com.php | 15 + .../picofeed/lib/PicoFeed/Rules/macg.co.php | 13 + .../picofeed/lib/PicoFeed/Rules/marc.info.php | 13 + .../PicoFeed/Rules/marriedtothesea.com.php | 12 + .../lib/PicoFeed/Rules/marycagle.com.php | 13 + .../Rules/maximumble.thebookofbiff.com.php | 10 + .../lib/PicoFeed/Rules/medium.com.php | 19 + .../lib/PicoFeed/Rules/mercworks.net.php | 17 + .../lib/PicoFeed/Rules/metronieuws.nl.php | 10 + .../lib/PicoFeed/Rules/milwaukeenns.php | 14 + .../Rules/mokepon.smackjeeves.com.php | 10 + .../lib/PicoFeed/Rules/monandroid.com.php | 13 + .../lib/PicoFeed/Rules/monwindows.com.php | 13 + .../lib/PicoFeed/Rules/moya-planeta.ru.php | 21 + .../lib/PicoFeed/Rules/mrlovenstein.com.php | 9 + .../lib/PicoFeed/Rules/muckrock.com.php | 20 + .../PicoFeed/Rules/mynorthshorenow.com.php | 27 + .../lib/PicoFeed/Rules/nakedCapitalism.php | 11 + .../picofeed/lib/PicoFeed/Rules/nasa.gov.php | 14 + .../lib/PicoFeed/Rules/nat-geo.ru.php | 11 + .../PicoFeed/Rules/nationaljournal.com.php | 15 + .../lib/PicoFeed/Rules/nature.com.php | 13 + .../picofeed/lib/PicoFeed/Rules/nba.com.php | 15 + .../lib/PicoFeed/Rules/nedroid.com.php | 8 + .../lib/PicoFeed/Rules/networkworld.com.php | 20 + .../lib/PicoFeed/Rules/neustadt-ticker.de.php | 15 + .../lib/PicoFeed/Rules/nextinpact.com.php | 18 + .../lib/PicoFeed/Rules/niceteethcomic.com.php | 10 + .../lib/PicoFeed/Rules/nichtlustig.de.php | 8 + .../picofeed/lib/PicoFeed/Rules/oglaf.com.php | 19 + .../picofeed/lib/PicoFeed/Rules/onhax.net.php | 15 + .../lib/PicoFeed/Rules/onmilwaukee.php | 24 + .../lib/PicoFeed/Rules/openculture.com.php | 11 + .../lib/PicoFeed/Rules/opennet.ru.php | 13 + .../PicoFeed/Rules/openrightsgroup.org.php | 20 + .../lib/PicoFeed/Rules/opensource.com.php | 22 + .../lib/PicoFeed/Rules/optipess.com.php | 8 + .../lib/PicoFeed/Rules/osnews.com.php | 11 + .../lib/PicoFeed/Rules/pastebin.com.php | 13 + .../lib/PicoFeed/Rules/peebleslab.com.php | 9 + .../lib/PicoFeed/Rules/penny-arcade.com.php | 21 + .../lib/PicoFeed/Rules/pixelbeat.org.php | 12 + .../lib/PicoFeed/Rules/plus.google.com.php | 11 + .../lib/PicoFeed/Rules/popstrip.com.php | 8 + .../PicoFeed/Rules/publicpolicyforum.org.php | 15 + .../picofeed/lib/PicoFeed/Rules/publy.ru.php | 24 + .../lib/PicoFeed/Rules/putaindecode.fr.php | 16 + .../lib/PicoFeed/Rules/recode.net.php | 20 + .../PicoFeed/Rules/retractionwatch.com.php | 18 + .../PicoFeed/Rules/rockpapershotgun.com.php | 11 + .../PicoFeed/Rules/rue89.nouvelobs.com.php | 13 + .../lib/PicoFeed/Rules/rugbyrama.fr.php | 20 + .../lib/PicoFeed/Rules/satwcomic.com.php | 13 + .../lib/PicoFeed/Rules/scrumalliance.org.php | 12 + .../lib/PicoFeed/Rules/securityfocus.com.php | 17 + .../PicoFeed/Rules/sentfromthemoon.com.php | 18 + .../lib/PicoFeed/Rules/sitepoint.com.php | 13 + .../lib/PicoFeed/Rules/slashdot.org.php | 11 + .../PicoFeed/Rules/smallhousebliss.com.php | 19 + .../lib/PicoFeed/Rules/smarthomewelt.de.php | 10 + .../PicoFeed/Rules/smashingmagazine.com.php | 10 + .../lib/PicoFeed/Rules/smbc-comics.com.php | 14 + .../lib/PicoFeed/Rules/snopes.com.php | 22 + .../lib/PicoFeed/Rules/soundandvision.com.php | 21 + .../lib/PicoFeed/Rules/spiegel.de.php | 11 + .../lib/PicoFeed/Rules/stereophile.com.php | 11 + .../lib/PicoFeed/Rules/stupidfox.net.php | 13 + .../lib/PicoFeed/Rules/subtraction.com.php | 15 + .../picofeed/lib/PicoFeed/Rules/sz.de.php | 10 + .../lib/PicoFeed/Rules/takprosto.cc.php | 21 + .../lib/PicoFeed/Rules/techcrunch.com.php | 15 + .../PicoFeed/Rules/the-ebook-reader.com.php | 15 + .../lib/PicoFeed/Rules/theatlantic.com.php | 23 + .../lib/PicoFeed/Rules/theawkwardyeti.com.php | 12 + .../lib/PicoFeed/Rules/thecodinglove.com.php | 10 + .../PicoFeed/Rules/thedoghousediaries.com.php | 18 + .../lib/PicoFeed/Rules/thegamercat.com.php | 10 + .../lib/PicoFeed/Rules/thehindu.com.php | 19 + .../lib/PicoFeed/Rules/thelocal.se.php | 17 + .../lib/PicoFeed/Rules/themerepublic.net.php | 10 + .../lib/PicoFeed/Rules/themoscowtimes.com.php | 18 + .../lib/PicoFeed/Rules/thenewslens.com.php | 21 + .../lib/PicoFeed/Rules/theodd1sout.com.php | 8 + .../lib/PicoFeed/Rules/theonion.com.php | 12 + .../lib/PicoFeed/Rules/thestandard.com.hk.php | 22 + .../lib/PicoFeed/Rules/threepanelsoul.com.php | 11 + .../Rules/timesofindia.indiatimes.com.php | 14 + .../lib/PicoFeed/Rules/travel-dealz.de.php | 15 + .../lib/PicoFeed/Rules/treehugger.com.php | 14 + .../lib/PicoFeed/Rules/treelobsters.com.php | 8 + .../lib/PicoFeed/Rules/twogag.com.php | 8 + .../PicoFeed/Rules/twokinds.keenspot.com.php | 10 + .../lib/PicoFeed/Rules/undeadly.org.php | 14 + .../picofeed/lib/PicoFeed/Rules/upi.com.php | 15 + .../lib/PicoFeed/Rules/usatoday.com.php | 27 + .../lib/PicoFeed/Rules/version2.dk.php | 12 + .../lib/PicoFeed/Rules/vgcats.com.php | 15 + .../picofeed/lib/PicoFeed/Rules/vuxml.org.php | 17 + .../PicoFeed/Rules/wausaudailyherald.com.php | 27 + .../lib/PicoFeed/Rules/www.bbc.co.uk.php | 33 + .../lib/PicoFeed/Rules/www.bdgest.com.php | 15 + .../lib/PicoFeed/Rules/www.bgr.in.php | 23 + .../PicoFeed/Rules/www.businessweek.com.php | 15 + .../lib/PicoFeed/Rules/www.cnn.com.php | 24 + .../lib/PicoFeed/Rules/www.developpez.com.php | 21 + .../lib/PicoFeed/Rules/www.egscomics.com.php | 12 + .../Rules/www.fakingnews.firstpost.com.php | 17 + .../lib/PicoFeed/Rules/www.forbes.com.php | 20 + .../PicoFeed/Rules/www.franceculture.fr.php | 14 + .../Rules/www.futura-sciences.com.php | 19 + .../PicoFeed/Rules/www.geekculture.com.php | 13 + .../lib/PicoFeed/Rules/www.howtogeek.com.php | 14 + .../lib/PicoFeed/Rules/www.lepoint.fr.php | 18 + .../PicoFeed/Rules/www.lesnumeriques.com.php | 25 + .../lib/PicoFeed/Rules/www.mac4ever.com.php | 13 + .../lib/PicoFeed/Rules/www.makeuseof.com.php | 18 + .../Rules/www.monsieur-le-chien.fr.php | 11 + .../lib/PicoFeed/Rules/www.npr.org.php | 28 + .../lib/PicoFeed/Rules/www.numerama.com.php | 15 + .../lib/PicoFeed/Rules/www.oneindia.com.php | 14 + .../Rules/www.pseudo-sciences.org.php | 16 + .../lib/PicoFeed/Rules/www.sciencemag.org.php | 16 + .../lib/PicoFeed/Rules/www.slate.fr.php | 19 + .../PicoFeed/Rules/www.universfreebox.com.php | 15 + .../lib/PicoFeed/Rules/www.zeit.de.php | 41 + .../picofeed/lib/PicoFeed/Rules/xkcd.com.php | 8 + .../lib/PicoFeed/Rules/ymatuhin.ru.php | 21 + .../picofeed/lib/PicoFeed/Rules/zdnet.com.php | 23 + .../lib/PicoFeed/Scraper/CandidateParser.php | 283 +++ .../lib/PicoFeed/Scraper/ParserInterface.php | 20 + .../lib/PicoFeed/Scraper/RuleLoader.php | 107 ++ .../lib/PicoFeed/Scraper/RuleParser.php | 102 ++ .../picofeed/lib/PicoFeed/Scraper/Scraper.php | 279 +++ .../PicoFeed/Serialization/Subscription.php | 175 ++ .../Serialization/SubscriptionList.php | 75 + .../Serialization/SubscriptionListBuilder.php | 204 +++ .../Serialization/SubscriptionListParser.php | 100 ++ .../Serialization/SubscriptionParser.php | 142 ++ .../PicoFeed/Syndication/AtomFeedBuilder.php | 65 + .../lib/PicoFeed/Syndication/AtomHelper.php | 139 ++ .../PicoFeed/Syndication/AtomItemBuilder.php | 63 + .../lib/PicoFeed/Syndication/FeedBuilder.php | 185 ++ .../lib/PicoFeed/Syndication/ItemBuilder.php | 209 +++ .../PicoFeed/Syndication/Rss20FeedBuilder.php | 76 + .../lib/PicoFeed/Syndication/Rss20Helper.php | 115 ++ .../PicoFeed/Syndication/Rss20ItemBuilder.php | 67 + vendor/miniflux/picofeed/picofeed | 135 ++ vendor/paragonie/random_compat/CHANGELOG.md | 260 +++ vendor/paragonie/random_compat/ERRATA.md | 34 + vendor/paragonie/random_compat/LICENSE | 22 + vendor/paragonie/random_compat/README.md | 176 ++ vendor/paragonie/random_compat/SECURITY.md | 108 ++ vendor/paragonie/random_compat/build-phar.sh | 5 + vendor/paragonie/random_compat/composer.json | 35 + .../dist/random_compat.phar.pubkey | 5 + .../dist/random_compat.phar.pubkey.asc | 11 + .../random_compat/lib/byte_safe_strings.php | 181 ++ .../random_compat/lib/cast_to_int.php | 71 + .../random_compat/lib/error_polyfill.php | 42 + vendor/paragonie/random_compat/lib/random.php | 197 +++ .../lib/random_bytes_com_dotnet.php | 81 + .../lib/random_bytes_dev_urandom.php | 148 ++ .../lib/random_bytes_libsodium.php | 86 + .../lib/random_bytes_libsodium_legacy.php | 86 + .../random_compat/lib/random_bytes_mcrypt.php | 76 + .../random_compat/lib/random_int.php | 191 +++ .../random_compat/other/build_phar.php | 57 + vendor/pimple/pimple/.gitignore | 3 + vendor/pimple/pimple/.travis.yml | 32 + vendor/pimple/pimple/CHANGELOG | 35 + vendor/pimple/pimple/LICENSE | 19 + vendor/pimple/pimple/README.rst | 201 +++ vendor/pimple/pimple/composer.json | 25 + vendor/pimple/pimple/ext/pimple/.gitignore | 30 + vendor/pimple/pimple/ext/pimple/README.md | 12 + vendor/pimple/pimple/ext/pimple/config.m4 | 63 + vendor/pimple/pimple/ext/pimple/config.w32 | 13 + vendor/pimple/pimple/ext/pimple/php_pimple.h | 121 ++ vendor/pimple/pimple/ext/pimple/pimple.c | 922 ++++++++++ .../pimple/pimple/ext/pimple/pimple_compat.h | 81 + .../pimple/pimple/ext/pimple/tests/001.phpt | 45 + .../pimple/pimple/ext/pimple/tests/002.phpt | 15 + .../pimple/pimple/ext/pimple/tests/003.phpt | 16 + .../pimple/pimple/ext/pimple/tests/004.phpt | 30 + .../pimple/pimple/ext/pimple/tests/005.phpt | 27 + .../pimple/pimple/ext/pimple/tests/006.phpt | 51 + .../pimple/pimple/ext/pimple/tests/007.phpt | 22 + .../pimple/pimple/ext/pimple/tests/008.phpt | 29 + .../pimple/pimple/ext/pimple/tests/009.phpt | 13 + .../pimple/pimple/ext/pimple/tests/010.phpt | 45 + .../pimple/pimple/ext/pimple/tests/011.phpt | 19 + .../pimple/pimple/ext/pimple/tests/012.phpt | 28 + .../pimple/pimple/ext/pimple/tests/013.phpt | 33 + .../pimple/pimple/ext/pimple/tests/014.phpt | 30 + .../pimple/pimple/ext/pimple/tests/015.phpt | 17 + .../pimple/pimple/ext/pimple/tests/016.phpt | 24 + .../pimple/pimple/ext/pimple/tests/017.phpt | 17 + .../pimple/pimple/ext/pimple/tests/017_1.phpt | 17 + .../pimple/pimple/ext/pimple/tests/018.phpt | 23 + .../pimple/pimple/ext/pimple/tests/019.phpt | 18 + .../pimple/pimple/ext/pimple/tests/bench.phpb | 51 + .../pimple/ext/pimple/tests/bench_shared.phpb | 25 + vendor/pimple/pimple/phpunit.xml.dist | 14 + vendor/pimple/pimple/src/Pimple/Container.php | 282 +++ .../src/Pimple/ServiceProviderInterface.php | 46 + .../src/Pimple/Tests/Fixtures/Invokable.php | 38 + .../Pimple/Tests/Fixtures/NonInvokable.php | 34 + .../Tests/Fixtures/PimpleServiceProvider.php | 54 + .../src/Pimple/Tests/Fixtures/Service.php | 35 + .../PimpleServiceProviderInterfaceTest.php | 76 + .../pimple/src/Pimple/Tests/PimpleTest.php | 440 +++++ vendor/psr/log/.gitignore | 1 + vendor/psr/log/LICENSE | 19 + vendor/psr/log/Psr/Log/AbstractLogger.php | 128 ++ .../log/Psr/Log/InvalidArgumentException.php | 7 + vendor/psr/log/Psr/Log/LogLevel.php | 18 + .../psr/log/Psr/Log/LoggerAwareInterface.php | 18 + vendor/psr/log/Psr/Log/LoggerAwareTrait.php | 26 + vendor/psr/log/Psr/Log/LoggerInterface.php | 123 ++ vendor/psr/log/Psr/Log/LoggerTrait.php | 140 ++ vendor/psr/log/Psr/Log/NullLogger.php | 28 + .../log/Psr/Log/Test/LoggerInterfaceTest.php | 140 ++ vendor/psr/log/README.md | 45 + vendor/psr/log/composer.json | 26 + vendor/ramsey/array_column/.gitignore | 6 + vendor/ramsey/array_column/.travis.yml | 26 + vendor/ramsey/array_column/CHANGELOG.md | 37 + vendor/ramsey/array_column/LICENSE | 19 + vendor/ramsey/array_column/README.md | 91 + vendor/ramsey/array_column/composer.json | 27 + vendor/ramsey/array_column/phpunit.xml.dist | 21 + .../ramsey/array_column/src/array_column.php | 115 ++ .../array_column/tests/ArrayColumnTest.php | 422 +++++ .../ramsey/array_column/tests/bootstrap.php | 5 + vendor/swiftmailer/swiftmailer/.gitattributes | 9 + vendor/swiftmailer/swiftmailer/.gitignore | 4 + vendor/swiftmailer/swiftmailer/.travis.yml | 27 + vendor/swiftmailer/swiftmailer/CHANGES | 241 +++ vendor/swiftmailer/swiftmailer/LICENSE | 19 + vendor/swiftmailer/swiftmailer/README | 15 + vendor/swiftmailer/swiftmailer/VERSION | 1 + vendor/swiftmailer/swiftmailer/composer.json | 37 + .../swiftmailer/swiftmailer/doc/headers.rst | 742 ++++++++ .../swiftmailer/doc/help-resources.rst | 44 + .../swiftmailer/doc/including-the-files.rst | 46 + vendor/swiftmailer/swiftmailer/doc/index.rst | 16 + .../swiftmailer/doc/installing.rst | 89 + .../swiftmailer/doc/introduction.rst | 135 ++ .../swiftmailer/swiftmailer/doc/japanese.rst | 22 + .../swiftmailer/swiftmailer/doc/messages.rst | 1061 ++++++++++++ .../swiftmailer/swiftmailer/doc/overview.rst | 159 ++ .../swiftmailer/swiftmailer/doc/plugins.rst | 385 +++++ .../swiftmailer/swiftmailer/doc/sending.rst | 571 ++++++ .../swiftmailer/doc/uml/Encoders.graffle | Bin 0 -> 3503 bytes .../swiftmailer/doc/uml/Mime.graffle | Bin 0 -> 5575 bytes .../swiftmailer/doc/uml/Transports.graffle | Bin 0 -> 3061 bytes .../swiftmailer/lib/classes/Swift.php | 80 + .../lib/classes/Swift/Attachment.php | 71 + .../AbstractFilterableInputStream.php | 181 ++ .../Swift/ByteStream/ArrayByteStream.php | 182 ++ .../Swift/ByteStream/FileByteStream.php | 231 +++ .../ByteStream/TemporaryFileByteStream.php | 42 + .../lib/classes/Swift/CharacterReader.php | 67 + .../GenericFixedWidthReader.php | 97 ++ .../Swift/CharacterReader/UsAsciiReader.php | 84 + .../Swift/CharacterReader/Utf8Reader.php | 176 ++ .../classes/Swift/CharacterReaderFactory.php | 26 + .../SimpleCharacterReaderFactory.php | 124 ++ .../lib/classes/Swift/CharacterStream.php | 89 + .../CharacterStream/ArrayCharacterStream.php | 293 ++++ .../CharacterStream/NgCharacterStream.php | 267 +++ .../lib/classes/Swift/ConfigurableSpool.php | 63 + .../lib/classes/Swift/DependencyContainer.php | 373 ++++ .../lib/classes/Swift/DependencyException.php | 27 + .../lib/classes/Swift/EmbeddedFile.php | 69 + .../swiftmailer/lib/classes/Swift/Encoder.php | 28 + .../classes/Swift/Encoder/Base64Encoder.php | 58 + .../lib/classes/Swift/Encoder/QpEncoder.php | 300 ++++ .../classes/Swift/Encoder/Rfc2231Encoder.php | 92 + .../lib/classes/Swift/Encoding.php | 64 + .../lib/classes/Swift/Events/CommandEvent.php | 65 + .../classes/Swift/Events/CommandListener.php | 24 + .../lib/classes/Swift/Events/Event.php | 38 + .../classes/Swift/Events/EventDispatcher.php | 83 + .../classes/Swift/Events/EventListener.php | 18 + .../lib/classes/Swift/Events/EventObject.php | 63 + .../classes/Swift/Events/ResponseEvent.php | 65 + .../classes/Swift/Events/ResponseListener.php | 24 + .../lib/classes/Swift/Events/SendEvent.php | 129 ++ .../lib/classes/Swift/Events/SendListener.php | 31 + .../Swift/Events/SimpleEventDispatcher.php | 156 ++ .../Swift/Events/TransportChangeEvent.php | 27 + .../Swift/Events/TransportChangeListener.php | 45 + .../Swift/Events/TransportExceptionEvent.php | 46 + .../Events/TransportExceptionListener.php | 24 + .../lib/classes/Swift/FailoverTransport.php | 45 + .../lib/classes/Swift/FileSpool.php | 208 +++ .../lib/classes/Swift/FileStream.php | 24 + .../lib/classes/Swift/Filterable.php | 32 + .../swiftmailer/lib/classes/Swift/Image.php | 61 + .../lib/classes/Swift/InputByteStream.php | 75 + .../lib/classes/Swift/IoException.php | 29 + .../lib/classes/Swift/KeyCache.php | 105 ++ .../classes/Swift/KeyCache/ArrayKeyCache.php | 206 +++ .../classes/Swift/KeyCache/DiskKeyCache.php | 321 ++++ .../Swift/KeyCache/KeyCacheInputStream.php | 51 + .../classes/Swift/KeyCache/NullKeyCache.php | 115 ++ .../KeyCache/SimpleKeyCacheInputStream.php | 127 ++ .../classes/Swift/LoadBalancedTransport.php | 45 + .../lib/classes/Swift/MailTransport.php | 47 + .../swiftmailer/lib/classes/Swift/Mailer.php | 114 ++ .../Swift/Mailer/ArrayRecipientIterator.php | 55 + .../Swift/Mailer/RecipientIterator.php | 32 + .../lib/classes/Swift/MemorySpool.php | 110 ++ .../swiftmailer/lib/classes/Swift/Message.php | 291 ++++ .../lib/classes/Swift/Mime/Attachment.php | 149 ++ .../classes/Swift/Mime/CharsetObserver.php | 24 + .../lib/classes/Swift/Mime/ContentEncoder.php | 34 + .../ContentEncoder/Base64ContentEncoder.php | 104 ++ .../ContentEncoder/NativeQpContentEncoder.php | 123 ++ .../ContentEncoder/PlainContentEncoder.php | 162 ++ .../Mime/ContentEncoder/QpContentEncoder.php | 134 ++ .../ContentEncoder/QpContentEncoderProxy.php | 98 ++ .../Mime/ContentEncoder/RawContentEncoder.php | 64 + .../lib/classes/Swift/Mime/EmbeddedFile.php | 45 + .../classes/Swift/Mime/EncodingObserver.php | 24 + .../lib/classes/Swift/Mime/Grammar.php | 176 ++ .../lib/classes/Swift/Mime/Header.php | 93 + .../lib/classes/Swift/Mime/HeaderEncoder.php | 24 + .../HeaderEncoder/Base64HeaderEncoder.php | 55 + .../Mime/HeaderEncoder/QpHeaderEncoder.php | 65 + .../lib/classes/Swift/Mime/HeaderFactory.php | 78 + .../lib/classes/Swift/Mime/HeaderSet.php | 169 ++ .../Swift/Mime/Headers/AbstractHeader.php | 503 ++++++ .../classes/Swift/Mime/Headers/DateHeader.php | 125 ++ .../Mime/Headers/IdentificationHeader.php | 180 ++ .../Swift/Mime/Headers/MailboxHeader.php | 353 ++++ .../Swift/Mime/Headers/OpenDKIMHeader.php | 133 ++ .../Mime/Headers/ParameterizedHeader.php | 258 +++ .../classes/Swift/Mime/Headers/PathHeader.php | 143 ++ .../Swift/Mime/Headers/UnstructuredHeader.php | 112 ++ .../lib/classes/Swift/Mime/Message.php | 223 +++ .../lib/classes/Swift/Mime/MimeEntity.php | 117 ++ .../lib/classes/Swift/Mime/MimePart.php | 212 +++ .../Swift/Mime/ParameterizedHeader.php | 34 + .../Swift/Mime/SimpleHeaderFactory.php | 193 +++ .../classes/Swift/Mime/SimpleHeaderSet.php | 414 +++++ .../lib/classes/Swift/Mime/SimpleMessage.php | 655 +++++++ .../classes/Swift/Mime/SimpleMimeEntity.php | 843 +++++++++ .../lib/classes/Swift/MimePart.php | 59 + .../lib/classes/Swift/NullTransport.php | 39 + .../lib/classes/Swift/OutputByteStream.php | 46 + .../classes/Swift/Plugins/AntiFloodPlugin.php | 141 ++ .../Swift/Plugins/BandwidthMonitorPlugin.php | 164 ++ .../Swift/Plugins/Decorator/Replacements.php | 31 + .../classes/Swift/Plugins/DecoratorPlugin.php | 204 +++ .../Swift/Plugins/ImpersonatePlugin.php | 69 + .../lib/classes/Swift/Plugins/Logger.php | 36 + .../classes/Swift/Plugins/LoggerPlugin.php | 142 ++ .../Swift/Plugins/Loggers/ArrayLogger.php | 72 + .../Swift/Plugins/Loggers/EchoLogger.php | 58 + .../classes/Swift/Plugins/MessageLogger.php | 74 + .../Swift/Plugins/Pop/Pop3Connection.php | 31 + .../Swift/Plugins/Pop/Pop3Exception.php | 27 + .../Swift/Plugins/PopBeforeSmtpPlugin.php | 273 +++ .../Swift/Plugins/RedirectingPlugin.php | 213 +++ .../lib/classes/Swift/Plugins/Reporter.php | 32 + .../classes/Swift/Plugins/ReporterPlugin.php | 61 + .../Swift/Plugins/Reporters/HitReporter.php | 59 + .../Swift/Plugins/Reporters/HtmlReporter.php | 39 + .../lib/classes/Swift/Plugins/Sleeper.php | 24 + .../classes/Swift/Plugins/ThrottlerPlugin.php | 200 +++ .../lib/classes/Swift/Plugins/Timer.php | 24 + .../lib/classes/Swift/Preferences.php | 103 ++ .../Swift/ReplacementFilterFactory.php | 27 + .../classes/Swift/RfcComplianceException.php | 27 + .../lib/classes/Swift/SendmailTransport.php | 45 + .../lib/classes/Swift/SignedMessage.php | 23 + .../swiftmailer/lib/classes/Swift/Signer.php | 20 + .../lib/classes/Swift/Signers/BodySigner.php | 33 + .../lib/classes/Swift/Signers/DKIMSigner.php | 698 ++++++++ .../classes/Swift/Signers/DomainKeySigner.php | 525 ++++++ .../classes/Swift/Signers/HeaderSigner.php | 65 + .../classes/Swift/Signers/OpenDKIMSigner.php | 190 ++ .../lib/classes/Swift/Signers/SMimeSigner.php | 436 +++++ .../lib/classes/Swift/SmtpTransport.php | 58 + .../swiftmailer/lib/classes/Swift/Spool.php | 53 + .../lib/classes/Swift/SpoolTransport.php | 47 + .../lib/classes/Swift/StreamFilter.php | 35 + .../ByteArrayReplacementFilter.php | 170 ++ .../StreamFilters/StringReplacementFilter.php | 66 + .../StringReplacementFilterFactory.php | 45 + .../lib/classes/Swift/SwiftException.php | 29 + .../lib/classes/Swift/Transport.php | 54 + .../Swift/Transport/AbstractSmtpTransport.php | 496 ++++++ .../Esmtp/Auth/CramMd5Authenticator.php | 81 + .../Esmtp/Auth/LoginAuthenticator.php | 51 + .../Esmtp/Auth/NTLMAuthenticator.php | 720 ++++++++ .../Esmtp/Auth/PlainAuthenticator.php | 50 + .../Esmtp/Auth/XOAuth2Authenticator.php | 70 + .../Swift/Transport/Esmtp/AuthHandler.php | 263 +++ .../Swift/Transport/Esmtp/Authenticator.php | 35 + .../classes/Swift/Transport/EsmtpHandler.php | 86 + .../Swift/Transport/EsmtpTransport.php | 413 +++++ .../Swift/Transport/FailoverTransport.php | 88 + .../lib/classes/Swift/Transport/IoBuffer.php | 67 + .../Swift/Transport/LoadBalancedTransport.php | 183 ++ .../classes/Swift/Transport/MailInvoker.php | 32 + .../classes/Swift/Transport/MailTransport.php | 295 ++++ .../classes/Swift/Transport/NullTransport.php | 93 + .../Swift/Transport/SendmailTransport.php | 160 ++ .../Swift/Transport/SimpleMailInvoker.php | 39 + .../lib/classes/Swift/Transport/SmtpAgent.php | 36 + .../Swift/Transport/SpoolTransport.php | 117 ++ .../classes/Swift/Transport/StreamBuffer.php | 325 ++++ .../lib/classes/Swift/TransportException.php | 29 + .../lib/classes/Swift/Validate.php | 43 + .../lib/dependency_maps/cache_deps.php | 23 + .../lib/dependency_maps/message_deps.php | 9 + .../lib/dependency_maps/mime_deps.php | 123 ++ .../lib/dependency_maps/transport_deps.php | 76 + .../swiftmailer/lib/mime_types.php | 1007 +++++++++++ .../swiftmailer/lib/preferences.php | 25 + .../swiftmailer/lib/swift_init.php | 28 + .../swiftmailer/lib/swift_required.php | 30 + .../swiftmailer/lib/swift_required_pear.php | 30 + .../lib/swiftmailer_generate_mimes_config.php | 193 +++ .../swiftmailer/swiftmailer/phpunit.xml.dist | 39 + .../tests/IdenticalBinaryConstraint.php | 62 + .../swiftmailer/tests/StreamCollector.php | 11 + .../tests/SwiftMailerSmokeTestCase.php | 46 + .../swiftmailer/tests/SwiftMailerTestCase.php | 34 + .../_samples/charsets/iso-2022-jp/one.txt | 11 + .../_samples/charsets/iso-8859-1/one.txt | 19 + .../tests/_samples/charsets/utf-8/one.txt | 22 + .../tests/_samples/charsets/utf-8/three.txt | 45 + .../tests/_samples/charsets/utf-8/two.txt | 3 + .../tests/_samples/dkim/dkim.test.priv | 15 + .../tests/_samples/dkim/dkim.test.pub | 6 + .../swiftmailer/tests/_samples/files/data.txt | 1 + .../tests/_samples/files/swiftmailer.png | Bin 0 -> 3194 bytes .../tests/_samples/files/textfile.zip | Bin 0 -> 202 bytes .../swiftmailer/tests/_samples/smime/CA.srl | 1 + .../swiftmailer/tests/_samples/smime/ca.crt | 21 + .../swiftmailer/tests/_samples/smime/ca.key | 27 + .../tests/_samples/smime/create-cert.sh | 40 + .../tests/_samples/smime/encrypt.crt | 19 + .../tests/_samples/smime/encrypt.key | 27 + .../tests/_samples/smime/encrypt2.crt | 19 + .../tests/_samples/smime/encrypt2.key | 27 + .../tests/_samples/smime/intermediate.crt | 19 + .../tests/_samples/smime/intermediate.key | 27 + .../swiftmailer/tests/_samples/smime/sign.crt | 19 + .../swiftmailer/tests/_samples/smime/sign.key | 27 + .../tests/_samples/smime/sign2.crt | 19 + .../tests/_samples/smime/sign2.key | 27 + .../tests/acceptance.conf.php.default | 44 + .../Swift/AttachmentAcceptanceTest.php | 12 + .../FileByteStreamAcceptanceTest.php | 174 ++ ...leCharacterReaderFactoryAcceptanceTest.php | 179 ++ .../DependencyContainerAcceptanceTest.php | 24 + .../Swift/EmbeddedFileAcceptanceTest.php | 12 + .../Encoder/Base64EncoderAcceptanceTest.php | 45 + .../Swift/Encoder/QpEncoderAcceptanceTest.php | 54 + .../Encoder/Rfc2231EncoderAcceptanceTest.php | 50 + .../Swift/EncodingAcceptanceTest.php | 30 + .../KeyCache/ArrayKeyCacheAcceptanceTest.php | 173 ++ .../KeyCache/DiskKeyCacheAcceptanceTest.php | 183 ++ .../Swift/MessageAcceptanceTest.php | 57 + .../Swift/Mime/AttachmentAcceptanceTest.php | 125 ++ .../Base64ContentEncoderAcceptanceTest.php | 56 + .../NativeQpContentEncoderAcceptanceTest.php | 88 + .../PlainContentEncoderAcceptanceTest.php | 88 + .../QpContentEncoderAcceptanceTest.php | 160 ++ .../Swift/Mime/EmbeddedFileAcceptanceTest.php | 138 ++ .../Base64HeaderEncoderAcceptanceTest.php | 32 + .../Swift/Mime/MimePartAcceptanceTest.php | 129 ++ .../Mime/SimpleMessageAcceptanceTest.php | 1251 ++++++++++++++ .../Swift/MimePartAcceptanceTest.php | 15 + .../AbstractStreamBufferAcceptanceTest.php | 133 ++ .../BasicSocketAcceptanceTest.php | 33 + .../StreamBuffer/ProcessAcceptanceTest.php | 26 + .../StreamBuffer/SocketTimeoutTest.php | 67 + .../StreamBuffer/SslSocketAcceptanceTest.php | 40 + .../StreamBuffer/TlsSocketAcceptanceTest.php | 39 + .../swiftmailer/tests/bootstrap.php | 21 + .../tests/bug/Swift/Bug111Test.php | 42 + .../tests/bug/Swift/Bug118Test.php | 20 + .../tests/bug/Swift/Bug206Test.php | 38 + .../tests/bug/Swift/Bug274Test.php | 21 + .../swiftmailer/tests/bug/Swift/Bug34Test.php | 75 + .../swiftmailer/tests/bug/Swift/Bug35Test.php | 73 + .../swiftmailer/tests/bug/Swift/Bug38Test.php | 194 +++ .../tests/bug/Swift/Bug518Test.php | 38 + .../swiftmailer/tests/bug/Swift/Bug51Test.php | 121 ++ .../tests/bug/Swift/Bug534Test.php | 38 + .../tests/bug/Swift/Bug650Test.php | 36 + .../swiftmailer/tests/bug/Swift/Bug71Test.php | 20 + .../swiftmailer/tests/bug/Swift/Bug76Test.php | 82 + ...FileByteStreamConsecutiveReadCallsTest.php | 19 + .../tests/fixtures/MimeEntityFixture.php | 59 + .../swiftmailer/tests/smoke.conf.php.default | 63 + .../smoke/Swift/Smoke/AttachmentSmokeTest.php | 33 + .../smoke/Swift/Smoke/BasicSmokeTest.php | 23 + .../Smoke/HtmlWithAttachmentSmokeTest.php | 31 + .../Swift/Smoke/InternationalSmokeTest.php | 40 + .../Swift/ByteStream/ArrayByteStreamTest.php | 203 +++ .../GenericFixedWidthReaderTest.php | 43 + .../CharacterReader/UsAsciiReaderTest.php | 52 + .../Swift/CharacterReader/Utf8ReaderTest.php | 65 + .../ArrayCharacterStreamTest.php | 360 ++++ .../unit/Swift/DependencyContainerTest.php | 175 ++ .../unit/Swift/Encoder/Base64EncoderTest.php | 173 ++ .../unit/Swift/Encoder/QpEncoderTest.php | 402 +++++ .../unit/Swift/Encoder/Rfc2231EncoderTest.php | 141 ++ .../unit/Swift/Events/CommandEventTest.php | 36 + .../unit/Swift/Events/EventObjectTest.php | 34 + .../unit/Swift/Events/ResponseEventTest.php | 40 + .../tests/unit/Swift/Events/SendEventTest.php | 99 ++ .../Events/SimpleEventDispatcherTest.php | 142 ++ .../Swift/Events/TransportChangeEventTest.php | 32 + .../Events/TransportExceptionEventTest.php | 43 + .../unit/Swift/KeyCache/ArrayKeyCacheTest.php | 242 +++ .../SimpleKeyCacheInputStreamTest.php | 73 + .../Mailer/ArrayRecipientIteratorTest.php | 42 + .../tests/unit/Swift/MailerTest.php | 147 ++ .../tests/unit/Swift/MessageTest.php | 130 ++ .../Swift/Mime/AbstractMimeEntityTest.php | 1054 ++++++++++++ .../tests/unit/Swift/Mime/AttachmentTest.php | 320 ++++ .../Base64ContentEncoderTest.php | 323 ++++ .../PlainContentEncoderTest.php | 173 ++ .../ContentEncoder/QpContentEncoderTest.php | 518 ++++++ .../unit/Swift/Mime/EmbeddedFileTest.php | 57 + .../HeaderEncoder/Base64HeaderEncoderTest.php | 13 + .../HeaderEncoder/QpHeaderEncoderTest.php | 223 +++ .../Swift/Mime/Headers/DateHeaderTest.php | 69 + .../Mime/Headers/IdentificationHeaderTest.php | 189 ++ .../Swift/Mime/Headers/MailboxHeaderTest.php | 327 ++++ .../Mime/Headers/ParameterizedHeaderTest.php | 400 +++++ .../Swift/Mime/Headers/PathHeaderTest.php | 77 + .../Mime/Headers/UnstructuredHeaderTest.php | 355 ++++ .../tests/unit/Swift/Mime/MimePartTest.php | 233 +++ .../Swift/Mime/SimpleHeaderFactoryTest.php | 168 ++ .../unit/Swift/Mime/SimpleHeaderSetTest.php | 739 ++++++++ .../unit/Swift/Mime/SimpleMessageTest.php | 829 +++++++++ .../unit/Swift/Mime/SimpleMimeEntityTest.php | 11 + .../Swift/Plugins/AntiFloodPluginTest.php | 95 + .../Plugins/BandwidthMonitorPluginTest.php | 130 ++ .../Swift/Plugins/DecoratorPluginTest.php | 269 +++ .../unit/Swift/Plugins/LoggerPluginTest.php | 190 ++ .../Swift/Plugins/Loggers/ArrayLoggerTest.php | 65 + .../Swift/Plugins/Loggers/EchoLoggerTest.php | 24 + .../Swift/Plugins/PopBeforeSmtpPluginTest.php | 103 ++ .../Swift/Plugins/RedirectingPluginTest.php | 185 ++ .../unit/Swift/Plugins/ReporterPluginTest.php | 88 + .../Plugins/Reporters/HitReporterTest.php | 64 + .../Plugins/Reporters/HtmlReporterTest.php | 54 + .../Swift/Plugins/ThrottlerPluginTest.php | 104 ++ .../unit/Swift/Signers/DKIMSignerTest.php | 227 +++ .../unit/Swift/Signers/OpenDKIMSignerTest.php | 45 + .../unit/Swift/Signers/SMimeSignerTest.php | 554 ++++++ .../ByteArrayReplacementFilterTest.php | 131 ++ .../StringReplacementFilterFactoryTest.php | 38 + .../StringReplacementFilterTest.php | 55 + .../AbstractSmtpEventSupportTest.php | 560 ++++++ .../unit/Swift/Transport/AbstractSmtpTest.php | 1249 ++++++++++++++ .../Esmtp/Auth/CramMd5AuthenticatorTest.php | 66 + .../Esmtp/Auth/LoginAuthenticatorTest.php | 66 + .../Esmtp/Auth/NTLMAuthenticatorTest.php | 235 +++ .../Esmtp/Auth/PlainAuthenticatorTest.php | 69 + .../Swift/Transport/Esmtp/AuthHandlerTest.php | 167 ++ .../EsmtpTransport/ExtensionSupportTest.php | 528 ++++++ .../Swift/Transport/EsmtpTransportTest.php | 297 ++++ .../Swift/Transport/FailoverTransportTest.php | 520 ++++++ .../Transport/LoadBalancedTransportTest.php | 751 ++++++++ .../Swift/Transport/MailTransportTest.php | 535 ++++++ .../Swift/Transport/SendmailTransportTest.php | 151 ++ .../unit/Swift/Transport/StreamBufferTest.php | 45 + vendor/symfony/console/.gitignore | 3 + vendor/symfony/console/Application.php | 1144 ++++++++++++ vendor/symfony/console/CHANGELOG.md | 73 + vendor/symfony/console/Command/Command.php | 681 ++++++++ .../symfony/console/Command/HelpCommand.php | 93 + .../symfony/console/Command/ListCommand.php | 97 ++ vendor/symfony/console/ConsoleEvents.php | 61 + .../Descriptor/ApplicationDescription.php | 160 ++ .../symfony/console/Descriptor/Descriptor.php | 122 ++ .../Descriptor/DescriptorInterface.php | 31 + .../console/Descriptor/JsonDescriptor.php | 166 ++ .../console/Descriptor/MarkdownDescriptor.php | 143 ++ .../console/Descriptor/TextDescriptor.php | 287 ++++ .../console/Descriptor/XmlDescriptor.php | 263 +++ .../console/Event/ConsoleCommandEvent.php | 62 + vendor/symfony/console/Event/ConsoleEvent.php | 67 + .../console/Event/ConsoleExceptionEvent.php | 67 + .../console/Event/ConsoleTerminateEvent.php | 58 + .../Exception/CommandNotFoundException.php | 43 + .../console/Exception/ExceptionInterface.php | 21 + .../Exception/InvalidArgumentException.php | 19 + .../Exception/InvalidOptionException.php | 21 + .../console/Exception/LogicException.php | 19 + .../console/Exception/RuntimeException.php | 19 + .../console/Formatter/OutputFormatter.php | 240 +++ .../Formatter/OutputFormatterInterface.php | 69 + .../Formatter/OutputFormatterStyle.php | 221 +++ .../OutputFormatterStyleInterface.php | 64 + .../Formatter/OutputFormatterStyleStack.php | 123 ++ .../console/Helper/DebugFormatterHelper.php | 127 ++ .../console/Helper/DescriptorHelper.php | 97 ++ .../symfony/console/Helper/DialogHelper.php | 502 ++++++ .../console/Helper/FormatterHelper.php | 82 + vendor/symfony/console/Helper/Helper.php | 119 ++ .../console/Helper/HelperInterface.php | 41 + vendor/symfony/console/Helper/HelperSet.php | 117 ++ .../console/Helper/InputAwareHelper.php | 33 + .../symfony/console/Helper/ProcessHelper.php | 151 ++ vendor/symfony/console/Helper/ProgressBar.php | 621 +++++++ .../symfony/console/Helper/ProgressHelper.php | 471 +++++ .../console/Helper/ProgressIndicator.php | 324 ++++ .../symfony/console/Helper/QuestionHelper.php | 449 +++++ .../console/Helper/SymfonyQuestionHelper.php | 107 ++ vendor/symfony/console/Helper/Table.php | 663 +++++++ vendor/symfony/console/Helper/TableCell.php | 79 + vendor/symfony/console/Helper/TableHelper.php | 269 +++ .../symfony/console/Helper/TableSeparator.php | 29 + vendor/symfony/console/Helper/TableStyle.php | 258 +++ vendor/symfony/console/Input/ArgvInput.php | 351 ++++ vendor/symfony/console/Input/ArrayInput.php | 210 +++ vendor/symfony/console/Input/Input.php | 236 +++ .../symfony/console/Input/InputArgument.php | 131 ++ .../console/Input/InputAwareInterface.php | 28 + .../symfony/console/Input/InputDefinition.php | 457 +++++ .../symfony/console/Input/InputInterface.php | 152 ++ vendor/symfony/console/Input/InputOption.php | 212 +++ vendor/symfony/console/Input/StringInput.php | 85 + vendor/symfony/console/LICENSE | 19 + .../symfony/console/Logger/ConsoleLogger.php | 119 ++ .../symfony/console/Output/BufferedOutput.php | 48 + .../symfony/console/Output/ConsoleOutput.php | 156 ++ .../console/Output/ConsoleOutputInterface.php | 35 + vendor/symfony/console/Output/NullOutput.php | 111 ++ vendor/symfony/console/Output/Output.php | 165 ++ .../console/Output/OutputInterface.php | 91 + .../symfony/console/Output/StreamOutput.php | 105 ++ .../console/Question/ChoiceQuestion.php | 177 ++ .../console/Question/ConfirmationQuestion.php | 61 + vendor/symfony/console/Question/Question.php | 250 +++ vendor/symfony/console/README.md | 20 + .../console/Resources/bin/hiddeninput.exe | Bin 0 -> 9216 bytes vendor/symfony/console/Shell.php | 233 +++ vendor/symfony/console/Style/OutputStyle.php | 116 ++ .../symfony/console/Style/StyleInterface.php | 159 ++ vendor/symfony/console/Style/SymfonyStyle.php | 440 +++++ .../console/Tester/ApplicationTester.php | 128 ++ .../symfony/console/Tester/CommandTester.php | 132 ++ .../symfony/console/Tests/ApplicationTest.php | 1146 +++++++++++++ .../console/Tests/Command/CommandTest.php | 395 +++++ .../console/Tests/Command/HelpCommandTest.php | 70 + .../console/Tests/Command/ListCommandTest.php | 112 ++ .../Descriptor/AbstractDescriptorTest.php | 106 ++ .../Tests/Descriptor/JsonDescriptorTest.php | 35 + .../Descriptor/MarkdownDescriptorTest.php | 27 + .../Tests/Descriptor/ObjectsProvider.php | 77 + .../Tests/Descriptor/TextDescriptorTest.php | 27 + .../Tests/Descriptor/XmlDescriptorTest.php | 27 + .../console/Tests/Fixtures/BarBucCommand.php | 11 + .../Tests/Fixtures/DescriptorApplication1.php | 18 + .../Tests/Fixtures/DescriptorApplication2.php | 24 + .../Tests/Fixtures/DescriptorCommand1.php | 27 + .../Tests/Fixtures/DescriptorCommand2.php | 32 + .../console/Tests/Fixtures/DummyOutput.php | 36 + .../console/Tests/Fixtures/Foo1Command.php | 26 + .../console/Tests/Fixtures/Foo2Command.php | 21 + .../console/Tests/Fixtures/Foo3Command.php | 29 + .../console/Tests/Fixtures/Foo4Command.php | 11 + .../console/Tests/Fixtures/Foo5Command.php | 10 + .../console/Tests/Fixtures/Foo6Command.php | 12 + .../console/Tests/Fixtures/FooCommand.php | 33 + .../Fixtures/FooSubnamespaced1Command.php | 26 + .../Fixtures/FooSubnamespaced2Command.php | 26 + .../console/Tests/Fixtures/FoobarCommand.php | 25 + .../Style/SymfonyStyle/command/command_0.php | 11 + .../Style/SymfonyStyle/command/command_1.php | 13 + .../Style/SymfonyStyle/command/command_10.php | 17 + .../Style/SymfonyStyle/command/command_11.php | 13 + .../Style/SymfonyStyle/command/command_2.php | 16 + .../Style/SymfonyStyle/command/command_3.php | 12 + .../Style/SymfonyStyle/command/command_4.php | 34 + .../Style/SymfonyStyle/command/command_5.php | 37 + .../Style/SymfonyStyle/command/command_6.php | 16 + .../Style/SymfonyStyle/command/command_7.php | 15 + .../Style/SymfonyStyle/command/command_8.php | 26 + .../Style/SymfonyStyle/command/command_9.php | 11 + .../Style/SymfonyStyle/output/output_0.txt | 3 + .../Style/SymfonyStyle/output/output_1.txt | 9 + .../Style/SymfonyStyle/output/output_10.txt | 7 + .../Style/SymfonyStyle/output/output_11.txt | 6 + .../Style/SymfonyStyle/output/output_2.txt | 13 + .../Style/SymfonyStyle/output/output_3.txt | 7 + .../Style/SymfonyStyle/output/output_4.txt | 32 + .../Style/SymfonyStyle/output/output_5.txt | 18 + .../Style/SymfonyStyle/output/output_6.txt | 6 + .../Style/SymfonyStyle/output/output_7.txt | 5 + .../Style/SymfonyStyle/output/output_8.txt | 9 + .../Style/SymfonyStyle/output/output_9.txt | 5 + .../console/Tests/Fixtures/TestCommand.php | 28 + .../console/Tests/Fixtures/application_1.json | 1 + .../console/Tests/Fixtures/application_1.md | 201 +++ .../console/Tests/Fixtures/application_1.txt | 17 + .../console/Tests/Fixtures/application_1.xml | 110 ++ .../console/Tests/Fixtures/application_2.json | 1 + .../console/Tests/Fixtures/application_2.md | 396 +++++ .../console/Tests/Fixtures/application_2.txt | 22 + .../console/Tests/Fixtures/application_2.xml | 190 ++ .../Tests/Fixtures/application_astext1.txt | 20 + .../Tests/Fixtures/application_astext2.txt | 16 + .../Tests/Fixtures/application_asxml1.txt | 146 ++ .../Tests/Fixtures/application_asxml2.txt | 37 + .../Tests/Fixtures/application_gethelp.txt | 1 + .../Fixtures/application_renderexception1.txt | 6 + .../Fixtures/application_renderexception2.txt | 8 + .../Fixtures/application_renderexception3.txt | 18 + .../application_renderexception3decorated.txt | 18 + .../Fixtures/application_renderexception4.txt | 7 + ...plication_renderexception_doublewidth1.txt | 8 + ..._renderexception_doublewidth1decorated.txt | 8 + ...plication_renderexception_doublewidth2.txt | 9 + .../Tests/Fixtures/application_run1.txt | 17 + .../Tests/Fixtures/application_run2.txt | 29 + .../Tests/Fixtures/application_run3.txt | 27 + .../Tests/Fixtures/application_run4.txt | 1 + .../console/Tests/Fixtures/command_1.json | 1 + .../console/Tests/Fixtures/command_1.md | 11 + .../console/Tests/Fixtures/command_1.txt | 7 + .../console/Tests/Fixtures/command_1.xml | 12 + .../console/Tests/Fixtures/command_2.json | 1 + .../console/Tests/Fixtures/command_2.md | 33 + .../console/Tests/Fixtures/command_2.txt | 13 + .../console/Tests/Fixtures/command_2.xml | 21 + .../console/Tests/Fixtures/command_astext.txt | 18 + .../console/Tests/Fixtures/command_asxml.txt | 38 + .../Tests/Fixtures/definition_astext.txt | 11 + .../Tests/Fixtures/definition_asxml.txt | 39 + .../Tests/Fixtures/input_argument_1.json | 1 + .../Tests/Fixtures/input_argument_1.md | 7 + .../Tests/Fixtures/input_argument_1.txt | 1 + .../Tests/Fixtures/input_argument_1.xml | 5 + .../Tests/Fixtures/input_argument_2.json | 1 + .../Tests/Fixtures/input_argument_2.md | 7 + .../Tests/Fixtures/input_argument_2.txt | 1 + .../Tests/Fixtures/input_argument_2.xml | 5 + .../Tests/Fixtures/input_argument_3.json | 1 + .../Tests/Fixtures/input_argument_3.md | 7 + .../Tests/Fixtures/input_argument_3.txt | 1 + .../Tests/Fixtures/input_argument_3.xml | 7 + .../Tests/Fixtures/input_argument_4.json | 1 + .../Tests/Fixtures/input_argument_4.md | 8 + .../Tests/Fixtures/input_argument_4.txt | 2 + .../Tests/Fixtures/input_argument_4.xml | 6 + .../Tests/Fixtures/input_definition_1.json | 1 + .../Tests/Fixtures/input_definition_1.md | 0 .../Tests/Fixtures/input_definition_1.txt | 0 .../Tests/Fixtures/input_definition_1.xml | 5 + .../Tests/Fixtures/input_definition_2.json | 1 + .../Tests/Fixtures/input_definition_2.md | 9 + .../Tests/Fixtures/input_definition_2.txt | 2 + .../Tests/Fixtures/input_definition_2.xml | 10 + .../Tests/Fixtures/input_definition_3.json | 1 + .../Tests/Fixtures/input_definition_3.md | 11 + .../Tests/Fixtures/input_definition_3.txt | 2 + .../Tests/Fixtures/input_definition_3.xml | 9 + .../Tests/Fixtures/input_definition_4.json | 1 + .../Tests/Fixtures/input_definition_4.md | 21 + .../Tests/Fixtures/input_definition_4.txt | 5 + .../Tests/Fixtures/input_definition_4.xml | 14 + .../Tests/Fixtures/input_option_1.json | 1 + .../console/Tests/Fixtures/input_option_1.md | 9 + .../console/Tests/Fixtures/input_option_1.txt | 1 + .../console/Tests/Fixtures/input_option_1.xml | 4 + .../Tests/Fixtures/input_option_2.json | 1 + .../console/Tests/Fixtures/input_option_2.md | 9 + .../console/Tests/Fixtures/input_option_2.txt | 1 + .../console/Tests/Fixtures/input_option_2.xml | 7 + .../Tests/Fixtures/input_option_3.json | 1 + .../console/Tests/Fixtures/input_option_3.md | 9 + .../console/Tests/Fixtures/input_option_3.txt | 1 + .../console/Tests/Fixtures/input_option_3.xml | 5 + .../Tests/Fixtures/input_option_4.json | 1 + .../console/Tests/Fixtures/input_option_4.md | 9 + .../console/Tests/Fixtures/input_option_4.txt | 1 + .../console/Tests/Fixtures/input_option_4.xml | 5 + .../Tests/Fixtures/input_option_5.json | 1 + .../console/Tests/Fixtures/input_option_5.md | 10 + .../console/Tests/Fixtures/input_option_5.txt | 2 + .../console/Tests/Fixtures/input_option_5.xml | 6 + .../Tests/Fixtures/input_option_6.json | 1 + .../console/Tests/Fixtures/input_option_6.md | 9 + .../console/Tests/Fixtures/input_option_6.txt | 1 + .../console/Tests/Fixtures/input_option_6.xml | 5 + .../OutputFormatterStyleStackTest.php | 70 + .../Formatter/OutputFormatterStyleTest.php | 99 ++ .../Tests/Formatter/OutputFormatterTest.php | 273 +++ .../Tests/Helper/FormatterHelperTest.php | 92 + .../console/Tests/Helper/HelperSetTest.php | 133 ++ .../console/Tests/Helper/HelperTest.php | 54 + .../Tests/Helper/LegacyDialogHelperTest.php | 263 +++ .../Tests/Helper/LegacyProgressHelperTest.php | 224 +++ .../Tests/Helper/LegacyTableHelperTest.php | 316 ++++ .../Tests/Helper/ProcessHelperTest.php | 117 ++ .../console/Tests/Helper/ProgressBarTest.php | 664 +++++++ .../Tests/Helper/ProgressIndicatorTest.php | 182 ++ .../Tests/Helper/QuestionHelperTest.php | 435 +++++ .../console/Tests/Helper/TableStyleTest.php | 27 + .../console/Tests/Helper/TableTest.php | 645 +++++++ .../console/Tests/Input/ArgvInputTest.php | 317 ++++ .../console/Tests/Input/ArrayInputTest.php | 138 ++ .../console/Tests/Input/InputArgumentTest.php | 111 ++ .../Tests/Input/InputDefinitionTest.php | 437 +++++ .../console/Tests/Input/InputOptionTest.php | 204 +++ .../symfony/console/Tests/Input/InputTest.php | 132 ++ .../console/Tests/Input/StringInputTest.php | 99 ++ .../Tests/Logger/ConsoleLoggerTest.php | 58 + .../Tests/Output/ConsoleOutputTest.php | 25 + .../console/Tests/Output/NullOutputTest.php | 39 + .../console/Tests/Output/OutputTest.php | 175 ++ .../console/Tests/Output/StreamOutputTest.php | 60 + .../console/Tests/Style/SymfonyStyleTest.php | 89 + .../Tests/Tester/ApplicationTesterTest.php | 69 + .../Tests/Tester/CommandTesterTest.php | 84 + vendor/symfony/console/composer.json | 44 + vendor/symfony/console/phpunit.xml.dist | 39 + vendor/symfony/event-dispatcher/.gitignore | 3 + vendor/symfony/event-dispatcher/CHANGELOG.md | 23 + .../ContainerAwareEventDispatcher.php | 187 ++ .../Debug/TraceableEventDispatcher.php | 339 ++++ .../TraceableEventDispatcherInterface.php | 34 + .../Debug/WrappedListener.php | 71 + .../RegisterListenersPass.php | 109 ++ vendor/symfony/event-dispatcher/Event.php | 120 ++ .../event-dispatcher/EventDispatcher.php | 177 ++ .../EventDispatcherInterface.php | 88 + .../EventSubscriberInterface.php | 46 + .../symfony/event-dispatcher/GenericEvent.php | 186 ++ .../ImmutableEventDispatcher.php | 93 + vendor/symfony/event-dispatcher/LICENSE | 19 + vendor/symfony/event-dispatcher/README.md | 15 + .../Tests/AbstractEventDispatcherTest.php | 382 +++++ .../ContainerAwareEventDispatcherTest.php | 273 +++ .../Debug/TraceableEventDispatcherTest.php | 199 +++ .../RegisterListenersPassTest.php | 200 +++ .../Tests/EventDispatcherTest.php | 22 + .../event-dispatcher/Tests/EventTest.php | 96 ++ .../Tests/GenericEventTest.php | 139 ++ .../Tests/ImmutableEventDispatcherTest.php | 105 ++ vendor/symfony/event-dispatcher/composer.json | 44 + .../symfony/event-dispatcher/phpunit.xml.dist | 29 + vendor/symfony/polyfill-mbstring/LICENSE | 19 + vendor/symfony/polyfill-mbstring/Mbstring.php | 664 +++++++ vendor/symfony/polyfill-mbstring/README.md | 13 + .../Resources/unidata/lowerCase.php | 1101 ++++++++++++ .../Resources/unidata/upperCase.php | 1109 ++++++++++++ .../symfony/polyfill-mbstring/bootstrap.php | 56 + .../symfony/polyfill-mbstring/composer.json | 34 + vendor/zendframework/zendxml/.gitignore | 5 + vendor/zendframework/zendxml/.travis.yml | 43 + vendor/zendframework/zendxml/CHANGELOG.md | 24 + vendor/zendframework/zendxml/LICENSE.md | 12 + vendor/zendframework/zendxml/README.md | 50 + vendor/zendframework/zendxml/composer.json | 40 + .../ZendXml/Exception/ExceptionInterface.php | 14 + .../Exception/InvalidArgumentException.php | 17 + .../ZendXml/Exception/RuntimeException.php | 17 + .../zendxml/library/ZendXml/Security.php | 374 ++++ .../zendframework/zendxml/tests/Bootstrap.php | 92 + .../tests/ZendXmlTest/MultibyteTest.php | 125 ++ .../tests/ZendXmlTest/SecurityTest.php | 135 ++ .../zendxml/tests/phpunit.xml.dist | 27 + 1787 files changed, 123616 insertions(+), 45 deletions(-) create mode 100644 vendor/aferrandini/phpqrcode/.gitignore create mode 100755 vendor/aferrandini/phpqrcode/LICENSE create mode 100755 vendor/aferrandini/phpqrcode/VERSION create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_1.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_1.png create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_10.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_10.png create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_11.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_11.png create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_12.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_12.png create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_13.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_13.png create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_14.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_14.png create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_15.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_15.png create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_16.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_16.png create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_17.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_17.png create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_18.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_18.png create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_19.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_19.png create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_2.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_2.png create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_20.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_20.png create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_21.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_21.png create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_22.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_22.png create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_23.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_23.png create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_24.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_24.png create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_25.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_25.png create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_26.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_26.png create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_27.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_27.png create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_28.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_28.png create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_29.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_29.png create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_3.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_3.png create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_30.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_30.png create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_31.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_31.png create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_32.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_32.png create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_33.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_33.png create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_34.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_34.png create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_35.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_35.png create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_36.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_36.png create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_37.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_37.png create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_38.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_38.png create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_39.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_39.png create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_4.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_4.png create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_40.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_40.png create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_5.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_5.png create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_6.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_6.png create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_7.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_7.png create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_8.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_8.png create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_9.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/frame_9.png create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_0/mask_101_0.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_0/mask_105_0.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_0/mask_109_0.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_0/mask_113_0.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_0/mask_117_0.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_0/mask_121_0.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_0/mask_125_0.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_0/mask_129_0.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_0/mask_133_0.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_0/mask_137_0.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_0/mask_141_0.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_0/mask_145_0.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_0/mask_149_0.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_0/mask_153_0.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_0/mask_157_0.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_0/mask_161_0.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_0/mask_165_0.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_0/mask_169_0.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_0/mask_173_0.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_0/mask_177_0.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_0/mask_21_0.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_0/mask_25_0.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_0/mask_29_0.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_0/mask_33_0.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_0/mask_37_0.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_0/mask_41_0.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_0/mask_45_0.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_0/mask_49_0.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_0/mask_53_0.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_0/mask_57_0.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_0/mask_61_0.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_0/mask_65_0.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_0/mask_69_0.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_0/mask_73_0.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_0/mask_77_0.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_0/mask_81_0.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_0/mask_85_0.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_0/mask_89_0.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_0/mask_93_0.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_0/mask_97_0.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_1/mask_101_1.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_1/mask_105_1.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_1/mask_109_1.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_1/mask_113_1.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_1/mask_117_1.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_1/mask_121_1.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_1/mask_125_1.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_1/mask_129_1.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_1/mask_133_1.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_1/mask_137_1.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_1/mask_141_1.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_1/mask_145_1.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_1/mask_149_1.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_1/mask_153_1.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_1/mask_157_1.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_1/mask_161_1.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_1/mask_165_1.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_1/mask_169_1.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_1/mask_173_1.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_1/mask_177_1.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_1/mask_21_1.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_1/mask_25_1.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_1/mask_29_1.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_1/mask_33_1.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_1/mask_37_1.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_1/mask_41_1.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_1/mask_45_1.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_1/mask_49_1.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_1/mask_53_1.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_1/mask_57_1.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_1/mask_61_1.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_1/mask_65_1.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_1/mask_69_1.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_1/mask_73_1.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_1/mask_77_1.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_1/mask_81_1.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_1/mask_85_1.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_1/mask_89_1.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_1/mask_93_1.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_1/mask_97_1.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_2/mask_101_2.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_2/mask_105_2.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_2/mask_109_2.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_2/mask_113_2.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_2/mask_117_2.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_2/mask_121_2.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_2/mask_125_2.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_2/mask_129_2.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_2/mask_133_2.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_2/mask_137_2.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_2/mask_141_2.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_2/mask_145_2.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_2/mask_149_2.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_2/mask_153_2.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_2/mask_157_2.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_2/mask_161_2.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_2/mask_165_2.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_2/mask_169_2.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_2/mask_173_2.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_2/mask_177_2.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_2/mask_21_2.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_2/mask_25_2.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_2/mask_29_2.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_2/mask_33_2.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_2/mask_37_2.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_2/mask_41_2.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_2/mask_45_2.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_2/mask_49_2.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_2/mask_53_2.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_2/mask_57_2.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_2/mask_61_2.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_2/mask_65_2.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_2/mask_69_2.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_2/mask_73_2.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_2/mask_77_2.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_2/mask_81_2.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_2/mask_85_2.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_2/mask_89_2.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_2/mask_93_2.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_2/mask_97_2.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_3/mask_101_3.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_3/mask_105_3.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_3/mask_109_3.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_3/mask_113_3.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_3/mask_117_3.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_3/mask_121_3.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_3/mask_125_3.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_3/mask_129_3.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_3/mask_133_3.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_3/mask_137_3.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_3/mask_141_3.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_3/mask_145_3.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_3/mask_149_3.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_3/mask_153_3.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_3/mask_157_3.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_3/mask_161_3.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_3/mask_165_3.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_3/mask_169_3.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_3/mask_173_3.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_3/mask_177_3.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_3/mask_21_3.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_3/mask_25_3.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_3/mask_29_3.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_3/mask_33_3.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_3/mask_37_3.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_3/mask_41_3.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_3/mask_45_3.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_3/mask_49_3.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_3/mask_53_3.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_3/mask_57_3.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_3/mask_61_3.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_3/mask_65_3.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_3/mask_69_3.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_3/mask_73_3.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_3/mask_77_3.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_3/mask_81_3.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_3/mask_85_3.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_3/mask_89_3.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_3/mask_93_3.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_3/mask_97_3.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_4/mask_101_4.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_4/mask_105_4.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_4/mask_109_4.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_4/mask_113_4.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_4/mask_117_4.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_4/mask_121_4.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_4/mask_125_4.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_4/mask_129_4.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_4/mask_133_4.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_4/mask_137_4.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_4/mask_141_4.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_4/mask_145_4.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_4/mask_149_4.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_4/mask_153_4.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_4/mask_157_4.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_4/mask_161_4.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_4/mask_165_4.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_4/mask_169_4.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_4/mask_173_4.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_4/mask_177_4.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_4/mask_21_4.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_4/mask_25_4.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_4/mask_29_4.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_4/mask_33_4.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_4/mask_37_4.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_4/mask_41_4.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_4/mask_45_4.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_4/mask_49_4.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_4/mask_53_4.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_4/mask_57_4.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_4/mask_61_4.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_4/mask_65_4.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_4/mask_69_4.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_4/mask_73_4.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_4/mask_77_4.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_4/mask_81_4.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_4/mask_85_4.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_4/mask_89_4.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_4/mask_93_4.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_4/mask_97_4.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_5/mask_101_5.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_5/mask_105_5.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_5/mask_109_5.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_5/mask_113_5.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_5/mask_117_5.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_5/mask_121_5.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_5/mask_125_5.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_5/mask_129_5.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_5/mask_133_5.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_5/mask_137_5.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_5/mask_141_5.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_5/mask_145_5.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_5/mask_149_5.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_5/mask_153_5.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_5/mask_157_5.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_5/mask_161_5.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_5/mask_165_5.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_5/mask_169_5.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_5/mask_173_5.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_5/mask_177_5.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_5/mask_21_5.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_5/mask_25_5.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_5/mask_29_5.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_5/mask_33_5.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_5/mask_37_5.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_5/mask_41_5.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_5/mask_45_5.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_5/mask_49_5.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_5/mask_53_5.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_5/mask_57_5.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_5/mask_61_5.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_5/mask_65_5.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_5/mask_69_5.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_5/mask_73_5.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_5/mask_77_5.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_5/mask_81_5.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_5/mask_85_5.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_5/mask_89_5.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_5/mask_93_5.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_5/mask_97_5.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_6/mask_101_6.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_6/mask_105_6.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_6/mask_109_6.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_6/mask_113_6.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_6/mask_117_6.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_6/mask_121_6.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_6/mask_125_6.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_6/mask_129_6.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_6/mask_133_6.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_6/mask_137_6.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_6/mask_141_6.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_6/mask_145_6.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_6/mask_149_6.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_6/mask_153_6.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_6/mask_157_6.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_6/mask_161_6.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_6/mask_165_6.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_6/mask_169_6.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_6/mask_173_6.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_6/mask_177_6.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_6/mask_21_6.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_6/mask_25_6.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_6/mask_29_6.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_6/mask_33_6.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_6/mask_37_6.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_6/mask_41_6.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_6/mask_45_6.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_6/mask_49_6.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_6/mask_53_6.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_6/mask_57_6.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_6/mask_61_6.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_6/mask_65_6.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_6/mask_69_6.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_6/mask_73_6.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_6/mask_77_6.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_6/mask_81_6.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_6/mask_85_6.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_6/mask_89_6.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_6/mask_93_6.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_6/mask_97_6.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_7/mask_101_7.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_7/mask_105_7.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_7/mask_109_7.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_7/mask_113_7.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_7/mask_117_7.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_7/mask_121_7.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_7/mask_125_7.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_7/mask_129_7.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_7/mask_133_7.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_7/mask_137_7.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_7/mask_141_7.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_7/mask_145_7.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_7/mask_149_7.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_7/mask_153_7.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_7/mask_157_7.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_7/mask_161_7.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_7/mask_165_7.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_7/mask_169_7.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_7/mask_173_7.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_7/mask_177_7.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_7/mask_21_7.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_7/mask_25_7.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_7/mask_29_7.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_7/mask_33_7.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_7/mask_37_7.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_7/mask_41_7.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_7/mask_45_7.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_7/mask_49_7.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_7/mask_53_7.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_7/mask_57_7.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_7/mask_61_7.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_7/mask_65_7.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_7/mask_69_7.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_7/mask_73_7.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_7/mask_77_7.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_7/mask_81_7.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_7/mask_85_7.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_7/mask_89_7.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_7/mask_93_7.dat create mode 100755 vendor/aferrandini/phpqrcode/cache/mask_7/mask_97_7.dat create mode 100644 vendor/aferrandini/phpqrcode/composer.json create mode 100644 vendor/aferrandini/phpqrcode/lib/PHPQRCode.php create mode 100755 vendor/aferrandini/phpqrcode/lib/PHPQRCode/Autoloader.php create mode 100644 vendor/aferrandini/phpqrcode/lib/PHPQRCode/Constants.php create mode 100644 vendor/aferrandini/phpqrcode/lib/PHPQRCode/FrameFiller.php create mode 100755 vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRbitstream.php create mode 100644 vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRcode.php create mode 100755 vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRencode.php create mode 100755 vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRimage.php create mode 100755 vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRinput.php create mode 100644 vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRinputItem.php create mode 100755 vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRmask.php create mode 100644 vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRrawcode.php create mode 100755 vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRrs.php create mode 100644 vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRrsItem.php create mode 100644 vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRrsblock.php create mode 100755 vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRspec.php create mode 100755 vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRsplit.php create mode 100644 vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRstr.php create mode 100755 vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRtools.php create mode 100755 vendor/aferrandini/phpqrcode/readme.md create mode 100644 vendor/autoload.php create mode 120000 vendor/bin/picofeed create mode 100644 vendor/christian-riesen/base32/.gitignore create mode 100644 vendor/christian-riesen/base32/.scrutinizer.yml create mode 100644 vendor/christian-riesen/base32/.travis.yml create mode 100644 vendor/christian-riesen/base32/LICENSE create mode 100644 vendor/christian-riesen/base32/README.md create mode 100644 vendor/christian-riesen/base32/build.xml create mode 100644 vendor/christian-riesen/base32/composer.json create mode 100644 vendor/christian-riesen/base32/phpunit.xml.dist create mode 100644 vendor/christian-riesen/base32/src/Base32.php create mode 100644 vendor/christian-riesen/base32/tests/Base32Test.php create mode 100644 vendor/christian-riesen/base32/tests/bootstrap.php create mode 100644 vendor/christian-riesen/otp/.gitignore create mode 100644 vendor/christian-riesen/otp/.travis.yml create mode 100644 vendor/christian-riesen/otp/LICENSE create mode 100644 vendor/christian-riesen/otp/README.md create mode 100644 vendor/christian-riesen/otp/composer.json create mode 100644 vendor/christian-riesen/otp/example/index.php create mode 100644 vendor/christian-riesen/otp/phpunit.xml.dist create mode 100644 vendor/christian-riesen/otp/src/Otp/GoogleAuthenticator.php create mode 100644 vendor/christian-riesen/otp/src/Otp/Otp.php create mode 100644 vendor/christian-riesen/otp/src/Otp/OtpInterface.php create mode 100644 vendor/christian-riesen/otp/tests/Otp/GoogleAuthenticatorTest.php create mode 100644 vendor/christian-riesen/otp/tests/Otp/OtpTest.php create mode 100644 vendor/composer/ClassLoader.php create mode 100644 vendor/composer/LICENSE create mode 100644 vendor/composer/autoload_classmap.php create mode 100644 vendor/composer/autoload_files.php create mode 100644 vendor/composer/autoload_namespaces.php create mode 100644 vendor/composer/autoload_psr4.php create mode 100644 vendor/composer/autoload_real.php create mode 100644 vendor/composer/autoload_static.php create mode 100644 vendor/composer/installed.json create mode 100644 vendor/eluceo/ical/.gitignore create mode 100644 vendor/eluceo/ical/.php_cs create mode 100644 vendor/eluceo/ical/.scrutinizer.yml create mode 100644 vendor/eluceo/ical/.travis.yml create mode 100644 vendor/eluceo/ical/CHANGELOG.md create mode 100644 vendor/eluceo/ical/LICENSE create mode 100644 vendor/eluceo/ical/README.md create mode 100644 vendor/eluceo/ical/UPGRADE.md create mode 100644 vendor/eluceo/ical/composer.json create mode 100644 vendor/eluceo/ical/examples/example1.php create mode 100644 vendor/eluceo/ical/examples/example2.php create mode 100644 vendor/eluceo/ical/examples/example3.php create mode 100644 vendor/eluceo/ical/examples/example4.php create mode 100644 vendor/eluceo/ical/examples/example5.php create mode 100644 vendor/eluceo/ical/examples/example6.php create mode 100644 vendor/eluceo/ical/examples/example7.php create mode 100644 vendor/eluceo/ical/phpunit.xml.dist create mode 100644 vendor/eluceo/ical/src/Eluceo/iCal/Component.php create mode 100644 vendor/eluceo/ical/src/Eluceo/iCal/Component/Alarm.php create mode 100644 vendor/eluceo/ical/src/Eluceo/iCal/Component/Calendar.php create mode 100644 vendor/eluceo/ical/src/Eluceo/iCal/Component/Event.php create mode 100644 vendor/eluceo/ical/src/Eluceo/iCal/Component/Timezone.php create mode 100644 vendor/eluceo/ical/src/Eluceo/iCal/Component/TimezoneRule.php create mode 100644 vendor/eluceo/ical/src/Eluceo/iCal/ParameterBag.php create mode 100644 vendor/eluceo/ical/src/Eluceo/iCal/Property.php create mode 100644 vendor/eluceo/ical/src/Eluceo/iCal/Property/ArrayValue.php create mode 100644 vendor/eluceo/ical/src/Eluceo/iCal/Property/DateTimeProperty.php create mode 100644 vendor/eluceo/ical/src/Eluceo/iCal/Property/DateTimesProperty.php create mode 100644 vendor/eluceo/ical/src/Eluceo/iCal/Property/Event/Attendees.php create mode 100644 vendor/eluceo/ical/src/Eluceo/iCal/Property/Event/Description.php create mode 100644 vendor/eluceo/ical/src/Eluceo/iCal/Property/Event/Organizer.php create mode 100644 vendor/eluceo/ical/src/Eluceo/iCal/Property/Event/RecurrenceId.php create mode 100644 vendor/eluceo/ical/src/Eluceo/iCal/Property/Event/RecurrenceRule.php create mode 100644 vendor/eluceo/ical/src/Eluceo/iCal/Property/StringValue.php create mode 100644 vendor/eluceo/ical/src/Eluceo/iCal/Property/ValueInterface.php create mode 100644 vendor/eluceo/ical/src/Eluceo/iCal/PropertyBag.php create mode 100644 vendor/eluceo/ical/src/Eluceo/iCal/Util/ComponentUtil.php create mode 100644 vendor/eluceo/ical/src/Eluceo/iCal/Util/DateUtil.php create mode 100644 vendor/eluceo/ical/src/Eluceo/iCal/Util/PropertyValueUtil.php create mode 100644 vendor/eluceo/ical/tests/Eluceo/iCal/Component/CalendarIntegrationTest.php create mode 100644 vendor/eluceo/ical/tests/Eluceo/iCal/ComponentTest.php create mode 100644 vendor/eluceo/ical/tests/Eluceo/iCal/ParameterBagTest.php create mode 100644 vendor/eluceo/ical/tests/Eluceo/iCal/Property/ArrayValueTest.php create mode 100644 vendor/eluceo/ical/tests/Eluceo/iCal/Property/Event/DescriptionTest.php create mode 100644 vendor/eluceo/ical/tests/Eluceo/iCal/Property/Event/OrganizerTest.php create mode 100644 vendor/eluceo/ical/tests/Eluceo/iCal/Property/Event/RecurrenceRuleTest.php create mode 100644 vendor/eluceo/ical/tests/Eluceo/iCal/Property/StringValueTest.php create mode 100644 vendor/eluceo/ical/tests/Eluceo/iCal/PropertyBagTest.php create mode 100644 vendor/eluceo/ical/tests/Eluceo/iCal/PropertyTest.php create mode 100644 vendor/erusev/parsedown/.travis.yml create mode 100644 vendor/erusev/parsedown/LICENSE.txt create mode 100755 vendor/erusev/parsedown/Parsedown.php create mode 100644 vendor/erusev/parsedown/README.md create mode 100644 vendor/erusev/parsedown/composer.json create mode 100644 vendor/erusev/parsedown/phpunit.xml.dist create mode 100644 vendor/erusev/parsedown/test/CommonMarkTest.php create mode 100644 vendor/erusev/parsedown/test/ParsedownTest.php create mode 100644 vendor/erusev/parsedown/test/TestParsedown.php create mode 100644 vendor/erusev/parsedown/test/bootstrap.php create mode 100644 vendor/erusev/parsedown/test/data/aesthetic_table.html create mode 100644 vendor/erusev/parsedown/test/data/aesthetic_table.md create mode 100644 vendor/erusev/parsedown/test/data/aligned_table.html create mode 100644 vendor/erusev/parsedown/test/data/aligned_table.md create mode 100644 vendor/erusev/parsedown/test/data/atx_heading.html create mode 100644 vendor/erusev/parsedown/test/data/atx_heading.md create mode 100644 vendor/erusev/parsedown/test/data/automatic_link.html create mode 100644 vendor/erusev/parsedown/test/data/automatic_link.md create mode 100644 vendor/erusev/parsedown/test/data/block-level_html.html create mode 100644 vendor/erusev/parsedown/test/data/block-level_html.md create mode 100644 vendor/erusev/parsedown/test/data/code_block.html create mode 100644 vendor/erusev/parsedown/test/data/code_block.md create mode 100644 vendor/erusev/parsedown/test/data/code_span.html create mode 100644 vendor/erusev/parsedown/test/data/code_span.md create mode 100644 vendor/erusev/parsedown/test/data/compound_blockquote.html create mode 100644 vendor/erusev/parsedown/test/data/compound_blockquote.md create mode 100644 vendor/erusev/parsedown/test/data/compound_emphasis.html create mode 100644 vendor/erusev/parsedown/test/data/compound_emphasis.md create mode 100644 vendor/erusev/parsedown/test/data/compound_list.html create mode 100644 vendor/erusev/parsedown/test/data/compound_list.md create mode 100644 vendor/erusev/parsedown/test/data/deeply_nested_list.html create mode 100644 vendor/erusev/parsedown/test/data/deeply_nested_list.md create mode 100644 vendor/erusev/parsedown/test/data/em_strong.html create mode 100644 vendor/erusev/parsedown/test/data/em_strong.md create mode 100644 vendor/erusev/parsedown/test/data/email.html create mode 100644 vendor/erusev/parsedown/test/data/email.md create mode 100644 vendor/erusev/parsedown/test/data/emphasis.html create mode 100644 vendor/erusev/parsedown/test/data/emphasis.md create mode 100644 vendor/erusev/parsedown/test/data/escaping.html create mode 100644 vendor/erusev/parsedown/test/data/escaping.md create mode 100644 vendor/erusev/parsedown/test/data/fenced_code_block.html create mode 100644 vendor/erusev/parsedown/test/data/fenced_code_block.md create mode 100644 vendor/erusev/parsedown/test/data/horizontal_rule.html create mode 100644 vendor/erusev/parsedown/test/data/horizontal_rule.md create mode 100644 vendor/erusev/parsedown/test/data/html_comment.html create mode 100644 vendor/erusev/parsedown/test/data/html_comment.md create mode 100644 vendor/erusev/parsedown/test/data/html_entity.html create mode 100644 vendor/erusev/parsedown/test/data/html_entity.md create mode 100644 vendor/erusev/parsedown/test/data/image_reference.html create mode 100644 vendor/erusev/parsedown/test/data/image_reference.md create mode 100644 vendor/erusev/parsedown/test/data/image_title.html create mode 100644 vendor/erusev/parsedown/test/data/image_title.md create mode 100644 vendor/erusev/parsedown/test/data/implicit_reference.html create mode 100644 vendor/erusev/parsedown/test/data/implicit_reference.md create mode 100644 vendor/erusev/parsedown/test/data/inline_link.html create mode 100644 vendor/erusev/parsedown/test/data/inline_link.md create mode 100644 vendor/erusev/parsedown/test/data/inline_link_title.html create mode 100644 vendor/erusev/parsedown/test/data/inline_link_title.md create mode 100644 vendor/erusev/parsedown/test/data/inline_title.html create mode 100644 vendor/erusev/parsedown/test/data/inline_title.md create mode 100644 vendor/erusev/parsedown/test/data/lazy_blockquote.html create mode 100644 vendor/erusev/parsedown/test/data/lazy_blockquote.md create mode 100644 vendor/erusev/parsedown/test/data/lazy_list.html create mode 100644 vendor/erusev/parsedown/test/data/lazy_list.md create mode 100644 vendor/erusev/parsedown/test/data/line_break.html create mode 100644 vendor/erusev/parsedown/test/data/line_break.md create mode 100644 vendor/erusev/parsedown/test/data/multiline_list_paragraph.html create mode 100644 vendor/erusev/parsedown/test/data/multiline_list_paragraph.md create mode 100644 vendor/erusev/parsedown/test/data/nested_block-level_html.html create mode 100644 vendor/erusev/parsedown/test/data/nested_block-level_html.md create mode 100644 vendor/erusev/parsedown/test/data/ordered_list.html create mode 100644 vendor/erusev/parsedown/test/data/ordered_list.md create mode 100644 vendor/erusev/parsedown/test/data/paragraph_list.html create mode 100644 vendor/erusev/parsedown/test/data/paragraph_list.md create mode 100644 vendor/erusev/parsedown/test/data/reference_title.html create mode 100644 vendor/erusev/parsedown/test/data/reference_title.md create mode 100644 vendor/erusev/parsedown/test/data/self-closing_html.html create mode 100644 vendor/erusev/parsedown/test/data/self-closing_html.md create mode 100644 vendor/erusev/parsedown/test/data/separated_nested_list.html create mode 100644 vendor/erusev/parsedown/test/data/separated_nested_list.md create mode 100644 vendor/erusev/parsedown/test/data/setext_header.html create mode 100644 vendor/erusev/parsedown/test/data/setext_header.md create mode 100644 vendor/erusev/parsedown/test/data/simple_blockquote.html create mode 100644 vendor/erusev/parsedown/test/data/simple_blockquote.md create mode 100644 vendor/erusev/parsedown/test/data/simple_table.html create mode 100644 vendor/erusev/parsedown/test/data/simple_table.md create mode 100644 vendor/erusev/parsedown/test/data/span-level_html.html create mode 100644 vendor/erusev/parsedown/test/data/span-level_html.md create mode 100644 vendor/erusev/parsedown/test/data/sparse_dense_list.html create mode 100644 vendor/erusev/parsedown/test/data/sparse_dense_list.md create mode 100644 vendor/erusev/parsedown/test/data/sparse_html.html create mode 100644 vendor/erusev/parsedown/test/data/sparse_html.md create mode 100644 vendor/erusev/parsedown/test/data/sparse_list.html create mode 100644 vendor/erusev/parsedown/test/data/sparse_list.md create mode 100644 vendor/erusev/parsedown/test/data/special_characters.html create mode 100644 vendor/erusev/parsedown/test/data/special_characters.md create mode 100644 vendor/erusev/parsedown/test/data/strikethrough.html create mode 100644 vendor/erusev/parsedown/test/data/strikethrough.md create mode 100644 vendor/erusev/parsedown/test/data/strong_em.html create mode 100644 vendor/erusev/parsedown/test/data/strong_em.md create mode 100644 vendor/erusev/parsedown/test/data/tab-indented_code_block.html create mode 100644 vendor/erusev/parsedown/test/data/tab-indented_code_block.md create mode 100644 vendor/erusev/parsedown/test/data/table_inline_markdown.html create mode 100644 vendor/erusev/parsedown/test/data/table_inline_markdown.md create mode 100644 vendor/erusev/parsedown/test/data/text_reference.html create mode 100644 vendor/erusev/parsedown/test/data/text_reference.md create mode 100644 vendor/erusev/parsedown/test/data/unordered_list.html create mode 100644 vendor/erusev/parsedown/test/data/unordered_list.md create mode 100644 vendor/erusev/parsedown/test/data/untidy_table.html create mode 100644 vendor/erusev/parsedown/test/data/untidy_table.md create mode 100644 vendor/erusev/parsedown/test/data/url_autolinking.html create mode 100644 vendor/erusev/parsedown/test/data/url_autolinking.md create mode 100644 vendor/erusev/parsedown/test/data/whitespace.html create mode 100644 vendor/erusev/parsedown/test/data/whitespace.md create mode 100644 vendor/fguillot/json-rpc/LICENSE create mode 100644 vendor/fguillot/json-rpc/src/JsonRPC/Client.php create mode 100644 vendor/fguillot/json-rpc/src/JsonRPC/Exception/AccessDeniedException.php create mode 100644 vendor/fguillot/json-rpc/src/JsonRPC/Exception/AuthenticationFailureException.php create mode 100644 vendor/fguillot/json-rpc/src/JsonRPC/Exception/ConnectionFailureException.php create mode 100644 vendor/fguillot/json-rpc/src/JsonRPC/Exception/InvalidJsonFormatException.php create mode 100644 vendor/fguillot/json-rpc/src/JsonRPC/Exception/InvalidJsonRpcFormatException.php create mode 100644 vendor/fguillot/json-rpc/src/JsonRPC/Exception/ResponseEncodingFailureException.php create mode 100644 vendor/fguillot/json-rpc/src/JsonRPC/Exception/ResponseException.php create mode 100644 vendor/fguillot/json-rpc/src/JsonRPC/Exception/ServerErrorException.php create mode 100644 vendor/fguillot/json-rpc/src/JsonRPC/HttpClient.php create mode 100644 vendor/fguillot/json-rpc/src/JsonRPC/MiddlewareHandler.php create mode 100644 vendor/fguillot/json-rpc/src/JsonRPC/MiddlewareInterface.php create mode 100644 vendor/fguillot/json-rpc/src/JsonRPC/ProcedureHandler.php create mode 100644 vendor/fguillot/json-rpc/src/JsonRPC/Request/BatchRequestParser.php create mode 100644 vendor/fguillot/json-rpc/src/JsonRPC/Request/RequestBuilder.php create mode 100644 vendor/fguillot/json-rpc/src/JsonRPC/Request/RequestParser.php create mode 100644 vendor/fguillot/json-rpc/src/JsonRPC/Response/ResponseBuilder.php create mode 100644 vendor/fguillot/json-rpc/src/JsonRPC/Response/ResponseParser.php create mode 100644 vendor/fguillot/json-rpc/src/JsonRPC/Server.php create mode 100644 vendor/fguillot/json-rpc/src/JsonRPC/Validator/HostValidator.php create mode 100644 vendor/fguillot/json-rpc/src/JsonRPC/Validator/JsonEncodingValidator.php create mode 100644 vendor/fguillot/json-rpc/src/JsonRPC/Validator/JsonFormatValidator.php create mode 100644 vendor/fguillot/json-rpc/src/JsonRPC/Validator/RpcFormatValidator.php create mode 100644 vendor/fguillot/json-rpc/src/JsonRPC/Validator/UserValidator.php create mode 100644 vendor/fguillot/picodb/LICENSE create mode 100644 vendor/fguillot/picodb/lib/PicoDb/Builder/BaseBuilder.php create mode 100644 vendor/fguillot/picodb/lib/PicoDb/Builder/ConditionBuilder.php create mode 100644 vendor/fguillot/picodb/lib/PicoDb/Builder/InsertBuilder.php create mode 100644 vendor/fguillot/picodb/lib/PicoDb/Builder/OrConditionBuilder.php create mode 100644 vendor/fguillot/picodb/lib/PicoDb/Builder/UpdateBuilder.php create mode 100644 vendor/fguillot/picodb/lib/PicoDb/Database.php create mode 100644 vendor/fguillot/picodb/lib/PicoDb/Driver/Base.php create mode 100644 vendor/fguillot/picodb/lib/PicoDb/Driver/Mssql.php create mode 100644 vendor/fguillot/picodb/lib/PicoDb/Driver/Mysql.php create mode 100644 vendor/fguillot/picodb/lib/PicoDb/Driver/Postgres.php create mode 100644 vendor/fguillot/picodb/lib/PicoDb/Driver/Sqlite.php create mode 100644 vendor/fguillot/picodb/lib/PicoDb/DriverFactory.php create mode 100644 vendor/fguillot/picodb/lib/PicoDb/Hashtable.php create mode 100644 vendor/fguillot/picodb/lib/PicoDb/LargeObject.php create mode 100644 vendor/fguillot/picodb/lib/PicoDb/SQLException.php create mode 100644 vendor/fguillot/picodb/lib/PicoDb/Schema.php create mode 100644 vendor/fguillot/picodb/lib/PicoDb/StatementHandler.php create mode 100644 vendor/fguillot/picodb/lib/PicoDb/Table.php create mode 100644 vendor/fguillot/picodb/lib/PicoDb/UrlParser.php create mode 100644 vendor/fguillot/simple-queue/LICENSE create mode 100644 vendor/fguillot/simple-queue/src/Adapter/AmqpQueueAdapter.php create mode 100644 vendor/fguillot/simple-queue/src/Adapter/AwsSqsQueueAdapter.php create mode 100644 vendor/fguillot/simple-queue/src/Adapter/BeanstalkQueueAdapter.php create mode 100644 vendor/fguillot/simple-queue/src/Adapter/DisqueQueueAdapter.php create mode 100644 vendor/fguillot/simple-queue/src/Adapter/MemoryQueueAdapter.php create mode 100644 vendor/fguillot/simple-queue/src/Exception/NotSupportedException.php create mode 100644 vendor/fguillot/simple-queue/src/Job.php create mode 100644 vendor/fguillot/simple-queue/src/Queue.php create mode 100644 vendor/fguillot/simple-queue/src/QueueAdapterInterface.php create mode 100644 vendor/fguillot/simple-validator/LICENSE create mode 100644 vendor/fguillot/simple-validator/src/SimpleValidator/Validator.php create mode 100644 vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Alpha.php create mode 100644 vendor/fguillot/simple-validator/src/SimpleValidator/Validators/AlphaNumeric.php create mode 100644 vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Base.php create mode 100644 vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Date.php create mode 100644 vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Email.php create mode 100644 vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Equals.php create mode 100644 vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Exists.php create mode 100644 vendor/fguillot/simple-validator/src/SimpleValidator/Validators/GreaterThan.php create mode 100644 vendor/fguillot/simple-validator/src/SimpleValidator/Validators/InArray.php create mode 100644 vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Integer.php create mode 100644 vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Ip.php create mode 100644 vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Length.php create mode 100644 vendor/fguillot/simple-validator/src/SimpleValidator/Validators/MaxLength.php create mode 100644 vendor/fguillot/simple-validator/src/SimpleValidator/Validators/MinLength.php create mode 100644 vendor/fguillot/simple-validator/src/SimpleValidator/Validators/NotEmpty.php create mode 100644 vendor/fguillot/simple-validator/src/SimpleValidator/Validators/NotEquals.php create mode 100644 vendor/fguillot/simple-validator/src/SimpleValidator/Validators/NotInArray.php create mode 100644 vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Numeric.php create mode 100644 vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Range.php create mode 100644 vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Required.php create mode 100644 vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Unique.php create mode 100644 vendor/fguillot/simpleLogger/LICENSE create mode 100644 vendor/fguillot/simpleLogger/src/SimpleLogger/Base.php create mode 100644 vendor/fguillot/simpleLogger/src/SimpleLogger/File.php create mode 100644 vendor/fguillot/simpleLogger/src/SimpleLogger/Logger.php create mode 100644 vendor/fguillot/simpleLogger/src/SimpleLogger/Stderr.php create mode 100644 vendor/fguillot/simpleLogger/src/SimpleLogger/Stdout.php create mode 100644 vendor/fguillot/simpleLogger/src/SimpleLogger/Syslog.php create mode 100644 vendor/gregwar/captcha/.gitignore create mode 100644 vendor/gregwar/captcha/.travis.yml create mode 100644 vendor/gregwar/captcha/CaptchaBuilder.php create mode 100644 vendor/gregwar/captcha/CaptchaBuilderInterface.php create mode 100644 vendor/gregwar/captcha/Font/captcha0.ttf create mode 100644 vendor/gregwar/captcha/Font/captcha1.ttf create mode 100644 vendor/gregwar/captcha/Font/captcha2.ttf create mode 100644 vendor/gregwar/captcha/Font/captcha3.ttf create mode 100644 vendor/gregwar/captcha/Font/captcha4.ttf create mode 100644 vendor/gregwar/captcha/Font/captcha5.ttf create mode 100644 vendor/gregwar/captcha/ImageFileHandler.php create mode 100644 vendor/gregwar/captcha/LICENSE create mode 100644 vendor/gregwar/captcha/PhraseBuilder.php create mode 100644 vendor/gregwar/captcha/PhraseBuilderInterface.php create mode 100644 vendor/gregwar/captcha/README.md create mode 100644 vendor/gregwar/captcha/autoload.php create mode 100644 vendor/gregwar/captcha/composer.json create mode 100644 vendor/gregwar/captcha/demo/demo.php create mode 100644 vendor/gregwar/captcha/demo/fingerprint.php create mode 100644 vendor/gregwar/captcha/demo/index.php create mode 100644 vendor/gregwar/captcha/demo/ocr.php create mode 100644 vendor/gregwar/captcha/demo/output.php create mode 100644 vendor/miniflux/picofeed/LICENSE create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Base.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Client/Client.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Client/ClientException.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Client/Curl.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Client/ForbiddenException.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Client/HttpHeaders.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Client/InvalidCertificateException.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Client/InvalidUrlException.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Client/MaxRedirectException.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Client/MaxSizeException.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Client/Stream.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Client/TimeoutException.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Client/UnauthorizedException.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Client/Url.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Encoding/Encoding.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Filter/Attribute.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Filter/Filter.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Filter/Html.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Filter/Tag.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Generator/ContentGeneratorInterface.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Generator/FileContentGenerator.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Generator/YoutubeContentGenerator.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Logging/Logger.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Parser/Atom.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Parser/DateParser.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Parser/Feed.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Parser/Item.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Parser/MalformedXmlException.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Parser/Parser.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Parser/ParserException.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Parser/ParserInterface.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss10.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss20.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss91.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss92.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Parser/XmlEntityException.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Parser/XmlParser.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/PicoFeedException.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Processor/ContentFilterProcessor.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Processor/ContentGeneratorProcessor.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Processor/ItemPostProcessor.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Processor/ItemProcessorInterface.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Processor/ScraperProcessor.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Reader/Favicon.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Reader/Reader.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Reader/ReaderException.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Reader/SubscriptionNotFoundException.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Reader/UnsupportedFeedFormatException.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/.blog.lemonde.fr.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/.blogs.nytimes.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/.igen.fr.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/.nytimes.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/.over-blog.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/.phoronix.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/.slate.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/.theguardian.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/.wikipedia.org.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/.wired.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/.wsj.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/01net.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/abstrusegoose.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/adventuregamers.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/alainonline.net.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/aljazeera.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/allafrica.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/allgemeine-zeitung.de.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/amazingsuperpowers.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/anythingcomic.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/ap.org.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/areadvd.de.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/arstechnica.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/awkwardzombie.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/backchannel.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/bangkokpost.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/bgr.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/bigfootjustice.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/bigpicture.ru.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/bizjournals.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/biztimes.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/bleepingcomputer.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/blog.fefe.de.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/blog.mapillary.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/brewers.mlb.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/buenosairesherald.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/bunicomic.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/buttersafe.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/cad-comic.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/chaoslife.findchaos.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/chinafile.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/cliquerefresh.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/cnet.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/consomac.fr.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/cowbirdsinlove.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/crash.net.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/csmonitor.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/dailyjs.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/dailyreporter.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/dailytech.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/degroupnews.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/derstandard.at.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/dilbert.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/discovermagazine.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/distrowatch.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/dozodomo.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/drawingboardcomic.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/e-w-e.ru.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/economist.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/encyclopedie.naheulbeuk.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/endlessorigami.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/engadget.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/escapistmagazine.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/espn.go.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/exocomics.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/explosm.net.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/extrafabulouscomics.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/factroom.ru.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/fastcodesign.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/fastcoexist.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/fastcompany.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/ffworld.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/foreignpolicy.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/fossbytes.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/fototelegraf.ru.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/fowllanguagecomics.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/geek.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/geektimes.ru.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/gerbilwithajetpack.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/giantitp.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/github.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/gocomics.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/golem.de.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/gorabbit.ru.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/habrahabr.ru.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/happletea.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/hardware.fr.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/heise.de.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/hotshowlife.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/huffingtonpost.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/imogenquest.net.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/indiehaven.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/ing.dk.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/invisiblebread.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/ir.amd.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/japantimes.co.jp.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/japantoday.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/journaldugeek.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/jsonline.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/justcoolidea.ru.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/kanpai.fr.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/karriere.jobfinder.dk.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/kodi.tv.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/koreaherald.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/koreatimes.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/lastplacecomics.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/legorafi.fr.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/lejapon.fr.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/lesjoiesducode.fr.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/lfg.co.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/lifehacker.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/lifehacker.ru.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/linux.org.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/linux.org.ru.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/linuxinsider.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/lists.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/loadingartist.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/loldwell.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/lukesurl.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/macg.co.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/marc.info.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/marriedtothesea.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/marycagle.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/maximumble.thebookofbiff.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/medium.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/mercworks.net.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/metronieuws.nl.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/milwaukeenns.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/mokepon.smackjeeves.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/monandroid.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/monwindows.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/moya-planeta.ru.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/mrlovenstein.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/muckrock.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/mynorthshorenow.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/nakedCapitalism.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/nasa.gov.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/nat-geo.ru.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/nationaljournal.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/nature.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/nba.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/nedroid.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/networkworld.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/neustadt-ticker.de.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/nextinpact.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/niceteethcomic.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/nichtlustig.de.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/oglaf.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/onhax.net.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/onmilwaukee.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/openculture.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/opennet.ru.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/openrightsgroup.org.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/opensource.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/optipess.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/osnews.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/pastebin.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/peebleslab.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/penny-arcade.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/pixelbeat.org.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/plus.google.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/popstrip.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/publicpolicyforum.org.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/publy.ru.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/putaindecode.fr.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/recode.net.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/retractionwatch.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/rockpapershotgun.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/rue89.nouvelobs.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/rugbyrama.fr.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/satwcomic.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/scrumalliance.org.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/securityfocus.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/sentfromthemoon.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/sitepoint.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/slashdot.org.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/smallhousebliss.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/smarthomewelt.de.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/smashingmagazine.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/smbc-comics.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/snopes.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/soundandvision.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/spiegel.de.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/stereophile.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/stupidfox.net.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/subtraction.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/sz.de.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/takprosto.cc.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/techcrunch.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/the-ebook-reader.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/theatlantic.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/theawkwardyeti.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/thecodinglove.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/thedoghousediaries.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/thegamercat.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/thehindu.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/thelocal.se.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/themerepublic.net.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/themoscowtimes.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/thenewslens.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/theodd1sout.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/theonion.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/thestandard.com.hk.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/threepanelsoul.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/timesofindia.indiatimes.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/travel-dealz.de.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/treehugger.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/treelobsters.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/twogag.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/twokinds.keenspot.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/undeadly.org.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/upi.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/usatoday.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/version2.dk.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/vgcats.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/vuxml.org.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/wausaudailyherald.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.bbc.co.uk.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.bdgest.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.bgr.in.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.businessweek.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.cnn.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.developpez.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.egscomics.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.fakingnews.firstpost.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.forbes.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.franceculture.fr.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.futura-sciences.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.geekculture.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.howtogeek.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.lepoint.fr.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.lesnumeriques.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.mac4ever.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.makeuseof.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.monsieur-le-chien.fr.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.npr.org.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.numerama.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.oneindia.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.pseudo-sciences.org.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.sciencemag.org.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.slate.fr.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.universfreebox.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.zeit.de.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/xkcd.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/ymatuhin.ru.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Rules/zdnet.com.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Scraper/CandidateParser.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Scraper/ParserInterface.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Scraper/RuleLoader.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Scraper/RuleParser.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Scraper/Scraper.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Serialization/Subscription.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionList.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionListBuilder.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionListParser.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionParser.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Syndication/AtomFeedBuilder.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Syndication/AtomHelper.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Syndication/AtomItemBuilder.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Syndication/FeedBuilder.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Syndication/ItemBuilder.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20FeedBuilder.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20Helper.php create mode 100644 vendor/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20ItemBuilder.php create mode 100755 vendor/miniflux/picofeed/picofeed create mode 100644 vendor/paragonie/random_compat/CHANGELOG.md create mode 100644 vendor/paragonie/random_compat/ERRATA.md create mode 100644 vendor/paragonie/random_compat/LICENSE create mode 100644 vendor/paragonie/random_compat/README.md create mode 100644 vendor/paragonie/random_compat/SECURITY.md create mode 100755 vendor/paragonie/random_compat/build-phar.sh create mode 100644 vendor/paragonie/random_compat/composer.json create mode 100644 vendor/paragonie/random_compat/dist/random_compat.phar.pubkey create mode 100644 vendor/paragonie/random_compat/dist/random_compat.phar.pubkey.asc create mode 100644 vendor/paragonie/random_compat/lib/byte_safe_strings.php create mode 100644 vendor/paragonie/random_compat/lib/cast_to_int.php create mode 100644 vendor/paragonie/random_compat/lib/error_polyfill.php create mode 100644 vendor/paragonie/random_compat/lib/random.php create mode 100644 vendor/paragonie/random_compat/lib/random_bytes_com_dotnet.php create mode 100644 vendor/paragonie/random_compat/lib/random_bytes_dev_urandom.php create mode 100644 vendor/paragonie/random_compat/lib/random_bytes_libsodium.php create mode 100644 vendor/paragonie/random_compat/lib/random_bytes_libsodium_legacy.php create mode 100644 vendor/paragonie/random_compat/lib/random_bytes_mcrypt.php create mode 100644 vendor/paragonie/random_compat/lib/random_int.php create mode 100644 vendor/paragonie/random_compat/other/build_phar.php create mode 100644 vendor/pimple/pimple/.gitignore create mode 100644 vendor/pimple/pimple/.travis.yml create mode 100644 vendor/pimple/pimple/CHANGELOG create mode 100644 vendor/pimple/pimple/LICENSE create mode 100644 vendor/pimple/pimple/README.rst create mode 100644 vendor/pimple/pimple/composer.json create mode 100644 vendor/pimple/pimple/ext/pimple/.gitignore create mode 100644 vendor/pimple/pimple/ext/pimple/README.md create mode 100644 vendor/pimple/pimple/ext/pimple/config.m4 create mode 100644 vendor/pimple/pimple/ext/pimple/config.w32 create mode 100644 vendor/pimple/pimple/ext/pimple/php_pimple.h create mode 100644 vendor/pimple/pimple/ext/pimple/pimple.c create mode 100644 vendor/pimple/pimple/ext/pimple/pimple_compat.h create mode 100644 vendor/pimple/pimple/ext/pimple/tests/001.phpt create mode 100644 vendor/pimple/pimple/ext/pimple/tests/002.phpt create mode 100644 vendor/pimple/pimple/ext/pimple/tests/003.phpt create mode 100644 vendor/pimple/pimple/ext/pimple/tests/004.phpt create mode 100644 vendor/pimple/pimple/ext/pimple/tests/005.phpt create mode 100644 vendor/pimple/pimple/ext/pimple/tests/006.phpt create mode 100644 vendor/pimple/pimple/ext/pimple/tests/007.phpt create mode 100644 vendor/pimple/pimple/ext/pimple/tests/008.phpt create mode 100644 vendor/pimple/pimple/ext/pimple/tests/009.phpt create mode 100644 vendor/pimple/pimple/ext/pimple/tests/010.phpt create mode 100644 vendor/pimple/pimple/ext/pimple/tests/011.phpt create mode 100644 vendor/pimple/pimple/ext/pimple/tests/012.phpt create mode 100644 vendor/pimple/pimple/ext/pimple/tests/013.phpt create mode 100644 vendor/pimple/pimple/ext/pimple/tests/014.phpt create mode 100644 vendor/pimple/pimple/ext/pimple/tests/015.phpt create mode 100644 vendor/pimple/pimple/ext/pimple/tests/016.phpt create mode 100644 vendor/pimple/pimple/ext/pimple/tests/017.phpt create mode 100644 vendor/pimple/pimple/ext/pimple/tests/017_1.phpt create mode 100644 vendor/pimple/pimple/ext/pimple/tests/018.phpt create mode 100644 vendor/pimple/pimple/ext/pimple/tests/019.phpt create mode 100644 vendor/pimple/pimple/ext/pimple/tests/bench.phpb create mode 100644 vendor/pimple/pimple/ext/pimple/tests/bench_shared.phpb create mode 100644 vendor/pimple/pimple/phpunit.xml.dist create mode 100644 vendor/pimple/pimple/src/Pimple/Container.php create mode 100644 vendor/pimple/pimple/src/Pimple/ServiceProviderInterface.php create mode 100644 vendor/pimple/pimple/src/Pimple/Tests/Fixtures/Invokable.php create mode 100644 vendor/pimple/pimple/src/Pimple/Tests/Fixtures/NonInvokable.php create mode 100644 vendor/pimple/pimple/src/Pimple/Tests/Fixtures/PimpleServiceProvider.php create mode 100644 vendor/pimple/pimple/src/Pimple/Tests/Fixtures/Service.php create mode 100644 vendor/pimple/pimple/src/Pimple/Tests/PimpleServiceProviderInterfaceTest.php create mode 100644 vendor/pimple/pimple/src/Pimple/Tests/PimpleTest.php create mode 100644 vendor/psr/log/.gitignore create mode 100644 vendor/psr/log/LICENSE create mode 100644 vendor/psr/log/Psr/Log/AbstractLogger.php create mode 100644 vendor/psr/log/Psr/Log/InvalidArgumentException.php create mode 100644 vendor/psr/log/Psr/Log/LogLevel.php create mode 100644 vendor/psr/log/Psr/Log/LoggerAwareInterface.php create mode 100644 vendor/psr/log/Psr/Log/LoggerAwareTrait.php create mode 100644 vendor/psr/log/Psr/Log/LoggerInterface.php create mode 100644 vendor/psr/log/Psr/Log/LoggerTrait.php create mode 100644 vendor/psr/log/Psr/Log/NullLogger.php create mode 100644 vendor/psr/log/Psr/Log/Test/LoggerInterfaceTest.php create mode 100644 vendor/psr/log/README.md create mode 100644 vendor/psr/log/composer.json create mode 100644 vendor/ramsey/array_column/.gitignore create mode 100644 vendor/ramsey/array_column/.travis.yml create mode 100644 vendor/ramsey/array_column/CHANGELOG.md create mode 100644 vendor/ramsey/array_column/LICENSE create mode 100644 vendor/ramsey/array_column/README.md create mode 100644 vendor/ramsey/array_column/composer.json create mode 100644 vendor/ramsey/array_column/phpunit.xml.dist create mode 100644 vendor/ramsey/array_column/src/array_column.php create mode 100644 vendor/ramsey/array_column/tests/ArrayColumnTest.php create mode 100644 vendor/ramsey/array_column/tests/bootstrap.php create mode 100644 vendor/swiftmailer/swiftmailer/.gitattributes create mode 100644 vendor/swiftmailer/swiftmailer/.gitignore create mode 100644 vendor/swiftmailer/swiftmailer/.travis.yml create mode 100644 vendor/swiftmailer/swiftmailer/CHANGES create mode 100644 vendor/swiftmailer/swiftmailer/LICENSE create mode 100644 vendor/swiftmailer/swiftmailer/README create mode 100644 vendor/swiftmailer/swiftmailer/VERSION create mode 100644 vendor/swiftmailer/swiftmailer/composer.json create mode 100644 vendor/swiftmailer/swiftmailer/doc/headers.rst create mode 100644 vendor/swiftmailer/swiftmailer/doc/help-resources.rst create mode 100644 vendor/swiftmailer/swiftmailer/doc/including-the-files.rst create mode 100644 vendor/swiftmailer/swiftmailer/doc/index.rst create mode 100644 vendor/swiftmailer/swiftmailer/doc/installing.rst create mode 100644 vendor/swiftmailer/swiftmailer/doc/introduction.rst create mode 100644 vendor/swiftmailer/swiftmailer/doc/japanese.rst create mode 100644 vendor/swiftmailer/swiftmailer/doc/messages.rst create mode 100644 vendor/swiftmailer/swiftmailer/doc/overview.rst create mode 100644 vendor/swiftmailer/swiftmailer/doc/plugins.rst create mode 100644 vendor/swiftmailer/swiftmailer/doc/sending.rst create mode 100644 vendor/swiftmailer/swiftmailer/doc/uml/Encoders.graffle create mode 100644 vendor/swiftmailer/swiftmailer/doc/uml/Mime.graffle create mode 100644 vendor/swiftmailer/swiftmailer/doc/uml/Transports.graffle create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Attachment.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/AbstractFilterableInputStream.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/ArrayByteStream.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/FileByteStream.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/TemporaryFileByteStream.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/GenericFixedWidthReader.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/UsAsciiReader.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/Utf8Reader.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReaderFactory.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReaderFactory/SimpleCharacterReaderFactory.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream/ArrayCharacterStream.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream/NgCharacterStream.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/ConfigurableSpool.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/DependencyContainer.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/DependencyException.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/EmbeddedFile.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/Base64Encoder.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/QpEncoder.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/Rfc2231Encoder.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoding.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/CommandEvent.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/CommandListener.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/Event.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/EventDispatcher.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/EventListener.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/EventObject.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/ResponseEvent.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/ResponseListener.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/SendEvent.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/SendListener.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/SimpleEventDispatcher.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportChangeEvent.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportChangeListener.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportExceptionEvent.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportExceptionListener.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/FailoverTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/FileSpool.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/FileStream.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Filterable.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Image.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/InputByteStream.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/IoException.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/ArrayKeyCache.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/DiskKeyCache.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/KeyCacheInputStream.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/NullKeyCache.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/SimpleKeyCacheInputStream.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/LoadBalancedTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/MailTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer/ArrayRecipientIterator.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer/RecipientIterator.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/MemorySpool.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Message.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Attachment.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/CharsetObserver.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/Base64ContentEncoder.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/NativeQpContentEncoder.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/PlainContentEncoder.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoder.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoderProxy.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/RawContentEncoder.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/EmbeddedFile.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/EncodingObserver.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Grammar.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Header.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderEncoder.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderEncoder/Base64HeaderEncoder.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderEncoder/QpHeaderEncoder.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderFactory.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderSet.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/AbstractHeader.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/DateHeader.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/IdentificationHeader.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/MailboxHeader.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/OpenDKIMHeader.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/ParameterizedHeader.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/PathHeader.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/UnstructuredHeader.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Message.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/MimeEntity.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/MimePart.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ParameterizedHeader.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleHeaderFactory.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleHeaderSet.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleMessage.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleMimeEntity.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/MimePart.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/NullTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/OutputByteStream.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/AntiFloodPlugin.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/BandwidthMonitorPlugin.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Decorator/Replacements.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/DecoratorPlugin.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/ImpersonatePlugin.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Logger.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/LoggerPlugin.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Loggers/ArrayLogger.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Loggers/EchoLogger.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/MessageLogger.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Pop/Pop3Connection.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Pop/Pop3Exception.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/PopBeforeSmtpPlugin.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/RedirectingPlugin.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporter.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/ReporterPlugin.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporters/HitReporter.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporters/HtmlReporter.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Sleeper.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/ThrottlerPlugin.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Timer.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Preferences.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/ReplacementFilterFactory.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/RfcComplianceException.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/SendmailTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/SignedMessage.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signer.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/BodySigner.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/DKIMSigner.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/DomainKeySigner.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/HeaderSigner.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/OpenDKIMSigner.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/SMimeSigner.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/SmtpTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Spool.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/SpoolTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilter.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/ByteArrayReplacementFilter.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilter.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilterFactory.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/SwiftException.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/AbstractSmtpTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/CramMd5Authenticator.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/LoginAuthenticator.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/NTLMAuthenticator.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/PlainAuthenticator.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/XOAuth2Authenticator.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/AuthHandler.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Authenticator.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/EsmtpHandler.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/EsmtpTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/FailoverTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/IoBuffer.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/LoadBalancedTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/MailInvoker.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/MailTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/NullTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SendmailTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SimpleMailInvoker.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SmtpAgent.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SpoolTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/StreamBuffer.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/TransportException.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Validate.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/dependency_maps/cache_deps.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/dependency_maps/message_deps.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/dependency_maps/mime_deps.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/dependency_maps/transport_deps.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/mime_types.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/preferences.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/swift_init.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/swift_required.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/swift_required_pear.php create mode 100755 vendor/swiftmailer/swiftmailer/lib/swiftmailer_generate_mimes_config.php create mode 100644 vendor/swiftmailer/swiftmailer/phpunit.xml.dist create mode 100644 vendor/swiftmailer/swiftmailer/tests/IdenticalBinaryConstraint.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/StreamCollector.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/SwiftMailerSmokeTestCase.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/SwiftMailerTestCase.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/charsets/iso-2022-jp/one.txt create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/charsets/iso-8859-1/one.txt create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/charsets/utf-8/one.txt create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/charsets/utf-8/three.txt create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/charsets/utf-8/two.txt create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/dkim/dkim.test.priv create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/dkim/dkim.test.pub create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/files/data.txt create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/files/swiftmailer.png create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/files/textfile.zip create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/smime/CA.srl create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/smime/ca.crt create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/smime/ca.key create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/smime/create-cert.sh create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/smime/encrypt.crt create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/smime/encrypt.key create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/smime/encrypt2.crt create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/smime/encrypt2.key create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/smime/intermediate.crt create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/smime/intermediate.key create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/smime/sign.crt create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/smime/sign.key create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/smime/sign2.crt create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/smime/sign2.key create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance.conf.php.default create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/AttachmentAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/ByteStream/FileByteStreamAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/CharacterReaderFactory/SimpleCharacterReaderFactoryAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/DependencyContainerAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/EmbeddedFileAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Encoder/Base64EncoderAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Encoder/QpEncoderAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Encoder/Rfc2231EncoderAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/EncodingAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/KeyCache/ArrayKeyCacheAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/KeyCache/DiskKeyCacheAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/MessageAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/AttachmentAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/Base64ContentEncoderAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/NativeQpContentEncoderAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/PlainContentEncoderAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/QpContentEncoderAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/EmbeddedFileAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/HeaderEncoder/Base64HeaderEncoderAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/MimePartAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/SimpleMessageAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/MimePartAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/AbstractStreamBufferAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/BasicSocketAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/ProcessAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/SocketTimeoutTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/SslSocketAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/TlsSocketAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/bootstrap.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug111Test.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug118Test.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug206Test.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug274Test.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug34Test.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug35Test.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug38Test.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug518Test.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug51Test.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug534Test.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug650Test.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug71Test.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug76Test.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/bug/Swift/BugFileByteStreamConsecutiveReadCallsTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/fixtures/MimeEntityFixture.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/smoke.conf.php.default create mode 100644 vendor/swiftmailer/swiftmailer/tests/smoke/Swift/Smoke/AttachmentSmokeTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/smoke/Swift/Smoke/BasicSmokeTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/smoke/Swift/Smoke/HtmlWithAttachmentSmokeTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/smoke/Swift/Smoke/InternationalSmokeTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/ByteStream/ArrayByteStreamTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterReader/GenericFixedWidthReaderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterReader/UsAsciiReaderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterReader/Utf8ReaderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterStream/ArrayCharacterStreamTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/DependencyContainerTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Encoder/Base64EncoderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Encoder/QpEncoderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Encoder/Rfc2231EncoderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/CommandEventTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/EventObjectTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/ResponseEventTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/SendEventTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/SimpleEventDispatcherTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/TransportChangeEventTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/TransportExceptionEventTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/KeyCache/ArrayKeyCacheTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/KeyCache/SimpleKeyCacheInputStreamTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mailer/ArrayRecipientIteratorTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/MailerTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/MessageTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/AbstractMimeEntityTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/AttachmentTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/ContentEncoder/Base64ContentEncoderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/ContentEncoder/PlainContentEncoderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/ContentEncoder/QpContentEncoderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/EmbeddedFileTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/HeaderEncoder/Base64HeaderEncoderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/HeaderEncoder/QpHeaderEncoderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/DateHeaderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/IdentificationHeaderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/MailboxHeaderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/ParameterizedHeaderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/PathHeaderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/UnstructuredHeaderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/MimePartTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleHeaderFactoryTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleHeaderSetTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleMessageTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleMimeEntityTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/AntiFloodPluginTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/BandwidthMonitorPluginTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/DecoratorPluginTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/LoggerPluginTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Loggers/ArrayLoggerTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Loggers/EchoLoggerTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/PopBeforeSmtpPluginTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/RedirectingPluginTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/ReporterPluginTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Reporters/HitReporterTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Reporters/HtmlReporterTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/ThrottlerPluginTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Signers/DKIMSignerTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Signers/OpenDKIMSignerTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Signers/SMimeSignerTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/StreamFilters/ByteArrayReplacementFilterTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/StreamFilters/StringReplacementFilterFactoryTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/StreamFilters/StringReplacementFilterTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/AbstractSmtpEventSupportTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/AbstractSmtpTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/CramMd5AuthenticatorTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/LoginAuthenticatorTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/NTLMAuthenticatorTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/PlainAuthenticatorTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/AuthHandlerTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/EsmtpTransport/ExtensionSupportTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/EsmtpTransportTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/FailoverTransportTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/LoadBalancedTransportTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/MailTransportTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/SendmailTransportTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/StreamBufferTest.php create mode 100644 vendor/symfony/console/.gitignore create mode 100644 vendor/symfony/console/Application.php create mode 100644 vendor/symfony/console/CHANGELOG.md create mode 100644 vendor/symfony/console/Command/Command.php create mode 100644 vendor/symfony/console/Command/HelpCommand.php create mode 100644 vendor/symfony/console/Command/ListCommand.php create mode 100644 vendor/symfony/console/ConsoleEvents.php create mode 100644 vendor/symfony/console/Descriptor/ApplicationDescription.php create mode 100644 vendor/symfony/console/Descriptor/Descriptor.php create mode 100644 vendor/symfony/console/Descriptor/DescriptorInterface.php create mode 100644 vendor/symfony/console/Descriptor/JsonDescriptor.php create mode 100644 vendor/symfony/console/Descriptor/MarkdownDescriptor.php create mode 100644 vendor/symfony/console/Descriptor/TextDescriptor.php create mode 100644 vendor/symfony/console/Descriptor/XmlDescriptor.php create mode 100644 vendor/symfony/console/Event/ConsoleCommandEvent.php create mode 100644 vendor/symfony/console/Event/ConsoleEvent.php create mode 100644 vendor/symfony/console/Event/ConsoleExceptionEvent.php create mode 100644 vendor/symfony/console/Event/ConsoleTerminateEvent.php create mode 100644 vendor/symfony/console/Exception/CommandNotFoundException.php create mode 100644 vendor/symfony/console/Exception/ExceptionInterface.php create mode 100644 vendor/symfony/console/Exception/InvalidArgumentException.php create mode 100644 vendor/symfony/console/Exception/InvalidOptionException.php create mode 100644 vendor/symfony/console/Exception/LogicException.php create mode 100644 vendor/symfony/console/Exception/RuntimeException.php create mode 100644 vendor/symfony/console/Formatter/OutputFormatter.php create mode 100644 vendor/symfony/console/Formatter/OutputFormatterInterface.php create mode 100644 vendor/symfony/console/Formatter/OutputFormatterStyle.php create mode 100644 vendor/symfony/console/Formatter/OutputFormatterStyleInterface.php create mode 100644 vendor/symfony/console/Formatter/OutputFormatterStyleStack.php create mode 100644 vendor/symfony/console/Helper/DebugFormatterHelper.php create mode 100644 vendor/symfony/console/Helper/DescriptorHelper.php create mode 100644 vendor/symfony/console/Helper/DialogHelper.php create mode 100644 vendor/symfony/console/Helper/FormatterHelper.php create mode 100644 vendor/symfony/console/Helper/Helper.php create mode 100644 vendor/symfony/console/Helper/HelperInterface.php create mode 100644 vendor/symfony/console/Helper/HelperSet.php create mode 100644 vendor/symfony/console/Helper/InputAwareHelper.php create mode 100644 vendor/symfony/console/Helper/ProcessHelper.php create mode 100644 vendor/symfony/console/Helper/ProgressBar.php create mode 100644 vendor/symfony/console/Helper/ProgressHelper.php create mode 100644 vendor/symfony/console/Helper/ProgressIndicator.php create mode 100644 vendor/symfony/console/Helper/QuestionHelper.php create mode 100644 vendor/symfony/console/Helper/SymfonyQuestionHelper.php create mode 100644 vendor/symfony/console/Helper/Table.php create mode 100644 vendor/symfony/console/Helper/TableCell.php create mode 100644 vendor/symfony/console/Helper/TableHelper.php create mode 100644 vendor/symfony/console/Helper/TableSeparator.php create mode 100644 vendor/symfony/console/Helper/TableStyle.php create mode 100644 vendor/symfony/console/Input/ArgvInput.php create mode 100644 vendor/symfony/console/Input/ArrayInput.php create mode 100644 vendor/symfony/console/Input/Input.php create mode 100644 vendor/symfony/console/Input/InputArgument.php create mode 100644 vendor/symfony/console/Input/InputAwareInterface.php create mode 100644 vendor/symfony/console/Input/InputDefinition.php create mode 100644 vendor/symfony/console/Input/InputInterface.php create mode 100644 vendor/symfony/console/Input/InputOption.php create mode 100644 vendor/symfony/console/Input/StringInput.php create mode 100644 vendor/symfony/console/LICENSE create mode 100644 vendor/symfony/console/Logger/ConsoleLogger.php create mode 100644 vendor/symfony/console/Output/BufferedOutput.php create mode 100644 vendor/symfony/console/Output/ConsoleOutput.php create mode 100644 vendor/symfony/console/Output/ConsoleOutputInterface.php create mode 100644 vendor/symfony/console/Output/NullOutput.php create mode 100644 vendor/symfony/console/Output/Output.php create mode 100644 vendor/symfony/console/Output/OutputInterface.php create mode 100644 vendor/symfony/console/Output/StreamOutput.php create mode 100644 vendor/symfony/console/Question/ChoiceQuestion.php create mode 100644 vendor/symfony/console/Question/ConfirmationQuestion.php create mode 100644 vendor/symfony/console/Question/Question.php create mode 100644 vendor/symfony/console/README.md create mode 100644 vendor/symfony/console/Resources/bin/hiddeninput.exe create mode 100644 vendor/symfony/console/Shell.php create mode 100644 vendor/symfony/console/Style/OutputStyle.php create mode 100644 vendor/symfony/console/Style/StyleInterface.php create mode 100644 vendor/symfony/console/Style/SymfonyStyle.php create mode 100644 vendor/symfony/console/Tester/ApplicationTester.php create mode 100644 vendor/symfony/console/Tester/CommandTester.php create mode 100644 vendor/symfony/console/Tests/ApplicationTest.php create mode 100644 vendor/symfony/console/Tests/Command/CommandTest.php create mode 100644 vendor/symfony/console/Tests/Command/HelpCommandTest.php create mode 100644 vendor/symfony/console/Tests/Command/ListCommandTest.php create mode 100644 vendor/symfony/console/Tests/Descriptor/AbstractDescriptorTest.php create mode 100644 vendor/symfony/console/Tests/Descriptor/JsonDescriptorTest.php create mode 100644 vendor/symfony/console/Tests/Descriptor/MarkdownDescriptorTest.php create mode 100644 vendor/symfony/console/Tests/Descriptor/ObjectsProvider.php create mode 100644 vendor/symfony/console/Tests/Descriptor/TextDescriptorTest.php create mode 100644 vendor/symfony/console/Tests/Descriptor/XmlDescriptorTest.php create mode 100644 vendor/symfony/console/Tests/Fixtures/BarBucCommand.php create mode 100644 vendor/symfony/console/Tests/Fixtures/DescriptorApplication1.php create mode 100644 vendor/symfony/console/Tests/Fixtures/DescriptorApplication2.php create mode 100644 vendor/symfony/console/Tests/Fixtures/DescriptorCommand1.php create mode 100644 vendor/symfony/console/Tests/Fixtures/DescriptorCommand2.php create mode 100644 vendor/symfony/console/Tests/Fixtures/DummyOutput.php create mode 100644 vendor/symfony/console/Tests/Fixtures/Foo1Command.php create mode 100644 vendor/symfony/console/Tests/Fixtures/Foo2Command.php create mode 100644 vendor/symfony/console/Tests/Fixtures/Foo3Command.php create mode 100644 vendor/symfony/console/Tests/Fixtures/Foo4Command.php create mode 100644 vendor/symfony/console/Tests/Fixtures/Foo5Command.php create mode 100644 vendor/symfony/console/Tests/Fixtures/Foo6Command.php create mode 100644 vendor/symfony/console/Tests/Fixtures/FooCommand.php create mode 100644 vendor/symfony/console/Tests/Fixtures/FooSubnamespaced1Command.php create mode 100644 vendor/symfony/console/Tests/Fixtures/FooSubnamespaced2Command.php create mode 100644 vendor/symfony/console/Tests/Fixtures/FoobarCommand.php create mode 100644 vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_0.php create mode 100644 vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_1.php create mode 100644 vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_10.php create mode 100644 vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_11.php create mode 100644 vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_2.php create mode 100644 vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_3.php create mode 100644 vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_4.php create mode 100644 vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_5.php create mode 100644 vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_6.php create mode 100644 vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_7.php create mode 100644 vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_8.php create mode 100644 vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_9.php create mode 100644 vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_0.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_1.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_10.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_11.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_2.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_3.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_4.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_5.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_6.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_7.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_8.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_9.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/TestCommand.php create mode 100644 vendor/symfony/console/Tests/Fixtures/application_1.json create mode 100644 vendor/symfony/console/Tests/Fixtures/application_1.md create mode 100644 vendor/symfony/console/Tests/Fixtures/application_1.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/application_1.xml create mode 100644 vendor/symfony/console/Tests/Fixtures/application_2.json create mode 100644 vendor/symfony/console/Tests/Fixtures/application_2.md create mode 100644 vendor/symfony/console/Tests/Fixtures/application_2.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/application_2.xml create mode 100644 vendor/symfony/console/Tests/Fixtures/application_astext1.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/application_astext2.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/application_asxml1.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/application_asxml2.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/application_gethelp.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/application_renderexception1.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/application_renderexception2.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/application_renderexception3.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/application_renderexception3decorated.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/application_renderexception4.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth1.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth1decorated.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth2.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/application_run1.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/application_run2.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/application_run3.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/application_run4.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/command_1.json create mode 100644 vendor/symfony/console/Tests/Fixtures/command_1.md create mode 100644 vendor/symfony/console/Tests/Fixtures/command_1.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/command_1.xml create mode 100644 vendor/symfony/console/Tests/Fixtures/command_2.json create mode 100644 vendor/symfony/console/Tests/Fixtures/command_2.md create mode 100644 vendor/symfony/console/Tests/Fixtures/command_2.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/command_2.xml create mode 100644 vendor/symfony/console/Tests/Fixtures/command_astext.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/command_asxml.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/definition_astext.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/definition_asxml.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/input_argument_1.json create mode 100644 vendor/symfony/console/Tests/Fixtures/input_argument_1.md create mode 100644 vendor/symfony/console/Tests/Fixtures/input_argument_1.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/input_argument_1.xml create mode 100644 vendor/symfony/console/Tests/Fixtures/input_argument_2.json create mode 100644 vendor/symfony/console/Tests/Fixtures/input_argument_2.md create mode 100644 vendor/symfony/console/Tests/Fixtures/input_argument_2.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/input_argument_2.xml create mode 100644 vendor/symfony/console/Tests/Fixtures/input_argument_3.json create mode 100644 vendor/symfony/console/Tests/Fixtures/input_argument_3.md create mode 100644 vendor/symfony/console/Tests/Fixtures/input_argument_3.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/input_argument_3.xml create mode 100644 vendor/symfony/console/Tests/Fixtures/input_argument_4.json create mode 100644 vendor/symfony/console/Tests/Fixtures/input_argument_4.md create mode 100644 vendor/symfony/console/Tests/Fixtures/input_argument_4.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/input_argument_4.xml create mode 100644 vendor/symfony/console/Tests/Fixtures/input_definition_1.json create mode 100644 vendor/symfony/console/Tests/Fixtures/input_definition_1.md create mode 100644 vendor/symfony/console/Tests/Fixtures/input_definition_1.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/input_definition_1.xml create mode 100644 vendor/symfony/console/Tests/Fixtures/input_definition_2.json create mode 100644 vendor/symfony/console/Tests/Fixtures/input_definition_2.md create mode 100644 vendor/symfony/console/Tests/Fixtures/input_definition_2.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/input_definition_2.xml create mode 100644 vendor/symfony/console/Tests/Fixtures/input_definition_3.json create mode 100644 vendor/symfony/console/Tests/Fixtures/input_definition_3.md create mode 100644 vendor/symfony/console/Tests/Fixtures/input_definition_3.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/input_definition_3.xml create mode 100644 vendor/symfony/console/Tests/Fixtures/input_definition_4.json create mode 100644 vendor/symfony/console/Tests/Fixtures/input_definition_4.md create mode 100644 vendor/symfony/console/Tests/Fixtures/input_definition_4.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/input_definition_4.xml create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_1.json create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_1.md create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_1.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_1.xml create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_2.json create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_2.md create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_2.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_2.xml create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_3.json create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_3.md create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_3.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_3.xml create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_4.json create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_4.md create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_4.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_4.xml create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_5.json create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_5.md create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_5.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_5.xml create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_6.json create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_6.md create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_6.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_6.xml create mode 100644 vendor/symfony/console/Tests/Formatter/OutputFormatterStyleStackTest.php create mode 100644 vendor/symfony/console/Tests/Formatter/OutputFormatterStyleTest.php create mode 100644 vendor/symfony/console/Tests/Formatter/OutputFormatterTest.php create mode 100644 vendor/symfony/console/Tests/Helper/FormatterHelperTest.php create mode 100644 vendor/symfony/console/Tests/Helper/HelperSetTest.php create mode 100644 vendor/symfony/console/Tests/Helper/HelperTest.php create mode 100644 vendor/symfony/console/Tests/Helper/LegacyDialogHelperTest.php create mode 100644 vendor/symfony/console/Tests/Helper/LegacyProgressHelperTest.php create mode 100644 vendor/symfony/console/Tests/Helper/LegacyTableHelperTest.php create mode 100644 vendor/symfony/console/Tests/Helper/ProcessHelperTest.php create mode 100644 vendor/symfony/console/Tests/Helper/ProgressBarTest.php create mode 100644 vendor/symfony/console/Tests/Helper/ProgressIndicatorTest.php create mode 100644 vendor/symfony/console/Tests/Helper/QuestionHelperTest.php create mode 100644 vendor/symfony/console/Tests/Helper/TableStyleTest.php create mode 100644 vendor/symfony/console/Tests/Helper/TableTest.php create mode 100644 vendor/symfony/console/Tests/Input/ArgvInputTest.php create mode 100644 vendor/symfony/console/Tests/Input/ArrayInputTest.php create mode 100644 vendor/symfony/console/Tests/Input/InputArgumentTest.php create mode 100644 vendor/symfony/console/Tests/Input/InputDefinitionTest.php create mode 100644 vendor/symfony/console/Tests/Input/InputOptionTest.php create mode 100644 vendor/symfony/console/Tests/Input/InputTest.php create mode 100644 vendor/symfony/console/Tests/Input/StringInputTest.php create mode 100644 vendor/symfony/console/Tests/Logger/ConsoleLoggerTest.php create mode 100644 vendor/symfony/console/Tests/Output/ConsoleOutputTest.php create mode 100644 vendor/symfony/console/Tests/Output/NullOutputTest.php create mode 100644 vendor/symfony/console/Tests/Output/OutputTest.php create mode 100644 vendor/symfony/console/Tests/Output/StreamOutputTest.php create mode 100644 vendor/symfony/console/Tests/Style/SymfonyStyleTest.php create mode 100644 vendor/symfony/console/Tests/Tester/ApplicationTesterTest.php create mode 100644 vendor/symfony/console/Tests/Tester/CommandTesterTest.php create mode 100644 vendor/symfony/console/composer.json create mode 100644 vendor/symfony/console/phpunit.xml.dist create mode 100644 vendor/symfony/event-dispatcher/.gitignore create mode 100644 vendor/symfony/event-dispatcher/CHANGELOG.md create mode 100644 vendor/symfony/event-dispatcher/ContainerAwareEventDispatcher.php create mode 100644 vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php create mode 100644 vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php create mode 100644 vendor/symfony/event-dispatcher/Debug/WrappedListener.php create mode 100644 vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php create mode 100644 vendor/symfony/event-dispatcher/Event.php create mode 100644 vendor/symfony/event-dispatcher/EventDispatcher.php create mode 100644 vendor/symfony/event-dispatcher/EventDispatcherInterface.php create mode 100644 vendor/symfony/event-dispatcher/EventSubscriberInterface.php create mode 100644 vendor/symfony/event-dispatcher/GenericEvent.php create mode 100644 vendor/symfony/event-dispatcher/ImmutableEventDispatcher.php create mode 100644 vendor/symfony/event-dispatcher/LICENSE create mode 100644 vendor/symfony/event-dispatcher/README.md create mode 100644 vendor/symfony/event-dispatcher/Tests/AbstractEventDispatcherTest.php create mode 100644 vendor/symfony/event-dispatcher/Tests/ContainerAwareEventDispatcherTest.php create mode 100644 vendor/symfony/event-dispatcher/Tests/Debug/TraceableEventDispatcherTest.php create mode 100644 vendor/symfony/event-dispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php create mode 100644 vendor/symfony/event-dispatcher/Tests/EventDispatcherTest.php create mode 100644 vendor/symfony/event-dispatcher/Tests/EventTest.php create mode 100644 vendor/symfony/event-dispatcher/Tests/GenericEventTest.php create mode 100644 vendor/symfony/event-dispatcher/Tests/ImmutableEventDispatcherTest.php create mode 100644 vendor/symfony/event-dispatcher/composer.json create mode 100644 vendor/symfony/event-dispatcher/phpunit.xml.dist create mode 100644 vendor/symfony/polyfill-mbstring/LICENSE create mode 100644 vendor/symfony/polyfill-mbstring/Mbstring.php create mode 100644 vendor/symfony/polyfill-mbstring/README.md create mode 100644 vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php create mode 100644 vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.php create mode 100644 vendor/symfony/polyfill-mbstring/bootstrap.php create mode 100644 vendor/symfony/polyfill-mbstring/composer.json create mode 100644 vendor/zendframework/zendxml/.gitignore create mode 100644 vendor/zendframework/zendxml/.travis.yml create mode 100644 vendor/zendframework/zendxml/CHANGELOG.md create mode 100644 vendor/zendframework/zendxml/LICENSE.md create mode 100644 vendor/zendframework/zendxml/README.md create mode 100644 vendor/zendframework/zendxml/composer.json create mode 100644 vendor/zendframework/zendxml/library/ZendXml/Exception/ExceptionInterface.php create mode 100644 vendor/zendframework/zendxml/library/ZendXml/Exception/InvalidArgumentException.php create mode 100644 vendor/zendframework/zendxml/library/ZendXml/Exception/RuntimeException.php create mode 100644 vendor/zendframework/zendxml/library/ZendXml/Security.php create mode 100644 vendor/zendframework/zendxml/tests/Bootstrap.php create mode 100644 vendor/zendframework/zendxml/tests/ZendXmlTest/MultibyteTest.php create mode 100644 vendor/zendframework/zendxml/tests/ZendXmlTest/SecurityTest.php create mode 100755 vendor/zendframework/zendxml/tests/phpunit.xml.dist diff --git a/.gitattributes b/.gitattributes index 92a0d686a8..5ca66a7116 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,15 +1,30 @@ app/constants.php export-subst +.github export-ignore .gitattributes export-ignore .gitignore export-ignore -.docker export-ignore -.scrutinizer.yml export-ignore +.dockerignore export-ignore .travis.yml export-ignore + +docker export-ignore Dockerfile export-ignore docker-compose.yml export-ignore Makefile export-ignore README.md export-ignore Vagrantfile export-ignore -issue_template.md export-ignore data/*.sqlite export-ignore tests export-ignore +CONTRIBUTING export-ignore +app.json export-ignore +bower.json export-ignore +composer.json export-ignore +composer.lock export-ignore +gulpfile.js export-ignore +package.json export-ignore + +assets/sass export-ignore +assets/js/components export-ignore +assets/js/core export-ignore +assets/js/polyfills export-ignore +assets/js/src export-ignore +assets/sass export-ignore diff --git a/.gitignore b/.gitignore index f6dc2fd38a..d4975f31db 100644 --- a/.gitignore +++ b/.gitignore @@ -18,7 +18,6 @@ Thumbs.db config.php data/files data/cache -/vendor *.bak !docker/kanboard/config.php node_modules diff --git a/.travis.yml b/.travis.yml index 0f0a2e5bbd..a2f8e6000c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,7 +21,7 @@ matrix: before_script: - if [[ $TRAVIS_PHP_VERSION != 7.x ]]; then phpenv config-rm xdebug.ini; fi - phpenv config-add tests/php.ini - - composer install + - composer install --dev - npm install script: diff --git a/Dockerfile b/Dockerfile index 99e940bb3a..e1d4d52ac7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,7 +6,6 @@ COPY docker/crontab/cronjob.alpine /var/spool/cron/crontabs/nginx COPY docker/services.d/cron /etc/services.d/cron COPY docker/php/env.conf /etc/php7/php-fpm.d/env.conf -RUN cd /var/www/app && composer --prefer-dist --no-dev --optimize-autoloader --quiet install RUN chown -R nginx:nginx /var/www/app/data /var/www/app/plugins VOLUME /var/www/app/data diff --git a/Makefile b/Makefile index 985aded77d..7851f1e504 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ archive: @ echo "Build archive: version=${version}, destination=${dst}" @ rm -rf ${BUILD_DIR}/kanboard ${BUILD_DIR}/kanboard-*.zip @ cd ${BUILD_DIR} && git clone --depth 1 -q https://github.com/kanboard/kanboard.git - @ cd ${BUILD_DIR}/kanboard && composer --prefer-dist --no-dev --optimize-autoloader --quiet install + @ cd ${BUILD_DIR}/kanboard @ rm -rf ${BUILD_DIR}/kanboard/data/*.sqlite @ rm -rf ${BUILD_DIR}/kanboard/data/*.log @ rm -rf ${BUILD_DIR}/kanboard/data/files diff --git a/doc/en_US/installation.markdown b/doc/en_US/installation.markdown index 8b8add9902..543ca4d0bf 100644 --- a/doc/en_US/installation.markdown +++ b/doc/en_US/installation.markdown @@ -26,11 +26,8 @@ People who are using a remote database (Mysql/Postgresql) and a remote object st From the git repository (development version) --------------------------------------------- -You must install [composer](https://getcomposer.org/) to use this method. - 1. `git clone https://github.com/kanboard/kanboard.git` -2. `composer install --no-dev` -3. Go to the third step just above +2. Go to the third step just above Note: This method will install the **current development version**, use at your own risk. diff --git a/doc/en_US/update.markdown b/doc/en_US/update.markdown index 76cac27adf..516b704b0b 100644 --- a/doc/en_US/update.markdown +++ b/doc/en_US/update.markdown @@ -30,8 +30,7 @@ From the repository (development version) ----------------------------------------- 1. `git pull` -2. `composer install --no-dev` -3. Login and check if everything is ok +2. Login and check if everything is ok Note: This method will install the **current development version**, use at your own risk. diff --git a/doc/es_ES/installation.markdown b/doc/es_ES/installation.markdown index f3dfc495ed..308b0e2b4f 100644 --- a/doc/es_ES/installation.markdown +++ b/doc/es_ES/installation.markdown @@ -27,12 +27,8 @@ Las personas que están utilizando una base de datos remota (MySQL / PostgreSQL) Desde el repositorio (versión de desarrollo) ----------------------------------------- - -Debe instalar [compositora] (https://getcomposer.org/)para utilizar este método. - 1. ` git clone https: // github.com / kanboard / kanboard.git` -2. ` composer instalar --no- dev` -3. Ir a la tercera etapa justo por encima +2. Ir a la tercera etapa justo por encima Nota: Este método se instalará la versión de **desarrollo actual** , utilice a su propio riesgo . diff --git a/doc/es_ES/update.markdown b/doc/es_ES/update.markdown index 06789d74e7..ae5d7a5ebb 100755 --- a/doc/es_ES/update.markdown +++ b/doc/es_ES/update.markdown @@ -28,7 +28,6 @@ Desde el repositorio (development version) ----------------------------------------- 1. `git pull` -2. `composer install --no-dev` -3. Inicia la sesión y comprobar si todo está bien +2. Inicia la sesión y comprobar si todo está bien Nota: Este método se instalará la **versión de desarrollo actual**, utilice a su propio riesgo. diff --git a/doc/fr_FR/installation.markdown b/doc/fr_FR/installation.markdown index 9e4cb47a3a..846261516a 100644 --- a/doc/fr_FR/installation.markdown +++ b/doc/fr_FR/installation.markdown @@ -26,11 +26,8 @@ Les gens qui utilisent une base de données distante (Mysql/Postgresql) ou un sy Depuis le dépôt git (version de développement) ---------------------------------------------- -Vous devez installer [composer](https://getcomposer.org/) pour utiliser cette méthode. - 1. `git clone https://github.com/kanboard/kanboard.git` -2. `composer install --no-dev` -3. Allez à l'étape 3) juste au-dessus +2. Allez à l'étape 3) juste au-dessus Cette méthode va installer **la version en cours de développement**, utilisez là à vos risques. diff --git a/doc/fr_FR/update.markdown b/doc/fr_FR/update.markdown index 68468e5042..ed248b58dd 100644 --- a/doc/fr_FR/update.markdown +++ b/doc/fr_FR/update.markdown @@ -30,8 +30,7 @@ Depuis le dépôt git (version de développement) --------------------------------------------- 1. `git pull` -2. `composer install --no-dev` -3. Testez que tout fonctionne correctement +2. Testez que tout fonctionne correctement Cette méthode va installer **la version en cours de développement**, utilisez là à vos risques. diff --git a/doc/ru_RU/installation.markdown b/doc/ru_RU/installation.markdown index ff989b1947..5bc4f66cd4 100644 --- a/doc/ru_RU/installation.markdown +++ b/doc/ru_RU/installation.markdown @@ -47,16 +47,8 @@ Инсталяция из репозитория (разрабатываемая версия)[¶](#from-the-repository-development-version "Ссылка на этот заголовок") -------------------------------------------------------------------------------------------------------------------------- - - -Вы можете установить [composer](https://getcomposer.org/) для этого метода инсталяции. - - 1. `git clone https://github.com/fguillot/kanboard.git` - -2. `composer install --no-dev` - -3. Далее, перейдите к третьему шагу [Инсталяция из архива](installation.html#from-the-archive-stable-version) +2. Далее, перейдите к третьему шагу [Инсталяция из архива](installation.html#from-the-archive-stable-version) diff --git a/doc/ru_RU/update.markdown b/doc/ru_RU/update.markdown index 7cfabdb089..84483f7b03 100644 --- a/doc/ru_RU/update.markdown +++ b/doc/ru_RU/update.markdown @@ -42,10 +42,7 @@ 1. `git pull` - -2. `composer install --no-dev` - -3. Выполните вход и проверьте, что все работает корректно +2. Выполните вход и проверьте, что все работает корректно **Внимание**: Выполняя обновление из разрабатываемой версии, вы должны понимать, что это нестабильная версия и берете все риски по работе Канборд на себя. diff --git a/doc/tr_TR/installation.markdown b/doc/tr_TR/installation.markdown index 23093e5630..c46c379db1 100644 --- a/doc/tr_TR/installation.markdown +++ b/doc/tr_TR/installation.markdown @@ -26,11 +26,8 @@ Uzak bir veritabanı (Mysql/Postgresql) ve uzak nesne depolama birimi (Aws S3 ve Git deposundan (geliştirme versiyonu) --------------------------------------------- -Bu yöntemi kullanmak için [composer](https://getcomposer.org/) yüklemelisiniz. - 1. `git clone https://github.com/kanboard/kanboard.git` -2. `composer install --no-dev` -3. Yukarıdaki üçüncü adıma geçin +2. Yukarıdaki üçüncü adıma geçin Not: Bu yöntem, **mevcut geliştirme sürümünü** yükleyecektir, kendi sorumluluğunuzdadır. diff --git a/doc/tr_TR/update.markdown b/doc/tr_TR/update.markdown index 842b1b5acb..1ca0a6548e 100644 --- a/doc/tr_TR/update.markdown +++ b/doc/tr_TR/update.markdown @@ -30,8 +30,7 @@ Depodan-repository (geliştirme versiyonu) ----------------------------------------- 1. `git pull` -2. `composer install --no-dev` -3. Giriş yapın ve her şeyin yolunda olduğunu kontrol edin +2. Giriş yapın ve her şeyin yolunda olduğunu kontrol edin Not: Bu yöntem, **mevcut geliştirme sürümünü** yükleyecektir, bu versiyonu kullanmanız kendi sorumluluğunuzdadır. diff --git a/vendor/aferrandini/phpqrcode/.gitignore b/vendor/aferrandini/phpqrcode/.gitignore new file mode 100644 index 0000000000..485dee64bc --- /dev/null +++ b/vendor/aferrandini/phpqrcode/.gitignore @@ -0,0 +1 @@ +.idea diff --git a/vendor/aferrandini/phpqrcode/LICENSE b/vendor/aferrandini/phpqrcode/LICENSE new file mode 100755 index 0000000000..1883303261 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/vendor/aferrandini/phpqrcode/VERSION b/vendor/aferrandini/phpqrcode/VERSION new file mode 100755 index 0000000000..e0d3a2ee70 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/VERSION @@ -0,0 +1,2 @@ +1.1.5 +2012021604 diff --git a/vendor/aferrandini/phpqrcode/cache/frame_1.dat b/vendor/aferrandini/phpqrcode/cache/frame_1.dat new file mode 100755 index 0000000000..be28feac47 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_1.dat @@ -0,0 +1,2 @@ +xڝ E9u`"PńC牗T!0$ +EɲQmh۾9{kI" 9Ln)Ap־>^zmnŖ;mn \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/frame_1.png b/vendor/aferrandini/phpqrcode/cache/frame_1.png new file mode 100755 index 0000000000000000000000000000000000000000..86ce6e98d8cf7071291d5e5bb39bc3080511c8b8 GIT binary patch literal 126 zcmeAS@N?(olHy`uVBq!ia0vp^q9Dx33?wJtG`|X@*aCb)T!Hle|NocXoPQU{GWK+F z45^rtobc!Hfq>uNnVVn7Gg`2HlysJm`1k++!4KjchA)1}^Yl3E66=VaXwbng!p`vB WU-#~W{*xdZ7(8A5T-G@yGywobm@6p& literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/frame_10.dat b/vendor/aferrandini/phpqrcode/cache/frame_10.dat new file mode 100755 index 0000000000000000000000000000000000000000..aff163f6882e1c842881c539160539ee97221e5b GIT binary patch literal 204 zcmV;-05ku1+U=K34ul{KMqP93Nm#TyxdGe+ZZ`bX2|6GY6RiCuCX1^|Hy)_wA3>UwXVkes{2iDcbZs(b-_ G?>v^)7G+HU literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/frame_10.png b/vendor/aferrandini/phpqrcode/cache/frame_10.png new file mode 100755 index 0000000000000000000000000000000000000000..dbfcd70b588c9cc508d210d761ca285c962e70c7 GIT binary patch literal 202 zcmeAS@N?(olHy`uVBq!ia0vp^mLSZ?3?#Q*6Fdf_*aCb)T!Hle|NocXoPQU{s`qqp z45^rNcH&0P1_Kcn{i)eEQ&eWabo5TEo z<(=q;l{2~SNFHOk7+K+Z++>o}0+W;js(lQ_L5J;4XHRbpJ@7EKSbjtGxs>0TanIFf yTerS?Jf(G)Wt_o(rC4{V)qPjP^(|O~A28=0dj4ToPRUb}EH+ z?=;|LU8z+!qM$&p(|BcWyT^0wyzYLMW@>oYiN^Dsz8hTJ4a-aiVeGGljlcQ?PgyF*WC@)ePT+&@l!`0?ief1~n6 zE|yl^gW5e`nKwIinOvP@sie!fPIxQBN@sPwvjzdvtR(AR7bMPLqt$5hLLY|jw(O$c@esqcjU~XWxX2%DQ}j2 Ypk>~2FWzvVWdEB3>HkBaKPQDn?w1{Br2qf` literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/frame_12.png b/vendor/aferrandini/phpqrcode/cache/frame_12.png new file mode 100755 index 0000000000000000000000000000000000000000..8ba67822cdbeb547858ff6fc50cacee24a1b7d39 GIT binary patch literal 216 zcmeAS@N?(olHy`uVBq!ia0vp^jv&m)3?#Q$MzaAawg8_HS0MfW|No^o=iddgdOckn zLn`JZC;Z`O`p;)zaHT2o|6@ssn1en08x5|_?@qWT|MUNU=7X>P-D6i`V>=dLP>_)D zgJ10T|NV^*^<88qe3j=qkQ8w3g4pi>{%_LN%!j{MZKyvjEwPU&^5g&i?aJMXS9tf# z))chnOn-VfGWh@Dzii+24R{?7GfXmbNLp7=x~^a^N0{!Mqa4YM488*Y@7T!EMuc4o**_dv29=tg%zuR9}#CwLe ZF7lm6+oM%3(p}KSMZA}4^aZ5yN}69FYBT@< literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/frame_13.png b/vendor/aferrandini/phpqrcode/cache/frame_13.png new file mode 100755 index 0000000000000000000000000000000000000000..6e49d35a02fdca84cbce7cbdcc4455a8f6c7a3bc GIT binary patch literal 210 zcmeAS@N?(olHy`uVBq!ia0vp^t{}|F3?$W@cV+=8wg8_Hkl_FS|CipJe;3GV_jGX# zshD$j(m~D!10L6U|G3>D>;fX{1#EjgrWdpJzq`==!Zpb!r-Z@r!+bgW{TFJO+!qN- zO!?Nbz~r}2caWn=<;hYT--4|xm^^H6ZOHEH)LZFp|21ezZE(M-7khSp>&l1M=G~k7 z```Ke>ZO4uStq~s?ri>iZOio6Z8x~5aew=^?BI!1D~3ap?Te2HrSt=x#Ng@b=d#Wz Gp$P!f)Kr=P literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/frame_14.dat b/vendor/aferrandini/phpqrcode/cache/frame_14.dat new file mode 100755 index 0000000000000000000000000000000000000000..e9ae093296aa29fab5e9902b6f08c589fb0ada98 GIT binary patch literal 227 zcmV<90382#+U=M@3dA4`MZ5Oa11^$Xy@B3DZ#HVvL77flO`r_qyJ{$pN0cm*QcbkW zOb{?`fMilEnI}OcLM9cXSrSxIB``^d*Q8K#Sp&p|blj)ly$_eQu1|D#ZtYL-`P`_v z&)r4JoKwyYFt*0oxjyAsirGHbg&iU}F_Jo|lREvc)7@EgS~DZ_@w7&yaPY@qP1;GU zPx{8yNuAV5=eW*sedFq+PER-?LOv2s0FYrKP6Y+uPUG`wUqQUsGc*0ziH*;3-ma%} dV(p*WKJj*9?cGU|IJ?7+#M);zc>`M4w|A(xa#R2S literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/frame_14.png b/vendor/aferrandini/phpqrcode/cache/frame_14.png new file mode 100755 index 0000000000000000000000000000000000000000..efc36c034f1a851498e5afad249adcce3d9fb04a GIT binary patch literal 213 zcmeAS@N?(olHy`uVBq!ia0vp^o*>M~3?#kx&YTaV*aCb)T!Hle|NocXoPQU{>hg4P z45^rNchW}Q1_K_Jxm}Srn$(yB6x19;G$JQ*YGh^#@P1G+>r7-mB7W`V*G+#Woopx# zaXH*8JW=$>j6!jp4LdeJ%jrLRbWNrUPz|e%?4;ffl``SFGrY?`?QLBi&=CD)URb+W z;hsD0-;#Cz-B}wlN7VYo-djpFrLW5Bvr5*ct<9@=upq-IF3r5?k;t7x_kgZq@O1Ta JS?83{1OWKuRWASl literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/frame_15.dat b/vendor/aferrandini/phpqrcode/cache/frame_15.dat new file mode 100755 index 0000000000000000000000000000000000000000..18727818d76dba4873f5993f6c9ba948ca4646af GIT binary patch literal 242 zcmV zjE0Ef4oN1(k{KEk$4bexVm9YiXdC56v{3T$rBr2ZX>#9!_g z`B^`!y7Nq9&A4060Wo%qv*Ufsu{CCYT^E&9np3604Q_DzFE?pw^~|@kYVIa#&+I*` zl@Y;Kkq??`U*qt@M8@#wp?=9QD&F@s(Gu5We?xHWW={>BMTjUwEzGB literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/frame_15.png b/vendor/aferrandini/phpqrcode/cache/frame_15.png new file mode 100755 index 0000000000000000000000000000000000000000..a9f416c7cf6d12d66b42288ca30634bd231620ed GIT binary patch literal 219 zcmeAS@N?(olHy`uVBq!ia0vp^z97uV3?y$Jul@|A*aCb)T!Hle|NocXoPQU{n&9c; z7*a7OIpGgKW4(k#*1|KZ|Y7;q&c({geOlq6{l_|NsB~@BeOj2?=LKg_g#L z_90*X^SA8}*y`}CUV@?f(2QoEFV40{_R4EX?7Qw<^Koy}!|cERKm4zM{J-8+&^5^V zM2+FAWR7Cxrk}D0bN=>!U_F$7K!)M7p-w2XYph^ate~2LVYZ-;n}smLZ{Gt?94;j$ Q0^P>o>FVdQ&MBb@0G@4Fe*gdg literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/frame_16.dat b/vendor/aferrandini/phpqrcode/cache/frame_16.dat new file mode 100755 index 0000000000..60af678453 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_16.dat @@ -0,0 +1 @@ +xA E]sIX;n6`qW6`%A/3!!g̡1N) E|;>6⸏97$c]kkw1[mC͜cR>E,hʼnp#xFyWVWG3+˓S}Ğ#G8b^c^cpc&3YQ"vk9܇} ĿQL/ \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/frame_16.png b/vendor/aferrandini/phpqrcode/cache/frame_16.png new file mode 100755 index 0000000000000000000000000000000000000000..6ac8fe890573bd9ec52af39a315ac3ab4ea2821f GIT binary patch literal 211 zcmeAS@N?(olHy`uVBq!ia0vp^fgsGt3?wHw-Y5l9YymzYu0Z<#|Nl#G&c6#}b$Gfs zhE&W+PWZ#m$}S<1weXDnM;@Ma0pIRB@R(Uk#Imf`XJ1(VT~b2AdEtS^#)tMHfByex zYuj(|)!|#c1w;4Yo6V7boNcfCeZ$Lh|MkLuTKNe-qW;_ev6oobrMz06=bb#S{I8Dh zN*l`;cPanr7UAppFZhu8w7WuKxXRac9?ujuXHUG*!(qm7^gY)#$(yb3fo@{(boFyt I=akR{0Keo@^#A|> literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/frame_17.dat b/vendor/aferrandini/phpqrcode/cache/frame_17.dat new file mode 100755 index 0000000000000000000000000000000000000000..87f0cf593bab1049b794e2cd5af31a03aed5ae4e GIT binary patch literal 237 zcmV??y z+zk`^4Hi`JDI!!RUucn1n-C-N9Fb z^_i|y`igmlD_r3USGeLnfcpUE6|QiFD_o!A%FM(h?MeyC;;xkP+jach+JBqBM9KYfaSc^$Z&>9G^P0tbL9Kb? literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/frame_17.png b/vendor/aferrandini/phpqrcode/cache/frame_17.png new file mode 100755 index 0000000000000000000000000000000000000000..5b929ac736efe8f6d1985337c1177744c1269ea7 GIT binary patch literal 211 zcmeAS@N?(olHy`uVBq!ia0vp^p&-o23?!MXtt5dITYyi9E0F&G|NqjP^X~##9iA?Z zAr*6y6aMhChD%6fCBE4#FAy~=;3R%V0whYqvPd*ASAoyV)#iDwF3O@+^;xolwc$WL45Dpax@=q3hFS3j3^ HP6<әt3;H#љt3Y+oghٽlnF>i^#awm;g~pgNs{6zp' \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/frame_18.png b/vendor/aferrandini/phpqrcode/cache/frame_18.png new file mode 100755 index 0000000000000000000000000000000000000000..ee0d6a35e2e6a84e715d5eb5b4fdc9b5d2749abb GIT binary patch literal 228 zcmeAS@N?(olHy`uVBq!ia0vp^ks!>-3?%c9O1%M6YymzYu0Z<#|Nl#G&c6#}&Gd9} z45^rtobZRAwO&R-A}euD{dY-;n8Z2%#V16aW903$A%y{rCU=?+2?S zoD&%`Zq?5?^Z!3DPkmyQ!<_#d4AUG7+P3l-Ff2$saPI&Ek5tBsDdq+>Yyb1R)Ia|J zpWl75<3-~We~dpEEmriJ=c8wIT*8-4vt+_y-iROtCy{xt9gWtVsEV2B>-_MFQnAED a3kLr4d-pH(c*O{GB!j1`pUXO@geCyv16v^g literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/frame_19.dat b/vendor/aferrandini/phpqrcode/cache/frame_19.dat new file mode 100755 index 0000000000..95e26adc1b --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_19.dat @@ -0,0 +1,3 @@ +xA + E.No7ћiiRN2W%x@ڜ' +u6.*S;}àT zrt%,};)ZLP$qgLdJ;w.]z#[͝Og" B}};w#1Gb;w_C+w@Dfu2N9R7|pWkk \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/frame_19.png b/vendor/aferrandini/phpqrcode/cache/frame_19.png new file mode 100755 index 0000000000000000000000000000000000000000..20fddd84c87b089846e770d1f605bb2ad7f57c71 GIT binary patch literal 225 zcmeAS@N?(olHy`uVBq!ia0vp^u^`OI3?%>V|Iq-X*aCb)T!Hle|NocXoPQU{n&#=^ z7*a9k?UaqYhYUnq&Wq-jC5YuSB#B&dnb|T?LRr=$Z_R}Tk2{2ZJNXJK?z>}STkBod zD!Ahe?+Y#-&W#RvJnw{;%)2x3`m?>pT<459AMQBAm|$4*AhL{Qjqyw_`&^NPya`bn zXJgWHCRV@tu;iM2+Sbh*zvtZi6J38hrG4+=)_Z5WUzM+1``TYoxK{mFuJ#L=ri0wm XqQq6W4|?kYUC7|+>gTe~DWM4f0svZN literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/frame_2.dat b/vendor/aferrandini/phpqrcode/cache/frame_2.dat new file mode 100755 index 0000000000..7e42f31cae --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_2.dat @@ -0,0 +1 @@ +x͒ F{v& &Y+?Z1S'y!a815&۴HٞclF1#6 f6O7C֏8gIfB\DԻ( \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/frame_2.png b/vendor/aferrandini/phpqrcode/cache/frame_2.png new file mode 100755 index 0000000000000000000000000000000000000000..9c150ebe3c6343f6eb7549ec855a85c65de7604d GIT binary patch literal 144 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~;3?%=liz@&rwg8_HS0MfW|No^o=iddgTs>VJ zLn`JZC;X{zXk7U1e=Qr^_5b1%IX6fouV7|A3{)sB@vn(vQN$aEu-Zyy=Ewg(ur1JZ on5OM)8&Req;!^#!TfKm!>(UHx3vIVCg!03uy4-2eap literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/frame_20.dat b/vendor/aferrandini/phpqrcode/cache/frame_20.dat new file mode 100755 index 0000000000000000000000000000000000000000..d5ecc1d8a0be752c422607ca863710fb41e89f29 GIT binary patch literal 250 zcmb=p^>&so*I@;L*3I9pv5Fh-u3+BVd2h|XrM?Rygy#mlsE%yq{QN_rm3^kbraf_K zT{CJkWkNo9UY6CcnsV--^u zOJldbdLR|gckNx0zDb^tqE2c~fteS{QV^$$s-(VRP{&d+%}!k2n8hWL&;YFRNm@X@0pL0J~;%WB>pF literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/frame_20.png b/vendor/aferrandini/phpqrcode/cache/frame_20.png new file mode 100755 index 0000000000000000000000000000000000000000..23a061d5442e5950dc57ae4e3f754a72b18ff53a GIT binary patch literal 225 zcmeAS@N?(olHy`uVBq!ia0vp^i6G3#3?%=YhPDX_8iU0msvcT2m((dUcsbb;eOISnHE*Dg<1ej05g z6TelqVW*J3LxB0VBTn;T9{U-YPuzYYm+{WVu)4`tCvuk0J$+=^JinE@{pYYme)_^G zRCKb+Ysa%PrSIR~y}9%L-~0HO`-ivRxp~21a(d3@Y0=B8FU(feo3S(D-WC(-B3)aC Yhx#Rc#-1{#fi7h5boFyt=akR{0I{51asU7T literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/frame_21.dat b/vendor/aferrandini/phpqrcode/cache/frame_21.dat new file mode 100755 index 0000000000..1974dd9d4b --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_21.dat @@ -0,0 +1 @@ +xA E]sIX;n6Upв]٘< i-eW)ŕ…H\jvqHL\6ЅrILܹ%@Vv(P4|Xngɝ~]Du1Us S\,2N?DKF-:eJ]p_,a0` X` w,` X]5 Y4{2vJs9)u۹,]^_7$_ \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/frame_21.png b/vendor/aferrandini/phpqrcode/cache/frame_21.png new file mode 100755 index 0000000000000000000000000000000000000000..291598c72d21bac6548d649a4c0ae361a3f3c281 GIT binary patch literal 235 zcmeAS@N?(olHy`uVBq!ia0vp^sUXbA3?%b5?e76nYymzYu0Z<#|Nl#G&c6#}E%0=4 z45^s&c8Ve2Ap-%H>!P`J6Rz)A#2UEl5$}@DKW0@L!ESpLCP{0HM08I+w9mQHp{3|< z#uoLvV(f=)_!nr4*yb$UIO$8w3qQFZD{qDu=PBg(==aGzoAaDygT+2UZ#}`II(`@a zTfYDFcHhw_Mf$&32ite~e#^+LRiYnGe^u zy!rp1m#1E^%JI(s6AU`tXJpwn$-ff(mztmOBd(vL_Y+&&`mg^F|356yrZelFIO- 7p 7$}>ɷ7p tssrs Vmҹ}R~7&?7ԦIbh{<Mi- \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/frame_23.png b/vendor/aferrandini/phpqrcode/cache/frame_23.png new file mode 100755 index 0000000000000000000000000000000000000000..b8f16ae239dd9f4c18c18191589b0f98e896c750 GIT binary patch literal 220 zcmeAS@N?(olHy`uVBq!ia0vp^xggBQ3?wI-)rtZswg8_HS0MfW|No^o=idcMc)B=- zRLn_E_|wl2FCmdNaYwkl!49Ruey#-t=bLuQzgozl!F}cTUb(J&bqNVPt_=cgZT~$s z{jWFp<9dnv$v=0tgvgY49MfCbML0qY4m1n4Xi4n5_TOEBC(>kZ^AY#v;4+grFO{vO z&Rv<5>-tpA?P=FPcV6u)6IiDGIq|S3riwW=Ht_4NMYDR2t#y5Bw5WhlxO>L&-&1e; Q10BcU>FVdQ&MBb@0PfXRdH?_b literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/frame_24.dat b/vendor/aferrandini/phpqrcode/cache/frame_24.dat new file mode 100755 index 0000000000..7b92e29c4d --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_24.dat @@ -0,0 +1 @@ +xA EMX0;nVP4HSSxU3/O LiJ4V JC%6VR&DBHjDJ??BlcDZ'UXUޏ0ywįj똳3ścj{:GqGNv;笓J <]#8#8H'GqGtr:9#8#8ؓhNt_>teS^\gQe?vuo;>*wlm \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/frame_24.png b/vendor/aferrandini/phpqrcode/cache/frame_24.png new file mode 100755 index 0000000000000000000000000000000000000000..397c64f8533159fe3c792420d93cab3ec64c673c GIT binary patch literal 242 zcmeAS@N?(olHy`uVBq!ia0vp^g&@qx3?y%CxHlO{u?6^qxB}__|Nk$&IsYz@wcOLi zF{EP7+o>CQ4;cu!o)=Bt;3dWH(5PX|Xd0Tbewt9=(giM&ioPz%uZwq_{AtYK_=L;b z-~axk|Ds~-#=Rctl_DS6E<4m{++hxOxW(*z(!L?xL2jbS`@Xaf{qGdGWmpvR5thb97@%;4$j=d#Wzp$Pzhc4tul literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/frame_25.dat b/vendor/aferrandini/phpqrcode/cache/frame_25.dat new file mode 100755 index 0000000000..ba125182e5 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_25.dat @@ -0,0 +1,3 @@ +xA + s낋]rxY51mMBG +*Sx|Ua5ƵZ-,1HPRjX5iG>WR/+uT廯 ӯ嗴u[Sa[kv5+5nJ%+VXbŊ߬u'SRtzZ++VXbŊٟٟٟ+VXb}Ŋ+VXVI+kq[toVZvoNVw}{r<ýR"R] Wr} \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/frame_25.png b/vendor/aferrandini/phpqrcode/cache/frame_25.png new file mode 100755 index 0000000000000000000000000000000000000000..25bc4454afbe21949bfd01d1a8bbab713486ae65 GIT binary patch literal 242 zcmeAS@N?(olHy`uVBq!ia0vp^r6A163?#iy+587mYymzYu0Z<#|Nl#G&c6#}E%$VB z45^s&cB&%ZAqN51|5`cu2Qt4gsF-;4Ir?6nTp+=+Nr<&Ydrf9n!e{G{vpig`o@IAV zpT3{+yxVcXEa?kbazcxq6{lLB{}VM?`;*JP)#Y~fCz=^>mۚ?vmg?ұηdCUIkE\Msfafa>[sӈ9쬩ެ8b]LgEo w1 \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/frame_26.png b/vendor/aferrandini/phpqrcode/cache/frame_26.png new file mode 100755 index 0000000000000000000000000000000000000000..f4a6b393861525908d76f29c6d8a3bfc2ba4f747 GIT binary patch literal 244 zcmeAS@N?(olHy`uVBq!ia0vp^l_1Q>3?$V}WIz&Z0X`wFK>Gjx|4VPqzYAoo^mK6y zshE?T@TZ^UpG-o+3Rkfo(&rC6=+gP1FW~!t-{pbx-~a#LB)q!sxL;FM;QZsxnjpG@X|Wc^3Whe7pc$LY4HynfX%(+id5E{n0v40R%_lqrDMPR_=K#Nbs1J=Z*FW%KHev5UH0bx zvhP3A?5`T{eKs|C?=(67e>XRlv;V$O{Jat(4^}wWvJV8GgET;O7Rx~3dn=g8x81kq zTBsYH(o>B~k(w6wdiUP<-~a4d_f^K|^b>jOg|*Ku{@y(>H?~&4sQTS6>xH+g>q>Tg S)BB(oTgzV+tiQ0S`YHfvkCqVt literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/frame_27.png b/vendor/aferrandini/phpqrcode/cache/frame_27.png new file mode 100755 index 0000000000000000000000000000000000000000..8419ec2301e8c0278f9f86d97417d51d3df000bb GIT binary patch literal 237 zcmeAS@N?(olHy`uVBq!ia0vp^wIIyM3?#Su{YwKY3Z=Ttv zWf2y7&zVY?&I!gH*lL(C<3L-FqWk?PvhJVcioOWu?Vls-VEE&Jg0;|sb157vj!F0I zoFJt8?3CA*9ZT0UM?PxZcc8vtF);OXk;vd$@?2>|KWW^(`l literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/frame_28.dat b/vendor/aferrandini/phpqrcode/cache/frame_28.dat new file mode 100755 index 0000000000000000000000000000000000000000..8cbaa1961bed61c3d774192448857480eb49762f GIT binary patch literal 318 zcmb=p^>&UUU$cUMYi#vvHNCX5BIcc(ch)S(P&rt#xF_>o`BhIHdwqxJ`pXR0ab3O; zyhvzU>thM$uzdDevN_u7>@yw8*(S-Z(T=tZ`1|rey6L^W%k_%NF5F|Ui=6h%{9e%d zZ;qDSdF$l&FG(-^99&vHr#0ZpbjM5kK3J?R3cg=E!9=w8_VG%C;KB!IWDhW~88k5R zBz#a~TYLY*^54yi?c%-~`hWLu-)r%Km;K+Pv$J34CnoIU-@%{2z$^h(euDc6>nEnl zMr%QcZlKHrafo2K5)VwFf<8p>oeNCgzsZd&I##GMUwz`}E2+Yy_3mfghfV%>+;#2K w_7?4#@$=nu@tm|;Z8o*f=bqOl7=Ny{aυR-rn.ꯋ\T:*)|) , ,x_}:^RUoɢu~މX`XЏЏЏЏ_`X`XЏЏЏ_`X`XЏЏЏЏwbX`PU)D"c{z3<}^?bm잃a.] +{Q6uT,9 \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/frame_29.png b/vendor/aferrandini/phpqrcode/cache/frame_29.png new file mode 100755 index 0000000000000000000000000000000000000000..ffe072c868268ea77f05f71c17088021d549090d GIT binary patch literal 232 zcmeAS@N?(olHy`uVBq!ia0vp^tsuP;BJn$?U}wS}0TXykDcgTe~DWM4fn*mjQ literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/frame_3.dat b/vendor/aferrandini/phpqrcode/cache/frame_3.dat new file mode 100755 index 0000000000..188d531c2f --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_3.dat @@ -0,0 +1 @@ +x E{v& &Y+bk'ya:TXl޶$W+ӏv9}gR@H0YPBEm?s"bt2cn:ﺭ;YzQ7 \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/frame_3.png b/vendor/aferrandini/phpqrcode/cache/frame_3.png new file mode 100755 index 0000000000000000000000000000000000000000..945ee7cb993022c3ba0081cdd6b833b92395b30c GIT binary patch literal 147 zcmeAS@N?(olHy`uVBq!ia0vp^vLMXJ3?y@6zxn_vwg8_HS0MfW|No^o=iddgJUm?- zLn`JZC;a)}-nj7F|NHzr>;C`eQV>iqJa$2VtqrL1f5H!Lr4wnjD-wRh|KHox`0@XB rt_3F@rfGYe5mn(<%W`B5%F}Pb9F^HI>YU!D_xBYq^}BRWJ#p5u$@O~+U60g$ z^ZZ%%C&E6_>--Dp^Lw@H{+B%qvQk^Qjp5k3r)$sId8T^L{8KD*Wwpx3yDA4+gnbMS zvP`Zw$-FB!Z`b@c+Rxs&eZ4X_H(t}zN!in)_Pe(B-kBwv{Vn`0k`1A%fm#-_F9E_Q zHV6?QtI7cc&w)*Z5ts^pmI@a>n)4-LQd5VAxSF@+|0}%z9_8)t4X9Z8-TGsS+L_O` o3*N2iKjl*T?_}Zp7w1|m>#S{6FV2w-Q4O5Cykv#>|8j$;0GhIr$^ZZW literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/frame_30.png b/vendor/aferrandini/phpqrcode/cache/frame_30.png new file mode 100755 index 0000000000000000000000000000000000000000..75dbddd2416814ef91b71363c859c3dbe86f0802 GIT binary patch literal 255 zcmeAS@N?(olHy`uVBq!ia0vp^ogmD}3?!9ab#?(Mwg8_HS0MfW|No^o=iddgws^WY zhE&XXJ8dKHAp-$deb!SFYn&!CY6Q4UH4Jk7sONJ?V2XzJ1%+7_{OTQ#YUCIa<5O*I z@1FcVdoow(KLw+VEhS36DYCp#b}QPnZQP#yZF5>L_wIw#N8!&ZL32x5J2SZFRNT#W zJ+d{@@YEHq?4aMP=C-7+TKmm!p0e&81)Cc7+)G|xes$W5z3%q9Wf`gZcFvkf-|8dM zyYV櫷:ñA8-+mTbllltM&]ll&]Ill&]y 6` 6`iuyXWi\tz>.zk t77wJϔ4w҈85 \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/frame_31.png b/vendor/aferrandini/phpqrcode/cache/frame_31.png new file mode 100755 index 0000000000000000000000000000000000000000..b14d1fa267f229a5b21beb06401aaea3c7ee4673 GIT binary patch literal 260 zcmeAS@N?(olHy`uVBq!ia0vp^y&%lU3?w&9$bJT-*aCb)T!Hle|NocXoPQU{+Ue=y z7*a9k?X-=&hYSQ<^+j{d5~R!+0*%=60@{+AM5auc(!n|5PK2l6>q3RVA30i5E;U78 zzm!b-J6oUkrH>MO^Sih(w%FVJC(3=iBOLgbf7_Y;qQm^gYNmAkvubmKsBNb!r<>aC-Pn**9g*ߚ?aOkL_<[c>c˘uLI%#0#0#otѢ}4fv_)Eph5R881#0#0itZ#0#0#0itZ#0#0#0itZl0#09q"HܜHQ"L5}-Y׾k`>z鸳4&p!!`:5 \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/frame_32.png b/vendor/aferrandini/phpqrcode/cache/frame_32.png new file mode 100755 index 0000000000000000000000000000000000000000..58d42db3c5b755228c7565cdf8128438559cfe1b GIT binary patch literal 262 zcmeAS@N?(olHy`uVBq!ia0vp^6G51f8A$#VJCOjS*aCb)T!Hle|NocXoPQU{+U@D$ z7*a9k?X-=&hYSQ<^`{N~#na9iwuURu!V?k}}KG;`at7keZ6HBO!j zPFw%CYT8codjH_FpLqjK{?_pC(kM^0-qBFEG_3wrT<*O&_g>F?-&noh_CE9X8vcLx zZ-?L5c<5S4(d*MS%jL4aFL*KcmFQoSBb$x~rEe4WKl3d;RlhCaH-pl}T98i}JYD@< J);T3K0RZV1Z(9HW literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/frame_33.dat b/vendor/aferrandini/phpqrcode/cache/frame_33.dat new file mode 100755 index 0000000000..a2613755ae --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_33.dat @@ -0,0 +1,14 @@ +xA a޺@n7+*L++柮bb*LCc kHrjJ5Yi~0_TT}e>5b_w͟?\Rai+7W\wLUNL ++ ++jOkc\˩|%o} 8 ++ ++ ++ 3g ++ ++ ++3g@ ++ ++ ++:RXB9I=ko/Swؘٯ`gr_ٙYVSYzIefnmQoz > \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/frame_33.png b/vendor/aferrandini/phpqrcode/cache/frame_33.png new file mode 100755 index 0000000000000000000000000000000000000000..924c728e0aa0b3c22b7537d833d53f072a923640 GIT binary patch literal 253 zcmV7LUZoG!D=_b+G>~XohCiGYUA3?`$qlQw=oFH)|?GM%W=ZP z?o-WP`{JCI-1j+kVd;lds9Q_l#)_e*e%!H*n2YxP(|eQ4yd?KKF7uLH<|VmLxpSZ$ z1MOt{Gd*JG7)eOIdQ#d>d~xt^_>>*JiBA`^i7E%w2i00000NkvXXu0mjf DAwYG& literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/frame_34.dat b/vendor/aferrandini/phpqrcode/cache/frame_34.dat new file mode 100755 index 0000000000000000000000000000000000000000..7ceb0259d4a6f14ad6d9e5d76eb87c07b034c711 GIT binary patch literal 331 zcmb=p_4bZoU$cP#>xCNzb)OE=JWTSu!=w6LqU*^gyy-W?Z;@;Fg>L`1=l-EkZ?iVvV?C0_X=i2_ zCW9G)ljhbgx7l(nwvgX`%e6^Q_5bh5(A^bzqxMK4)(Kt#i%i{@a^9b964fc@euQ;PW=~uiO$9&bgnPb zQZeW4^o^WMjsmX#U8f~jtBEBjv`tG?seT)Aq*3=qXXlI+_hnO3Z`!AqXdKG<^zh@1 z$2a89EJ`kxb5FQ`=(boyS7nhxxW0w*`IRj*&5GUQE?a*(lXrI>tApXq$M?>eXvpg? zJ7Dnt${M{rF8!;dRI6f&a!y#e`IJAq{!imf{LG6>4VD`%_D=Lmb(?#S3j3^P65% literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/frame_35.dat b/vendor/aferrandini/phpqrcode/cache/frame_35.dat new file mode 100755 index 0000000000000000000000000000000000000000..56bc3e28ee83b24767b00391773560ca842f0b44 GIT binary patch literal 342 zcmb=p_4e+@yk-Lt)(dC;l&Z2-bJsJ}Gu4L)&k?(_Yz24S#B<8tYk0!l*-FIIBjt?0 zZ`YVyJa2n|_x3Zdk9bXgHfz^qygTh2XweY|A-_q!&2k4t`5 zdHt-uf3>=7$NxRcHpcK?YhPbowodr?x--Th*X`3@Jbts=sCI7N@2dT+b7tjn%l`^M?L^?B>}TrgGEWxv+gtv>&1 z^SpAXK9B(~H-km1?tx8$7`Ys%2q^kG_8{C6zxh|wzed(x`wSQ9yIX(x>6}yP9JAN? zaAi*~^7>i6``g|}l``+#Pxx*7Q>a$?+fMp@&Wq;Q9mQL}|JJ)V^U3dOv#MPg=?~>E Qwf>fGyj#QM-^sTe06Gf6oB#j- literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/frame_35.png b/vendor/aferrandini/phpqrcode/cache/frame_35.png new file mode 100755 index 0000000000000000000000000000000000000000..d29806c6037dea38b05d8211a0a45c2e0c1ff1d0 GIT binary patch literal 243 zcmV69g+8T9FaOlD5lXr@5A{4g21 z`3F-KR1%=7ED2boeU%@kIrD;Vui5n8>omRhI!*7rPSbn8llib6 tcmDZ{$vZ*}#x^0hn~*mYfxOG)^abWznpY06{Y3x(002ovPDHLkV1fp`ZrcC= literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/frame_36.dat b/vendor/aferrandini/phpqrcode/cache/frame_36.dat new file mode 100755 index 0000000000000000000000000000000000000000..282c60d23bca6a7c3ac2c3da0744992f3415d8d0 GIT binary patch literal 370 zcmb=p_4ck~UbBG!>xDHtOI5|b@#Qg?H_pDw$;Z^XB}m*L@cxD_oki=_lM}=$=E<9G z-R?29c-FLpVq^dBT+!v^g&NTH_@wd)yXEcGyBzK-j`OqvsA7=ytvYW@9>9b z_WqKQLf0xI)<4~MTkdf{-#UAXb5bv_XIHKgJ-y+Ial~c2Rr$$h4+rJ0P5XKC?x8T> z8}0rlCMY_$@kss?KYU%i=k@&DYZIUS(0u#r`Qg$fYBKRcefj5YDxU=N96s;-dSNve zNU2c@M9){5FJQQ)9U&%Zbfx$L7;cb*sRL;UIPVCC2diGpI|Q*2B5lhKfv>@?fNH7t zol$&B)74DpqnE3hkLL7O>+2T(ei-}yr{??h8{VJ({BR!k#40|+{iXYjd3MGhD3d#O cr#P`%x8CVcM|J%hp|(5rsloS;^$2?d0KeI-=l}o! literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/frame_36.png b/vendor/aferrandini/phpqrcode/cache/frame_36.png new file mode 100755 index 0000000000000000000000000000000000000000..96ecb421354bec386b535fe1e2cf4fd753142013 GIT binary patch literal 272 zcmV+r0q_2aP)#C@Z9i(!~1M zrir*qV+k70-90^>68kqFj-O|}12;kA2%H6FtI=5KdLSEeASYjjHt&ri*^^Il@nz@$ zcM<0VP`V6=a&MyY!``k&|IeMgxU(i;@#22_fZ4~6ypj)cPBagDuD;9QQOl0fagwX_p(bdJihERcvP@-vOY{Y; W&Oa>B-?9<_0000W;+CoZ{7==byZrmTWaR#jx=*j}d9!=n z?jLue-7XczO-tXCTNhVr9_xF%CU42z`5zaC*qmOutsr!7Nc8d9(}S-rUG+8hGgqu? z>hF#zI+2@FPEJzwo~AQDvGQO3&(Hs_exLZN>iNf?wdd`Eg1#-+{C#?NzFg&`r^?Tk ze!Y~>4^#=)^407M7_OO)5CclC+`bSD6Zb-m25VS)uMGyjf-Qs@QzZ?7*Sz=JBAN4V zs?k%Ey^Est6oX4n+Z;cW+cNi&e)WmBGn&?4`2F$Yr!zA>_V(^buig{cW~=_xLjUnR b%i@}2^Cua-iuB0WJUmakbVYvd1M%+wXsyHf literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/frame_37.png b/vendor/aferrandini/phpqrcode/cache/frame_37.png new file mode 100755 index 0000000000000000000000000000000000000000..fcc51627d75b1195d79e915e5ba5a26accff30b6 GIT binary patch literal 279 zcmV+y0qFjTP)Q+l9uWqZrGW15EmmL1Za)FnMD)vS~08#pCh dPbukz=wHCg@a-IQ|2+Tz002ovPDHLkV1gG4c{u<8 literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/frame_38.dat b/vendor/aferrandini/phpqrcode/cache/frame_38.dat new file mode 100755 index 0000000000..71cf53eb5c --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_38.dat @@ -0,0 +1 @@ +xA0ЎuA2;Нk(gytp9$D\e^'t-aIFMSkIŤ:7|LkN8N7i}i,[WgӴ?31iN}}=OM:4)SL2eʔ)SL#$ JJM:}]L٧SQL2eʔ)SL2աPt(:)SL2eʔ)S:ECq2eʔ)SL2eʔECѡ8O2eʔ)SL2eTCѡPL2eʔ)SL2ݓsJCIKԂi93n_ +Ri4\g;% }an \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/frame_38.png b/vendor/aferrandini/phpqrcode/cache/frame_38.png new file mode 100755 index 0000000000000000000000000000000000000000..89238f3c5bd6d3fa6aea44d2037dfd3bc0a34723 GIT binary patch literal 279 zcmeAS@N?(olHy`uVBq!ia0vp^D?yl%8AxVH_vZj9wg8_HS0MfW|No^o=iddg&Um^w zhE&XXJKd1$kb{7$dT5{Gy(7n%mk6AeQJ$Kzej0N`L_}BkhKVNY?%zz+J@k*Y+f^`a z^_k}%XBzDPc}byOdLr}0>WR7QehE+CtzjSL!0mG&m*?>2?tA~Zcn+}6IBv0`eDmq~ zc}z}+JJ0WJ_POR~GP(Dkvz2Uh;m>QHUB{HvnxDHtOI5|b@#Qg?H_jFnQdbgP@~WvsW^bbC&dz=Fbh`ibKL2SI zT-|A9x%8!2QQou6xhFC&&-ym4$aD9pW82OKdqz6mnsRx4=J8U#egE%mowI%0@84xI zZ4W<8zrSnRI_rgRZWpP2x%=6e@AT!byU*{w{_FTmgQ=a_yU*CBT#j3w|M=bQ)LTYp z-zJx@O!B(5Z`GEZo10QkPt%RwHa}ta_dLm$f4^*>CH-=s6v(7v&e*fR{ ztx~zGZ*4yp{T!$et^>&Eoe;)fc50TK5C1au;(Jh&pbBm$WPVLpzUJk@F9}r!=o)#a zk*D#WWv<`r;O<;6&F5D_CzowJTRZ36gVgy2`}e(i{XD+qP1dt!Yu@B<2_N6LRd3?0 je}Av+{;~a+3#!$wOf&lxJI6os@crUlGxJ^MEBXTf`CjAm literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/frame_39.png b/vendor/aferrandini/phpqrcode/cache/frame_39.png new file mode 100755 index 0000000000000000000000000000000000000000..1dc9cd1bc53a2e27ad5b61791c7a76cdab408123 GIT binary patch literal 264 zcmeAS@N?(olHy`uVBq!ia0vp^YeATi8A$#}%mtDRYymzYu0Z<#|Nl#G&c6#}?e%nV z45^s&cKSvxCPM)i^QmwDPN{$JaQO_kPo;GYt#kKIo$zc&c3+{t4%5trR)KEM%_$W+ zC*PSQGk)DUdv52RwA;cz%>sV@oL27Om%5WD`n|Bh@=ZQB4@JyxmgML?8g}V+>Zb|( zXHzc5U$%Rcbzopr09yKW761SM literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/frame_4.dat b/vendor/aferrandini/phpqrcode/cache/frame_4.dat new file mode 100755 index 0000000000..67b30e82ab --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_4.dat @@ -0,0 +1 @@ +x E=u pجQCOM'ˏ$ @3eF\FNXRyؾC{a8R Ńa2@qkH1(`cj~0ܨعnXGĀ \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/frame_4.png b/vendor/aferrandini/phpqrcode/cache/frame_4.png new file mode 100755 index 0000000000000000000000000000000000000000..b72f9e70d374741d1d4ec4537b7a379088325669 GIT binary patch literal 149 zcmeAS@N?(olHy`uVBq!ia0vp^iXhC$3?y^40(5{BTYyi9E0F&G|NqjP^X~##UY;(F zAr*6y6aM`F&%o^b_W%Fyk`gih|L^bcaylaE>?|uG@sHV&`Ea~}XRw*|gxLT4_cSuT v*!F>Kf^<]߳bZn^AQ}[9^]ynajM܇K̘1cƌ3f̘1{W5}{7lMޚxI<Kαyl3f̘1cƌ3f̘1ۻٻ={αyl3f̘1cƌ3f̘1ۻٻ={αyl3f̘1cƌ3f̘1ۻٻ={αyl3f̘1cƌ3f̘1ۻٻ={αyl3f̘1cƌ3f̘SʑӒ7HK޼g\u_r'4[-]qL8ƝY1q!/(% \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/frame_40.png b/vendor/aferrandini/phpqrcode/cache/frame_40.png new file mode 100755 index 0000000000000000000000000000000000000000..8034d862d60aed55180914d323aa4557f39c036f GIT binary patch literal 267 zcmV+m0rdWfP)xUy&%?@Zqe$Lt^IHF*f5Ycy_0y589U#OP zDOgy2<>WYTpYrhA^tGLJe{+Ghzs|!@a34iK>1DI|zE}TO_YMEQ{K#kcE&Gwr@LTpH zpW(OcM?S;hKGje=R>w-jeWqvU%KmI=|;a=Y2bm$Ju*=arXY8`~`*2(t2@u R+XVms002ovPDHLkV1jNhe*XXf literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/frame_5.dat b/vendor/aferrandini/phpqrcode/cache/frame_5.dat new file mode 100755 index 0000000000..d5dafe1860 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_5.dat @@ -0,0 +1 @@ +x1 Eu7ЛZ|ND B0@R$l,->VKZ[Io+o1yT37=P9COrj~W-G?9JF6VLpcd literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/frame_6.png b/vendor/aferrandini/phpqrcode/cache/frame_6.png new file mode 100755 index 0000000000000000000000000000000000000000..05ca358b0ebc0c3a0b76df7bc0411af2d596f586 GIT binary patch literal 151 zcmeAS@N?(olHy`uVBq!ia0vp^njp-`3?wJ5o3s^3u?6^qxB}__|Nk$&IsYz@<>Tq% z7*a7OIpGgbjFGJ^N}+Uq<3Ul6SeNAsih33jb6-tP*oFY|>b2SQ8z)uR?vtd#dj@Cr|apornv1)El=m yfE)D&wC=s-ob2oKGnB!6a?|8|>$jotR__k)Tfb?1CbP75iyKQ(;+|ur1|Lo4=9^M0XYLiuS90INiM|3P! zl1w_Dw_=lP*>oFG#pgDRUYDi$t)1S^DCLO~UG_j|*#gebx3BZ+nH+yL4#Xe`MZ5Oaleo}by@B3DZ#Iau!Ks7Twck}KFFz0sl!D6kkmQ`t zEwPxAt7e4vK(tICs>9R+3CvUzZbGE0t`1I&>PRt0PIx4!4(e4n`AHE@7YTQ-GD-FT zU5Un`f|ct4S(0g==b;u6VVT$-+hayNZ>kV6cem$hSdySrt`hAq)&LF)(-K_0ARP&tIq1zdFuig-v-KQf|`xVm{I3hDX Dr=(<@ literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/frame_8.png b/vendor/aferrandini/phpqrcode/cache/frame_8.png new file mode 100755 index 0000000000000000000000000000000000000000..db1f1877ab3aa0f221156a6c8ab461cf548b7c22 GIT binary patch literal 204 zcmeAS@N?(olHy`uVBq!ia0vp^h9Jzy3?w~+F2@5Ywg8_HS0MfW|No^o=iddg8a-Vc zLn`KMov@L&*+9hQeCRTV$kt=bOB^&Mo4IaE8~7hfU-_hp z>GjwD9k;ZU{x$DC`?c>k>SwyYUwnVQ%-n^a7#d|n|4z96z#8Zn22WQ%mvv4FO#qqh BQ^o)Q literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/frame_9.dat b/vendor/aferrandini/phpqrcode/cache/frame_9.dat new file mode 100755 index 0000000000000000000000000000000000000000..d79295ee0f93bf3b101c220b436cc0d302b3dd72 GIT binary patch literal 206 zcmV;<05Sh~+U=G>4ul{KMO}02Nm#TyxdGe+ZZ;LFff1oFD-%8|o4)*%Sp6YHdA;?7 z06sMb75pI)g+!%pq7Eks1(Ax~0~7*wDR<~T*G1Lma@N`!#dGl;n!P;E()p>G>kd7u z??pPBm5wsHKNm@}RC?UEAu(m9sO7c1-s)A`rDjb%?QOHhl&f?!YX5jm&9&Z*<@HW4 z${vU~>O~9yjCv75*xjqWU*~WAGqlU$A+xo=8}e*Zc!_gVK9@YR<##wsNwa7{iF0qJ IFSFt|>Yb-#$p8QV literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/frame_9.png b/vendor/aferrandini/phpqrcode/cache/frame_9.png new file mode 100755 index 0000000000000000000000000000000000000000..74ddf08da2f671de67f1fed645afe705662e8424 GIT binary patch literal 199 zcmeAS@N?(olHy`uVBq!ia0vp^rXbA73?y%B7#ag9wg8_HS0MfW|No^o=iddgYCK&W zLn`L%ov@L!AwagA1{7>@m-%iy`qAdx51;UBica)=ne)?S3j3^P6=xt5c>OY+~gW)7igeMcUJuTaR$o&p-O;5+hmJG|D H^z#4!b%aZc literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_105_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_105_0.dat new file mode 100755 index 0000000000000000000000000000000000000000..97e9e5dfff667572d9451add45a3a5e05c157625 GIT binary patch literal 162 zcmb=p_4bw{SAzlr%YohNFaEDz7aTYx-~rdw7iUi!E!oMQ9qZ10=B~!Y1!Xh8FbC`Z zyZK8u`25eDzn4VhGJ|J-om&>Dy5;$~&1-gk)KCM0Wh*o*?Q>r*t&Z>8Q#|w7j5+Ec zQ8l$%fAdS<5uE#%zyDF50RnO9r|hm-%}`_g>f0!1_?Y+V!H}75vy!UUpR@9hH`4Vh H(~brJDN;^O literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_109_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_109_0.dat new file mode 100755 index 0000000000..eadf83a2c3 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_109_0.dat @@ -0,0 +1,2 @@ +x= +0 нi9'b$t^#ii?bK[AUF徝Ƶijx]m]2-ĖK~ Vw}X&Oɓ666666yR'%lllll/hlm dl3+mͫ \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_113_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_113_0.dat new file mode 100755 index 0000000000..5eb7f5de20 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_113_0.dat @@ -0,0 +1,2 @@ +x; +0>I9+Es=ϤL1̄[FZU4?i<;7;P#W-[ݯ6ddddddc",;"sk摑Q&erw######L.摑Иy1^˲\3 v \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_117_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_117_0.dat new file mode 100755 index 0000000000..781c7f875b --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_117_0.dat @@ -0,0 +1,2 @@ +xA +0 }OrR,#3,o5Cq:;;wvNJZG=m} ѱ2iRkj_YYYYYYYYe_/WVVVVVVkd-Ϻ,#OZc]|{ž$ \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_121_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_121_0.dat new file mode 100755 index 0000000000..68810c347b --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_121_0.dat @@ -0,0 +1 @@ +x1 О/w YMS8>2SFOEcW\ۼ{cpKGBКmxhfffffff/s22W|*d1*5̬RWas\xm~8߮r0wjsdm&y \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_125_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_125_0.dat new file mode 100755 index 0000000000..2c73ef1a89 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_125_0.dat @@ -0,0 +1,2 @@ +xA + н_TH`3AOL4 k(ewGW. #2} \Ygggggggggg_d>j^s;;;;;;;;;;'q;;;;;;;;;'˰qu_PYw{e=dG/ \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_129_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_129_0.dat new file mode 100755 index 0000000000..812ee8a664 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_129_0.dat @@ -0,0 +1,2 @@ +x1 + /*DE'hgt-}_pV \"b=s[J=8Dho۞' 0X ۴e0`  j" 0`Wf`^P0`2Ȁ  d07(Y/XLGby"pT \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_137_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_137_0.dat new file mode 100755 index 0000000000..f6d993b033 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_137_0.dat @@ -0,0 +1 @@ +x1 О/+FZ?J L7Ժ*Ba%L~˻ʓCJYIWJ .K]R0a„ $INTwlLaL0a„ &Ld@PO0a„ &L0e@P?a„ &L0aDe@ &L0aMIlL&)dlgacR<$v,ɺ?U2] \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_141_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_141_0.dat new file mode 100755 index 0000000000..8c685c8ead --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_141_0.dat @@ -0,0 +1,2 @@ +x= +0 нi9'EDx͘%I9+E{$m^&uS"D6ڟ]98UMbҾY[2拉Ĉ#F1bĈ%iRN潝ѳ#;#F1bĈN1i#F1bĈ#FtZ}Nk1bĈ#F1bktZ;#F1bFV-u"IoD-*7uj>bMV+ \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_149_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_149_0.dat new file mode 100755 index 0000000000..d258350297 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_149_0.dat @@ -0,0 +1,3 @@ +xA + н_MEQXP৞.|94e{JLv#^n[ ?; +ZIV-*w˒1*+VXbŊXgwqX}JRYbŊ+VXbeΠwfeΠ^bŊ+VXbʜAʜAbŊ+VXbŊ9ٜAbŊ+VXbŊl0*0Tj`?Ϊ;X=zZr* \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_153_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_153_0.dat new file mode 100755 index 0000000000..fc79e9eded --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_153_0.dat @@ -0,0 +1 @@ +x1 Н/礑h&F`Ҽ@I;PZ^X͌mf.=5 [if-R+!wr˜g\j̘1cƌ3f̘1cfo.2?1z `ƌ3f̘1cƌzƌ3f̘1cƌ3fztf3f̘1cƌ3f̘kk030cƌ3f̘1c9;Ď`vf͚̆ZϘW9 \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_157_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_157_0.dat new file mode 100755 index 0000000000..ad749f3059 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_157_0.dat @@ -0,0 +1,2 @@ +xA + н_QRY k*q͵=j7~nN.p%ڵsi.رcǎ;vر{.-W2={mgy+رcǎ;vɳ2;yּcǎ;vرcNɳ;vرcǎ;v2xI3`FaC~S7rJnYfY8T-%e~DHFIIc5zv^p@ZbtcP7qJ_1+9^La zKHT%7-Y-t#T{NC)k=gd2o zjNY~Ld^ftnUmGj|mU3?60jkj`HqbYoV)>#S!~tpmi%zkeFHURTr~Ks{-QatqS5&9ib(yR@{gMnQ+Lcye6Ty_ Q@^8h%UuzjeUK>3D05K(RC;$Ke literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_165_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_165_0.dat new file mode 100755 index 0000000000..3a17a0510a --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_165_0.dat @@ -0,0 +1,2 @@ +x; +0>I9+DyI4ˠ5:Wvdqߜܴ<d2x%[U%2]&K,Ydɒ%ˡ,S՗r2yd=,k_{Xdɒ%K,Yd)0m,Ydɒ%K,Yd)0m,Ydɒ%K,Yme,e%K,Ydɒ%K,eq Ò%K,Ydɒe:I9EQ=Ls I{ZtR}Sn:|R[?_*SL2eʔ)SL&ϦI O2O2eʔ)SL2e*C1PPSL2eʔ)SLP22)SL2eʔ)SLe(}2)SL2eʔ)SLe(}2)SL2eʔ)Sic7;"ޙFͦސٙvL ^2}oO'r \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_173_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_173_0.dat new file mode 100755 index 0000000000..5ef85e7ad4 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_173_0.dat @@ -0,0 +1 @@ +x10ޯT [4v2ƽok݇;Ӳ]f֞dljlG0n+߻mG˖-[lٲe"Y}oV[lٲe˖-[lٲeհՃ[2lٲe˖-[lٲeհՃ[2lٲe˖-[lٲeհՃ[lٲe˖-[lٲeValٲe˖-[lٲef[BmаE;N-ۜT/rl?* \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_177_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_177_0.dat new file mode 100755 index 0000000000..78a26a77b9 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_177_0.dat @@ -0,0 +1,2 @@ +x1 +0>I9+?߁iև d̹xֈxN/է|{ظ8d0h=cFf̘1cƌ3f̘qq=w6;l4cƕ<nj3f̘1cƌ3fXһ1ֻcƌ3f̘1cƌ3fXbwnj3f̘1cƌ3f̘M'X&1cƌ3f̘1cƌ3ֻnn1cƌ3f̘1cƌÍ3U< \7+(<OƌΊnj4@ \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_21_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_21_0.dat new file mode 100755 index 0000000000000000000000000000000000000000..368c9941fd09f695d58934eb0de062a54fd74c02 GIT binary patch literal 48 zcmb=pWo*#E=&aDfq?gm=vB)I2o5jQUx&ZeRqn1^Uf@;3Hn^Ft5vfXH5Z;oPEP!;?T E0GJaIuK)l5 literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_25_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_25_0.dat new file mode 100755 index 0000000000000000000000000000000000000000..e4a5b6d8afc4516779d7d246ebfcf7ced36029d4 GIT binary patch literal 57 zcmV-90LK4#+A}bK0YfN+0xnzV>=60G~7^dI6e8pX8D%7wG^1 literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_33_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_33_0.dat new file mode 100755 index 0000000000000000000000000000000000000000..2ec712a7adcd6edf1ce2c785adf00e3ca7c5b729 GIT binary patch literal 62 zcmV-E0Kxxw+A}bK0Yfwh1zdy_AgLHMX5awSafcxR$OTi6Q^}|q!_EwNn!%sOz-Hh| UV{|eD?iT|>N~mE50E^|%1m89on*aa+ literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_37_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_37_0.dat new file mode 100755 index 0000000000000000000000000000000000000000..1588cfce1318608538dbde89fa7f04d05ff574e4 GIT binary patch literal 65 zcmV-H0KWft+A}bK0YfYh3b=?-grsWFnuP~Y*IkB0KrWabyvjz+8Z~Q>n+4Agg!2&C XEPQ!r0L+5>(SR6b)HDkKY5W93c-0$f literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_41_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_41_0.dat new file mode 100755 index 0000000000000000000000000000000000000000..e369027e3831ce2d9ac858eaa2cdfa5e5498df07 GIT binary patch literal 68 zcmV-K0K5Nq+A}bK0Ye-R3b=?>h@^7Jm`MQ8#+`;lLN1s-f=Wlt95r*)%t2))yeuHD a3;~--s0 \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_53_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_53_0.dat new file mode 100755 index 0000000000..572d279efb --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_53_0.dat @@ -0,0 +1,2 @@ +xK +@!йoQϺ:(m&s-6Z{m4YX.F٭XZij=:έ֋b忑VH 8 #[Y^Xe \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_57_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_57_0.dat new file mode 100755 index 0000000000..ea81e6dc61 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_57_0.dat @@ -0,0 +1,4 @@ +xA + {^s=YL՚ ( +ouj)  +Z7yv,ԴwVQ iGiҤDfەwo4ѤoLLȼ}4 h \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_61_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_61_0.dat new file mode 100755 index 0000000000000000000000000000000000000000..93d2444d8249bf3c534223bc1bb339d54f7af957 GIT binary patch literal 119 zcmV--0Eqv1+U?dk3IHJt1<*dHN$!6+E5(hNrI=BE>;m7%=n!PCCC1eixx=ze|IFU^ z&9=7bWCto@OXj<~I62OGT~zl9#bLvS4O_9jbFKC1I6Jle?Kb{3*sx(Qw*P@W*om-i Z8|GT{jXc==mTf+B&fNDGb^()u!gb2GK0EGW|+U?gt4!|G?1i(A5f&YK5CmXS?mE~YV7mp;Hi4u*ftt_y*5-7lK6FzHr z>I4k)=)nLMYOw4+bP*CfgX@x(2S?CA0}V9%z<}J֞!Ŋ+V嬪.2XbŊ+VX.kBzwձ̀gkYZ \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_89_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_89_0.dat new file mode 100755 index 0000000000..aaa4c5267d --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_89_0.dat @@ -0,0 +1 @@ +x1 ὧi9'Hl?L^"&M?bq?˸,9!z]VScƌ3_c!`n3f̘1č 3f̘1/f>.Uc˻; 2;Y+7 \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_93_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_93_0.dat new file mode 100755 index 0000000000..e218fa0ef3 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_93_0.dat @@ -0,0 +1,3 @@ +xK + EyV,OmޠrPH0{2bc{tQ] +{Q{{弬֒ǎ;v_ڳ}L}l߱cǎ;v̑̑̑رcǎ.Legw3qeѾ@i \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_97_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_97_0.dat new file mode 100755 index 0000000000000000000000000000000000000000..74ac719d06e7ba35f7b749e668c884f291daefb7 GIT binary patch literal 150 zcmb=p_4cMC*8v3{my5N2lkfa5ijADYwQUyT?SDJYDA|}8ET~@TVG}wt#i2yK=7z!b z>XaF;OL-RsNjlZ?U*+(wbH1LOIxmNXjYq;D;XuQ~?`*DdR@K|)ymqtY`Of=>87kg* z$16{D`=-laMU02Hv7J?~OBUdlkQVwh=~Mq+LyI<1=UdT5U+wnXEMO=-^wS0a*1ӘK}:!iY'*3]fsmb[JƶŖK9}cccccc'u.6Ʀs6666R[^g{/lٷ 7͂ \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_113_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_113_1.dat new file mode 100755 index 0000000000..1dd666d9b9 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_113_1.dat @@ -0,0 +1 @@ +x1  -8fL(pBlDM9";-;?1p{\%-3:@ad4*Nadddddd########c]751xYu \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_117_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_117_1.dat new file mode 100755 index 0000000000..8921f64377 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_117_1.dat @@ -0,0 +1,2 @@ +xֻ >ӘK$^ 8YQSV'z8jzʇ^]סekXYYYYYYYjݵ# ++yeeeeeeee#WVVVVVVVV;"+yeeeeeeel'e;b&^9{/J$p \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_121_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_121_1.dat new file mode 100755 index 0000000000..64bd8ba04c --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_121_1.dat @@ -0,0 +1,2 @@ +x1 + н\CPbїE$DdƩYtڅλ0$ήꝝga7yٯ痽Y??{{D \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_129_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_129_1.dat new file mode 100755 index 0000000000000000000000000000000000000000..62cd1c9a18c2367d1c2c4a125b0c7bfb7647cd07 GIT binary patch literal 164 zcmb=p_4c+Q*8v3{)(d-=d%mmRskg|fP}}X+^rc5NZJtc#aociguIS=*E7o0PY;Hez z>gn9i&b%cXe#-fTKV)dW7BEeenT946)3$`s uʮ>Wd )g'M{3\d6ubذaÆ 6lؼn]Nذ9FްaÆ 6lذa3a#oذaÆ 6lذذ5e16lذaÆ ]Sbk6lذaÆ mͤ;CcfIdsG \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_145_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_145_1.dat new file mode 100755 index 0000000000..6a9950f7a7 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_145_1.dat @@ -0,0 +1 @@ +x!0@k 4a)q2i.YCUO{35UZFn]fN>bdwtzJF}F1bĈ#F(F6r1bĈ#F1E1ilF1bĈ#FtF#F1bĈ#FtZ}##F1bĈleHGܣ@ٝ \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_149_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_149_1.dat new file mode 100755 index 0000000000..02a3cdc6ed --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_149_1.dat @@ -0,0 +1 @@ +x1 Ӕ_΅qH_Xci#Gd̘Ք՛gLU^ݮVR>dKVXbŊ+VXeoXJ_bŊ+VXb;ݙ+}Ŋ+VXbŊ+VAVngŊ+VXbŊ}+ +VXbŊVj>hewf*`uTq \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_153_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_153_1.dat new file mode 100755 index 0000000000..2abfca20bf --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_153_1.dat @@ -0,0 +1,2 @@ +x1 +0\9btEc'HH9efߞmffM#.̘1cƌ3f̘1cf73f̘g̘1cƌ3f̘1co2c]?3f̘1cƌ3f5Mf3f̘1cƌ3f̘17utf3f̘1cƌ3f̘=lj3>V \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_157_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_157_1.dat new file mode 100755 index 0000000000..17344b8911 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_157_1.dat @@ -0,0 +1,2 @@ +x1 + >94Sd/51V)SkJv7eGcǎ;vرc]Zٱc'رcǎ;vر+رg;vرcǎ;}V`N+رcǎ;v:;v;vرcǎ;;}Vޱcǎ;vص'vz#;]klwoA` \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_161_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_161_1.dat new file mode 100755 index 0000000000..669ade1b84 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_161_1.dat @@ -0,0 +1 @@ +x10_΅Xš yi~Qbkvp7'M u=]([ 2dȐ +\' 2 2dȐ!C 2s0/3d() 2dȐ!C 241dh 2dȐ!C 2dhcȐSL2eʔ)SL2M SLSL2eʔ)SL2M}LSSL2eʔ)SLeSy)SŔ)SL2eʔ)S;ٔ)S;)SL2eʔ)Sv()Sv()SL2eʔ)SLdT6}a*3mljmzC' \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_173_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_173_1.dat new file mode 100755 index 0000000000..436918c0bf --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_173_1.dat @@ -0,0 +1 @@ +x1 Ӕ_Υ''@y]X1?"g:1犝fn˶˻mm.?lٲe˖-F>glٲ2lٲe˖-[lٲeO`˖e˖-[lٲe˖-[l lٲlٲe˖-[lٲeVO`˖e˖-[lٲe˖-[z0}[z0y˖-[lٲe˖-[Ee[hOVWö=t*| \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_177_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_177_1.dat new file mode 100755 index 0000000000..12e2e522f9 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_177_1.dat @@ -0,0 +1 @@ +x1 Ep0X,a#r}6}nj~\8ƌ3f̘1cƌ7{3f,y3f̘1cƌ3fX_`X&3f̘1cƌ3f̘M_1cy̘1cƌ3f̘1cƌ+3f,y3f̘1cƌ3fX_bX&3f̘1cƌ3fx2dX'x[cy| 3 \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_21_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_21_1.dat new file mode 100755 index 0000000000000000000000000000000000000000..f87e0a11227bb4e1e6f46a1dbb133ca9b1cc8109 GIT binary patch literal 42 ycmb=pWo*#E^%B$z4|g)+XrS~G>tDQj I0gJJp;@7?u-2eap literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_33_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_33_1.dat new file mode 100755 index 0000000000000000000000000000000000000000..318949df07745528a680a428ac74ea85f016c2aa GIT binary patch literal 53 zcmb=pWo*#E(KTk=YPW25x~DW1ZnK;yQ#k{?W*9i^mo(k2MWv7BXK JDEWV$2>`Yr5WxTd literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_37_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_37_1.dat new file mode 100755 index 0000000000000000000000000000000000000000..5bd9e3aa0d08230508938e61404749c9ae72e32d GIT binary patch literal 56 zcmb=pWo*#Ec0ARA$6JzFue`IJvZq{C68loc~P O{VIfV)fhyX>>B}kGZlIO literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_45_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_45_1.dat new file mode 100755 index 0000000000000000000000000000000000000000..b35c567dca6aa84fbd8758e1fba4ba2feb397de2 GIT binary patch literal 82 zcmV-Y0ImOc+A}bK0z+KD00FoRNC61dbTS*2PX)7SsT!xdk(^GV*-*`fM1fJW2ff*Z o(g~bDg3KOGC-gI$Q0Yx=v#FI%h$&--irG{SZ{o}b0N2uhd5F*?p8x;= literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_49_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_49_1.dat new file mode 100755 index 0000000000000000000000000000000000000000..d20d71710eecee1842e3fc1535a09df5a9bc8761 GIT binary patch literal 84 zcmV-a0IUCa+A}bK0z-Vj00FoRNCgNTbhiUtgi3bMLkC`eAbEuhJD>&_5)DS}7`1~) qJBUfUaFG#i$7s45S#}Up3)038>ZV;{>!_h=2TkIGcsl^2!^he0dm&5! literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_53_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_53_1.dat new file mode 100755 index 0000000000000000000000000000000000000000..a676d7dfa2092a94b7a81322980eb4def98fba74 GIT binary patch literal 87 zcmV-d0I2_X+A}bK0z*Q;00FoR$N>ml185hf7!~cJr!Ip2Lh>4!c0mm>Bngb#HEP!o tw~N^P1s5B&Ycw5>+BN9xBDS?bOS@>1zes5V31pqmJ6B4Gdk literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_57_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_57_1.dat new file mode 100755 index 0000000000000000000000000000000000000000..896ed43575bbcdf0ad49fbfabe490f0645a69df4 GIT binary patch literal 92 zcmV-i0HgnS+A}bK0z+be0RnItkP8qx2Zf#3M5%1&K=cx?F0b9ifGvGnJH}m literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_61_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_61_1.dat new file mode 100755 index 0000000000..4165a4bd2e --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_61_1.dat @@ -0,0 +1 @@ +x30CbpPi`@&H^nadQG{nF3s~{gDf@VF{6Pqb^n%N<(VOM!% z$k4$xhh-EO4838Mg8>5u3?CSjTRkcmt{T`$3>YwEH7H+JtRd!Bt3S?PhR}V`4GpQu FA{XR~DnI}L literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_69_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_69_1.dat new file mode 100755 index 0000000000000000000000000000000000000000..03bba657f6d0df2a65ab63bc13715660ffc4ba1b GIT binary patch literal 102 zcmV-s0Ga=I+U?gd3IHGoh2g!^*!?fpBGuB6Ru=tl734z%QACqxBP~f*q;+|~GOlg3 z+|*@yF5PGrS+r?(1q&7|Sg<^6vEHLv!?NAN?qb1$1ZaPm@r{VZ}QxyUc>an#ExUago&HU9mj+T)Arjuh3Vye^8R7`ZVIqZ Kep3TS=JO^`WH?p; literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_77_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_77_1.dat new file mode 100755 index 0000000000000000000000000000000000000000..0fe0b03e868a00c5cc8f97a40528a7873b189a78 GIT binary patch literal 110 zcmV-!0FnQA+U?je4ge4gh2h<4>i(Cr2nH6ODE>E!G+%@^VU`nTV>Q8)snssB^}WNL zdu^VpO`ltHbYfdB9wots4cnKt)&5pivAMDF7O-K%#>`?aFte~>!}hvu_4ni(Cr2nG^~hc@}&Xs9m@nlP)$C$XMn>hy{MjVt$j z{gE+3#iKqBt-N5Y8)R89V#J7X)2MhK+d4+qM(zwoj2JO8ubJ1(Ym68%VvJ~1{NnyH UdYUt;KT?e16B)r+06TKr<%ttFF8}}l literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_85_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_85_1.dat new file mode 100755 index 0000000000..b8a20c7543 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_85_1.dat @@ -0,0 +1,2 @@ +x1 + н\]4AA hM\QjsAkUjmun2RΚ5:k;jƲ[eo[o[kZ \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_89_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_89_1.dat new file mode 100755 index 0000000000..e9d226f35f --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_89_1.dat @@ -0,0 +1 @@ +x1 Ӕ_΅Nh}%@ iDOH*c"_.4Iy킎`)-5*(of[sm}6YM ;;;;;G{zطz1vw}=wuL%?"=~ei \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_97_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_97_1.dat new file mode 100755 index 0000000000..24fa60fc31 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_97_1.dat @@ -0,0 +1,2 @@ +x1 +0н1\tncKD"H$DH$D"Q&WerH$D"*x[(?/'nd \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_117_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_117_2.dat new file mode 100755 index 0000000000..b4dcce46f7 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_117_2.dat @@ -0,0 +1,2 @@ +x1 + >94!m dOs\0X,la5#E>Z[ַRT*JR?Q-*T*JR?UW*JRTݟ+JRԤ~m5;S&+ \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_121_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_121_2.dat new file mode 100755 index 0000000000000000000000000000000000000000..a2a0097b1fd803424ccf0a741277066f44b04dae GIT binary patch literal 127 zcmb=p_4c+Q7lR@X%YlD!!vFtUN&Z3X+ Wr*l6!Q#;$o?ELu?!b)#0qyqpqZ94e? literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_125_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_125_2.dat new file mode 100755 index 0000000000..0ea40fdaec --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_125_2.dat @@ -0,0 +1 @@ +x! PӔ_@ U(kp@^Mڮ5-:VF_\t:NtyNqt:NtG;Nt:.8:NtzA}yNq;+n& \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_129_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_129_2.dat new file mode 100755 index 0000000000..bf048394b6 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_129_2.dat @@ -0,0 +1,2 @@ +x1 +0н_KVڡ'.!w]A0X~  !࣠fK# xFy4 vey@^+  ~  L#veI \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_133_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_133_2.dat new file mode 100755 index 0000000000..9e78b6de5d --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_133_2.dat @@ -0,0 +1,10 @@ +x1 + н&`LQ-g=Aqbʪl fƄȚ44& )OȚYF4444444444c4~9S:3ЌטpǮ> \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_145_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_145_2.dat new file mode 100755 index 0000000000..9ff2bbf3f4 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_145_2.dat @@ -0,0 +1,4 @@ +x1 + нr] +,tQ^&C~ +щj~mɾ.FgMDDDDDDDDDDDST׈DHdZL+ɴDDDDDDDDDDD2-'"""""""":BתEYDd \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_149_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_149_2.dat new file mode 100755 index 0000000000..d52e0484b2 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_149_2.dat @@ -0,0 +1 @@ +x;@/gcaGBXB'-ˆouէUQdRVOmT*ǫ;;j廝Ee2PQQQQQQQQQQQ TTTTTTTTTTTTr33R &Tskz_e2P=d \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_153_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_153_2.dat new file mode 100755 index 0000000000..3b06041019 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_153_2.dat @@ -0,0 +1,2 @@ +x1 +0 Ӥ8ZP!BZu賶"bu*)]MFFFFFFFFFFFF%= #ddddddddddddr ot2yFFFFFFFFFFFF& #k5L 2222222222(Y7"d@H \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_157_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_157_2.dat new file mode 100755 index 0000000000..2baf535e27 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_157_2.dat @@ -0,0 +1,3 @@ +x1 +0>s6MqUH1X&U̘f/u-'.[KGGGGGGGGGGH|NG(ttttttttttNF;::::::::::}Nz$ +>n A#^AG(t =3{ \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_161_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_161_2.dat new file mode 100755 index 0000000000000000000000000000000000000000..d2df7594eafccef2dacee35dfd7b2fa20d22e1dc GIT binary patch literal 190 zcmb=p_4bY;UxNV;Yry%;UBBb`j2ojEHvT!3{p!RC?nRON7i3-Yc;zkY`Xf0}_x!$! zFE2cT6SG?D4qx=zQRnO+ECdE-0&RD4IXDluF5^{1fQ4roCj>lm@IU~K;tl%3r)17J zJ0ZZ#=G-X(&k}A4K#T&Dr(`%dQ~6pDpuA;fz2pBjgY@glDn4Sf%vz*>v-~PkmIDA9 Cq)A-> literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_165_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_165_2.dat new file mode 100755 index 0000000000..2e6cd7c6f0 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_165_2.dat @@ -0,0 +1,2 @@ +x1 +0 Ӥ?BVUG%*+_fs MIIIIIIIII2d;l4()))))))))))eqJنIDIIIIIIIIIII)۠mPRRRRRRRRRRR6l JJJJJJJJJJJJن}RaQRRRRRRRRRRNeK?R퐔͔&W3U \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_169_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_169_2.dat new file mode 100755 index 0000000000000000000000000000000000000000..4052062b8db42699eecf910b9792afd64d0db84b GIT binary patch literal 196 zcmb=p_4ckI*8v3{)(d-=cYfa=BffC9bK#GR;+rC$JQO$)A7z^D@-5td;v_x$KZRRf zZ@YKti&y>Si-EiLS+DR!2HU-y%YuV0IWJizqKN=2d5((tdS!V{oP+?U0&*@cSz;s- xgbbohG*|MpPC)`8ZBAZV&W8R7OY$eT{eSz+A@fq6m)EU$prLMGxEX}SGyvz^RrCM= literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_173_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_173_2.dat new file mode 100755 index 0000000000..0a30ba530c --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_173_2.dat @@ -0,0 +1 @@ +x+@ Pift:>y &d U߬S[]5Z;a5V۞A[Z˴VՃI0ZZZZZZZZZZZZZZ=-Lhi`VFK?ݧhioJ0}o \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_177_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_177_2.dat new file mode 100755 index 0000000000..d2c52f9909 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_177_2.dat @@ -0,0 +1,2 @@ +x1 + E>Y4V$~ ,C&U;Ook5bϙGx9%&&&&&&&&&&&n$OL|v#&&&&&&&&&&&&&bbݍXw#&l7bbbbbbbbbbbbbbM"l7bbbbbbbbbbbbbbMa!&݈3)U0}qMK$nTp4?}1s?+4l5MclS literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_29_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_29_2.dat new file mode 100755 index 0000000000000000000000000000000000000000..5112d11eff0ce0631ba1000e8823e0592ee3230e GIT binary patch literal 45 zcmb=pWo*#EnW&mR* B5Pkpv literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_33_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_33_2.dat new file mode 100755 index 0000000000000000000000000000000000000000..5bac0c80d2fd997d753427178c6294dd3dd9184a GIT binary patch literal 47 zcmb=pWo*#E*F> \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_45_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_45_2.dat new file mode 100755 index 0000000000000000000000000000000000000000..ad44ff1885f2c4e0d2158824e4d2b5537e0e8eb5 GIT binary patch literal 68 zcmV-K0K5Nq+A}bK0z=#g4!8^kw`w#Iid7Q>h%(!dOx19XArWX)^>9~>HN_4))uS;# aNK_M3B2!g0&U7-YR1co;#)SYYI)8L$SQ+gA literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_49_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_49_2.dat new file mode 100755 index 0000000000000000000000000000000000000000..6e8edff24b9344b19623318f9e10802439cf2b40 GIT binary patch literal 70 zcmV-M0J;Bo+A}bK0zFeF c>OklqvR0#s4%}&Xxa%04@qrTo0MEO|#KK}86#xJL literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_53_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_53_2.dat new file mode 100755 index 0000000000000000000000000000000000000000..682cae2aa6532baf02d2aa3e0c1dd2ab5227a34e GIT binary patch literal 73 zcmV-P0Ji^l+A}bK0z*Ow32+$&77oAwesj8DyK(d{NRL}|M7?Oxab&l#B)j2eE;wiyK ib&jT?QJteYsi%|F)*Fp=5-1Z!bq=xk#E$^gB4-2t4I$0| literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_61_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_61_2.dat new file mode 100755 index 0000000000000000000000000000000000000000..77d3815eb51a7feb77ebf8fa7001e8428c252db7 GIT binary patch literal 78 zcmb=pWo*#E&42|mGA0{eU)ahq{&Zsci5AoT|sbCRL;^iky?c_QPW#8n#wmU scCwG!AOHf}SdL4%7jXKBFflomDSq>=(yS;mZJM;_nealU{J-+60Ag+8uB49o zCeM)i$;BWcAt6nrP(L9Zkl0KlBqVyA9!Ek#%9h06kgHO>pO7@^?R^TT0-T8OPY@d{ Aga7~l literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_77_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_77_2.dat new file mode 100755 index 0000000000..903cba4a0f --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_77_2.dat @@ -0,0 +1 @@ +x1 н_CM>Gt ѫe+FWZEm&gއFѶhF+t/FYvFj[*7a \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_81_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_81_2.dat new file mode 100755 index 0000000000..17a9ac2a1e --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_81_2.dat @@ -0,0 +1,2 @@ +x1 +0н_KҩVi!O\"A]:xbW1uȦ&_T ΋6H$U^D~bׯb=gX \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_85_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_85_2.dat new file mode 100755 index 0000000000..72c74ff9ad --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_85_2.dat @@ -0,0 +1,2 @@ +x1 +0=1\B7O$A0$8Wwjguu槊RT*uS֧JRTJRRޢN浘V \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_89_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_89_2.dat new file mode 100755 index 0000000000..06c9a4fe34 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_89_2.dat @@ -0,0 +1 @@ +xٱ 0 >/&E*cQqŃ zf$rM6hu5#zF6@yLURBPN&o9dL&(sq|@niI{j|F;se@4&-NL5N8P;_ J=`Rs#W7tsZG`0W$ literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_97_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_97_2.dat new file mode 100755 index 0000000000..38842b9899 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_97_2.dat @@ -0,0 +1,2 @@ +x1 +0н_KivH4 )_%s_d3KO1^aL,$H"$KzRPt[I&X9$H"$I$ysI$DI$ɓI$I$Dɍ%es!=LAZ5'̓IVrn/2oƅ \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_113_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_113_3.dat new file mode 100755 index 0000000000..023b27304d --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_113_3.dat @@ -0,0 +1,2 @@ +xA +0 D}NrnDFj2KCt?WݲZi.qoP %Smj7ަ:*N:@:***fW9d2*j*}S@`*j৪6Jlѿ}}էTUa24hnt \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_117_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_117_3.dat new file mode 100755 index 0000000000..79cc04d145 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_117_3.dat @@ -0,0 +1,4 @@ +x1 +0 ]Q.xIB$?~!-~Ih^HVEgKLuZlO;cUwJKHSx)|O2B zFzfxyYi|AgTNeL2=}VZ;Ev?R7P^rty{%*guOnUQ^gxskvcVQYX$3@&ONuIXHIa)Pv zd*)?`uy^M1DbmmD%Zo4AT@Sf`A^-TAmugk}r7i9rS@ZJQ!dsI6-_8j2Oj_vmiRt9J G3$_65-fl?% literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_125_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_125_3.dat new file mode 100755 index 0000000000..e2febdbdd1 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_125_3.dat @@ -0,0 +1,2 @@ +x1 + ~N3rۄ]BБ'?xFrjdA{$T!&?~Jee~GOn@z#E)RHZ@bl-)ݿ<ߧ*OUR"5&5*ie J]+ \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_145_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_145_3.dat new file mode 100755 index 0000000000..338b7e7a85 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_145_3.dat @@ -0,0 +1,3 @@ +x +@|:^ Jy̡yMj-' +9VS֦K9e)PyUwe-m jԨQF5jԨRi٫F4_wk}0+jRBRF5jԨQeOMBJHjԨQF5jwP״˪IH I5jԨQFͳc w5jԨQF:zS*2UZ_C*e_OZ%dIȯb \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_149_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_149_3.dat new file mode 100755 index 0000000000..30bc5fabcd --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_149_3.dat @@ -0,0 +1 @@ +xA0}Oܠ⦐H頯'Z2{oV|Ι%>yR{!8ÂI+JpI|#f5κ[P A $H Q})&X{ט+Wb`I)5%d \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_153_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_153_3.dat new file mode 100755 index 0000000000..89cdec031f --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_153_3.dat @@ -0,0 +1,2 @@ +xA +0}Ns˹)7mJ,}8X=cW^GeNE;R(13DFEx15%o)(+gL2|3uiEv#I;;|4`apdpkS*^~xvLf_IwePW`Q`R8jLgJQhVasX}og%bb( literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_161_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_161_3.dat new file mode 100755 index 0000000000..72a26a4fc9 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_161_3.dat @@ -0,0 +1,3 @@ +xA +0}NrnFTk,N Zђf7J:ƒ^`WٔUnXڤǧM-#V+ߡ+9zҖY l)SLocʱO_C?ߩ%;*KlM2eʔ)SL2Ye][bSL2eʔ)SLyVYlWG[bSL2eʔ)SL +]g+$eʔ)SL2eʔ/qBbKlʔ)SL2eqʙ]£{A/~V9\%[P#' \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_165_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_165_3.dat new file mode 100755 index 0000000000..870af8f4f0 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_165_3.dat @@ -0,0 +1,2 @@ +xA +0}N3T #Z YuÏ:̛1ϱEUTT|S q)m-sG B.Cĉ'N8qğwv7['tg!.ե:qĉ'N8qv`_+.ե:qĉ'N8qv`R8qĉ'N8qΤ8"ե:qĉ'N8qĝIяہq&EKuĉ'N8q+9:}kFT?^ЏGo<0իCg/_ \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_169_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_169_3.dat new file mode 100755 index 0000000000..9431095263 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_169_3.dat @@ -0,0 +1 @@ +xA0}O3rn񷾦 )}'31Eh]4P[ _]Kv<˙fp#y_l[geӧO>}o%uJV/{%O>}ӧO}K~O>}ӧO>Q=/ї>}ӧO>}u{ח>}ӧO>}u{蟪/%?}ӧO>}ׯ.N4჏VMmRt(1| \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_177_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_177_3.dat new file mode 100755 index 0000000000000000000000000000000000000000..9586979a1f9d74c4e115757feeeabb74819df45b GIT binary patch literal 312 zcmb=p_4ckK*C7KDmJ79hlkfa5%Jb^v=;WB?Q=`hguxbC(hf6kRuDyQoTio}+UFQXv zzYC}H-0`hx>pHl#M}PCVx?h`vgGKGt$xx|wRV!3 zfoMp8BP)#fxJc>g$uD7oUs9|>H)gE5sHrt=^#lc>7A~z73z`r_mvKnO#w`)M&Z=su zbg*n(k1PjN^&?s|N=H{r^nS3Sqe@3D+;oUNM?Y$9iHOp<`z#Ekc_EUqAPXMxfb>qw zEAHgtRC0WyjcRYJR@RjRYnSlaR2M&;SL^DVy)i4Q+x2LaXy<=XYjqD_?+J5e|6#~G HtjGiaa;}E0 literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_21_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_21_3.dat new file mode 100755 index 0000000000000000000000000000000000000000..bcb4eec4919b60e2c25c4e18ebb9c34423536b63 GIT binary patch literal 60 zcmb=pWo*#EQOIfiJ63sVS+xNu1fplLAA habXVvLtL={H4TR*l1wwiWg5b#xHRE54FHDyc9;xW9FqV5 literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_29_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_29_3.dat new file mode 100755 index 0000000000000000000000000000000000000000..6150ac1289809bad7646c662dd9cf836311f47f8 GIT binary patch literal 75 zcmV-R0JQ&j+A}bK0z)JO16&9u7I`jwl1Q?&GZ`D8$YeOr5DVhME(}Fn_#_QSO-42g hU)nIl9)oZ$?ieJ~WF$A^k;Gv#QfT0j2LK0SpN7|C8+8Bx literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_33_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_33_3.dat new file mode 100755 index 0000000000000000000000000000000000000000..6053b5e396ca957ae26193aceb85d08a43dacd9c GIT binary patch literal 79 zcmV-V0I>gf+A}bK0z(W00bB+MfK>sq3hWAyRM5v3T!1RJz`2Gv5EpJKDB>cdVAPhO lW(z#c;7?-^TkxkbL)`g=Dz?D=LP!BVTMUV{1pswi&V8ytAOipZ literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_37_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_37_3.dat new file mode 100755 index 0000000000000000000000000000000000000000..5dea5b9cbe14fcff482e0fe9a647359aca07547b GIT binary patch literal 83 zcmV-Z0IdIb+A}bK0z+&F3b+gq0H-2kRd@{JG8i};@d2vZ2XK(!(BO=uY7eM-(E6p3Zv0bsQxl5nw^V$$JC%}Bqlsj9DvDo{aE;P>E# hYO*WtyB%t`BNs_$5b6wWqgwwJ)C%Io3LZM{#-RD-KY#!L literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_53_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_53_3.dat new file mode 100755 index 0000000000..8800beab1a --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_53_3.dat @@ -0,0 +1,2 @@ +xK +0Ds ! -(.Bp&|"-t&`qQ-"9_+)Be/H8D%a~}spKFN=,;;a^t4\FSN \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_57_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_57_3.dat new file mode 100755 index 0000000000000000000000000000000000000000..4e1e5da386a57dacaf76f93d05473ff922574488 GIT binary patch literal 126 zcmV-^0D=E_+U=Gx3cxT719!hh{{QE?gyNwnmS{-_8xJAm$PtVPuqu0fSCq8`R#F2C z`Jye^+}fgNtCnh;u5A$xa)x*4$m(#xu^rp7Rcwt+;I=%SqH*TgTz7#%(6RO)$6#_BUJmZJUz-cM|kVFF3Ae8Lopr+yDRo literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_61_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_61_3.dat new file mode 100755 index 0000000000..bf1a3cc7f5 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_61_3.dat @@ -0,0 +1,2 @@ +xA +0fz4-%*dp!yZܫu(~=&ۓ)R2"/"<9FΊ=rb"/rw"2B#3-0-KW \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_65_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_65_3.dat new file mode 100755 index 0000000000..85892089bf --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_65_3.dat @@ -0,0 +1,2 @@ +xQ + D4\?R ,!O-Nv1:cZu "UMÕF ~jK-la[^q^Q\=o-laZpUB @IKJzɢ|1Í  \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_69_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_69_3.dat new file mode 100755 index 0000000000..55318a87be --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_69_3.dat @@ -0,0 +1,2 @@ +x +0 {&2'd l=,Fy;$쇤WE-R:%T,O2g"",Ȣ/DyĈɧ{O䮳",:NvEWN#(&,,]x؅ \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_73_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_73_3.dat new file mode 100755 index 0000000000..15be77f64b --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_73_3.dat @@ -0,0 +1,2 @@ +xQ +0 C{g;JJ?dԬK=RasJhTJ6exka\$nIE,-/XB*х=wee4t̒tLщtt߫b gFf qoddn-? \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_77_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_77_3.dat new file mode 100755 index 0000000000..ec78280442 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_77_3.dat @@ -0,0 +1,2 @@ +xA +0 &BiRaK"t`I@|fXyilE:Sza18GifK*?:YC1쌞졘(ቷJ*jl*TRIKR^ؙks)c)c)JZa \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_81_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_81_3.dat new file mode 100755 index 0000000000..47bc0f7930 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_81_3.dat @@ -0,0 +1,2 @@ +x1 + F=\,JGAġhj>#3X:kԹ\FM Jhu3>TZ{PSgP'kVjժU_ۯUV=P oO:Wҝj[Wxm 5 \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_85_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_85_3.dat new file mode 100755 index 0000000000000000000000000000000000000000..02c4f8cdbbe43a99de71a77162d811d36ad4f51b GIT binary patch literal 160 zcmV;R0AK%j+U?m(3V<*SMbUXy^Sb}dIVrU|sn!>N+|q$Y2;pF8P^qqRd9N$FYg^Jj ztFhS7{Q{@rM+N5MyIOtAC{U6F+M{SU#kj+puI|W4iU0x#Ab>!sfcG@40`85I_KdmkG#TsrP@uMqv1rdge4##%ZWgv6J(FGUkB?1g^dV O%IE@ppbHjj!&bK*zf9i% literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_89_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_89_3.dat new file mode 100755 index 0000000000..2b4cb59fcd --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_89_3.dat @@ -0,0 +1,2 @@ +x1 ὧ) *.@U |eŵ6ۢw5*) oiK4nk>1}d>@ 4XYCo ۡ1<AhFt + 4@51Wr>7G}}x7|NgN \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_93_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_93_3.dat new file mode 100755 index 0000000000..b4cc8a971d --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_93_3.dat @@ -0,0 +1,2 @@ +xA +0 D}NrnJɪQ~B06na<<ׇe6MRCP L̓i9M 2 LkŮdDv*"aXjBdAddZTdAdqY0exqeN&WVQvc \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_97_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_97_3.dat new file mode 100755 index 0000000000000000000000000000000000000000..7adc9ebaa77cfc3c6f7a54307a831fbeee0c87db GIT binary patch literal 175 zcmV;g08syU+U?rG3V&G`N=*G0-MD(Ga{FCK!(&5t$)O3EWmo!5xs*ixXZ zVA@1zxl+BnrYNa(r@cz9VNyyDD4l1cxytAa-_m)8y8uckp@b4jD53OrC10xSQX0*J zF=dt7h5IPcalDsMLJ1|5P(o>6B~s-^N^~6WC6rJ?2_=+%t0e0VeE$jDlr;7Ux}BHg dPL&$iL~p8;J5{b%n)WQo-EsPG#~oB@^PHSYS7`tM literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_101_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_101_4.dat new file mode 100755 index 0000000000..1c97dc0483 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_101_4.dat @@ -0,0 +1,2 @@ +xA Fs^1bИ]4m+8+Ve^HR]\c +oWN#X+l HEcp \^.9qW9":.BB \0aPǨcp \ONqjpG}}$.˅ \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_105_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_105_4.dat new file mode 100755 index 0000000000..0211cdb3ae --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_105_4.dat @@ -0,0 +1,2 @@ +xK +0 D=Mr˹A TeEFL2 #鹢_I!딤Ѻ-իkmO]sS T6*'8 N$'NZ^XXh%Zrr$W}rU*G9r|c[cN[_=׫5^J 1*qv \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_117_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_117_4.dat new file mode 100755 index 0000000000..386725918a --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_117_4.dat @@ -0,0 +1,2 @@ +x + н_s]4Dgn2Jj}ҾRsSWGRɧ)5Em#ܯk_"z3\rʕ+r Lk|/{;'jk^zGw=K_GJACF5KEX*@<$z}?p^YqOrD6l{pq{+`&l7JcHsOy z_GM@Ox_x?b=y>Jqe}Ao?uX%DM`IXJr{-ZaqKAvlH`*6wj&*7((AW$y+_m1`VYgIgEyrCkW+Vc literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_125_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_125_4.dat new file mode 100755 index 0000000000000000000000000000000000000000..b98dc813afe773cdcd71a8779391ca295c0d3a51 GIT binary patch literal 213 zcmb=p_4c+S-ysD7)(f`F8-K^2;|+^RST%8G)>;-rsHGtPebu=wvE(Rqud?@Jdiv-z32@0r%Fa7(4|DUhVIr-8&`h2IF>D|wlymv&u|5gc>1uLtXBe(o`B(jjL zU;pLDUo3v_shtNkChn^3Ki;iZZS8BT4}Mgedi!xzR{!b=ahL8^+0UC-$jr|$aq=Dj DA0&Li literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_129_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_129_4.dat new file mode 100755 index 0000000000000000000000000000000000000000..8ecfa250845e8ef1add89a66c88fc9f31250a436 GIT binary patch literal 220 zcmb=p_4c+kU$cP#%Y}Q(H@>gmX_E0-a3R;c&@UP>6Fhnzw0;$t`MGk<)@^G`dv2;9 zJa>BiGETqum$vWzzWVcDA(y^F$>O^;cE3bkZ7!_If6o7~R(2)#^;-&#|1=8@FtW=O zT)5zvG5y;kg&7OBX?Mm(S)NeKczodZftUvk%zPFg;tOl7WBdj2GpxmpM2j^4FA?}L o{bhTz9Ot|H{BduueXdPB_S/#\p\p># \p\p#>qp\p.$Iq dGR_4  \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_137_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_137_4.dat new file mode 100755 index 0000000000000000000000000000000000000000..0c09c487c2e85488f8caf2dcc863e260cfd077d4 GIT binary patch literal 248 zcmb=p_4bYxEjs$#?!2-R}~*>9$YZ>m}P8_SPSlc>J(ZYVC8? zO1oo*K1q`<^UtZRwccRy_UiY^lc)4;t3SOfES@w`rW=l2^YbTRO$JKN#b*jQw`{%rV(;`b$vL{4 zXRQkQ#dOtY-ox+n-*;b@wS2d1)2vU1rN&=NV*fwfSo>q^jJZKc}*2a`@ScXA0|@PM!+Z)$&&j+uR`i+&96LlQothXU{Gd06E`pF literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_145_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_145_4.dat new file mode 100755 index 0000000000000000000000000000000000000000..33fb211249fd13955f9481a5af91416b0850c79f GIT binary patch literal 255 zcmb=p_4bYjXH=f@| Kjo |SSWKZm׌j\Ъ2 W\qW\q"~ jvtv_\qW\qW\q%g3 }+++ r9ArW\qW\qŕA g3WA W\qW\qW]V~v{D3Ȝ!\W^Tڍ[S7vۜgq? +{peo383838{YXz,_OYfe3s38383\C!Ms38383r \C?37938383\C!07M8383q,mMrskWv3~W WB \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_157_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_157_4.dat new file mode 100755 index 0000000000..ad5fcf69de --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_157_4.dat @@ -0,0 +1 @@ +x10ޯs4"FP=iRX¢X0멪u 4ftl}m➭S|юS P5<]rwqwq^QN6ÏZsߙ,wqwqǝ>Μ5g;;Y}Vgw,wqwqw>9wqwq>3gY;[ww?P3Ƙggt퐮;].3w4A \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_161_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_161_4.dat new file mode 100755 index 0000000000..7604c4540a --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_161_4.dat @@ -0,0 +1 @@ +xA@ fs!AL_|,4l)iml׉0' +E ]N\x#2/_{7g9쏼ٷ}2r!?}-#Te9C9C9~6Sʇ겺!r!r!ۘse9C9C94_Ɯ|.r!r!s/s0 2r!r8}DwrDXΡ|x|!2 \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_165_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_165_4.dat new file mode 100755 index 0000000000..d83d631656 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_165_4.dat @@ -0,0 +1,3 @@ +xA +1 }Or."*?fPLHIkΫZQ8 +Gyqk-n5+?|֎kKnEŹK.K.?2.|EJ{2<:.Ku\K.K.ǝmu)_8\r%\r%\Џ;'2!_8\r%\r%\Џ;'2!_\r%\r%\rinC?nn9 RK.K.;.HqY'ݽNF?K㕢,R| My*3 \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_169_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_169_4.dat new file mode 100755 index 0000000000000000000000000000000000000000..4aac95c1e3fc9ba5257f331564b211fab568c376 GIT binary patch literal 297 zcmb=p_4bw{7n30m>xEkX%lG~l?V3;+6CwFBVc+Mpz`!LezkNJB-=9zK3Cy|6dc7)c z$=5@7j?a{Q`J_AleCn>hU3Whz>;A1NtE#vC#=Q1!Zp)m1?|$5V9DCCK-|1Vc*mtaT zocL^x-SRm_r!-;2ueFy?#eVr#8L;Ge>5J}i?kE{?B4PtL69K8~&|}TJq5E>1FRHmwz3+$1r7q GdJ_PDBBetB literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_173_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_173_4.dat new file mode 100755 index 0000000000..9df4d865d4 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_173_4.dat @@ -0,0 +1,2 @@ +xK +1}Nrna ~ZY!Jt^5(/jkz[pj_?~v:|jwՖ_mXzo6?n%^4`7 P=;q=RrH8oyopw{-jJFsR literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_25_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_25_4.dat new file mode 100755 index 0000000000000000000000000000000000000000..0c7c44bbb5bb50a1f060373ea3ca14d083fe686e GIT binary patch literal 76 zcmV-S0JHyi+A}af07FAV1c%Fjuna^PAxDf1Irg9fvh0EKpc;@!E_4AniAM&vX|%Qn i>NlJ|HN*%VIGY$5BJ4r(4;Eo;_F$1gmjnRDU3Su2%N^nX literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_29_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_29_4.dat new file mode 100755 index 0000000000000000000000000000000000000000..c28dc20e8248d196503c88e3e58b9048de750a16 GIT binary patch literal 78 zcmV-U0I~mg+A}af07FAVI0*(^2E@r?NRc2*iM`kWMfO73Na9E&7dAmSNkA6A$s@yF k1DFWbSimBS8G{geiIFARUKIc0kiuy%rYtrE0Gke<<${17%K!iX literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_33_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_33_4.dat new file mode 100755 index 0000000000000000000000000000000000000000..5834b6fb5672ddd54227a5229ad248efb56f9931 GIT binary patch literal 89 zcmV-f0H*(V+A}af07FAV1PK%5G9X?BinJkI0ZA&T;SOAYD(-+P$1ng+a^aGIlSHT> vY|E%S29rDBDF$Z>!lD9ODl{aRP7U#;Q^M{*2@gEd1l)nH0+%8HgW}HuoFyU1 literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_37_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_37_4.dat new file mode 100755 index 0000000000000000000000000000000000000000..4bf2e26ed0238cb7f0fa88b4a2e8d9f607c6c465 GIT binary patch literal 86 zcmV-c0IC0Y+A}af07FAVG!hE93`kRjsRW^pR8`b;7d}8$cR_hr3_>Eg@JYf+B2^J* sQ04GUMNxbc& v?i_XJs5{AZrvXfsV99_*CH~R|;!aXkQtVEY@FYSRQSQW4Nk}mOMgc#BT0kV* literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_45_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_45_4.dat new file mode 100755 index 0000000000000000000000000000000000000000..1b921f3009656cf429e67486e59237b8dd15ff5f GIT binary patch literal 120 zcmV-;0Ehp0+U=A<3IHJpMfaXY?|-@3gj12@{}N1#TnN<@9`Z@`Ei+qoFVPwup|*kU zpmqSgF*>cacXR`pI4*58TFBT1fxMG6XL7wac>{^GqK!9Q$bqGK7QU=)P)>BL}k8 eRu0aQtjeU@>+_-I`CuK_9H00P-1Gn#s>kM_-9E?w literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_53_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_53_4.dat new file mode 100755 index 0000000000000000000000000000000000000000..7e88826dd67c8eed58a77ecc7bd6326cd996537c GIT binary patch literal 128 zcmV-`0Du2@+U=H03cxT31^1pt?|->=VN(l5e9snI(1i&h1DSjzs%My4l0%60s2sV1 zUFvoM>Q$%8k$LTH-z$uZ_X`^svLKUJCMj-(<27Edc$MCl9xf#1?;NQB?uAn=uepcg i^-r(*&mns8lo?t9xVoWK-RMw{czy1?FmMC1XBA^pK|VMD literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_57_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_57_4.dat new file mode 100755 index 0000000000000000000000000000000000000000..84669c7d65c97c0b040497b32d3d3037c70d826e GIT binary patch literal 130 zcmV-|0Db>>+U=H04uCKSME9Hq?teKq4gQfCYGNWRShRhHSItD#wao0Xdx_S`3}%zb z)6ua+cp@ZJk!QRPPwS|R$7C*c-vtG{7f62E_H57pdBSPpo*a39C`!$ZCqdRc`@3z= k_I%kB_3cxT31n<5^|NpsON^2;!>e@qKa}XLb#Yw^v)q9v(qB=lyhR&g$ zouh+=UGEBA9-;T^egK`j*5&+4Z}?KB16>uQvCipDZ+dUw-O4os=_q+W=RtbJoA;dF m^rrXIyZXXjy>U8x$cKrH<+(z6K0*KBy}^0+esBPHn8I@q@JM_B literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_65_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_65_4.dat new file mode 100755 index 0000000000..c24343d947 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_65_4.dat @@ -0,0 +1,2 @@ +xK DtXЙ.4E1^hvOxW0 JHŻz[^܈[v +yyZk=`vcǃ<ȃaCe \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_81_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_81_4.dat new file mode 100755 index 0000000000..dd65216133 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_81_4.dat @@ -0,0 +1,3 @@ +xA +0 yMyXE m7"892ѸQ1ݳ+xx;t35DIY1x\:u}e/ #Th< +UBz<5G<5{G<5<饫>]Urxu \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_85_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_85_4.dat new file mode 100755 index 0000000000000000000000000000000000000000..c8d5123e26909726dbd71694e1f6697cad51529e GIT binary patch literal 154 zcmV;L0A>Gp+U=P!3cw%?MR!k=-2ZZ2N*jmT*5nJN&mmxtH#~v~YIZJnPs9LfrlsbP zk*TK+4nE7TrIxLs*SC2W7zhg5zDpNC7h5Ilh$5=S0ABHmSG?ji)1NN$BI-+pH9Di \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_97_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_97_4.dat new file mode 100755 index 0000000000000000000000000000000000000000..5d848caade31141b6a6d238828e0513940b9aec8 GIT binary patch literal 176 zcmb=p^>&scSAzi$%llg2U;ocnYTk;AQoMQf@D~ArLUH5VhzlL*U+;ClQn_{Ld4Rh8 z>uTAu_q(T_ELu@IVU2P7<-3>5gi15Bf~CWkN@uS0RjmncZrXE$dhO>e3gqq;cwFbMH2i4?0P>DhOaK4? literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_101_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_101_5.dat new file mode 100755 index 0000000000..c21869e83c --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_101_5.dat @@ -0,0 +1,2 @@ +x + E+%=M3Cbv ѬNkûgqkqq{%Oo,iKee3[|iVh]` ` 0ʕz˴T0Gu/q8F13:W>#ȕ0c0Q8E=F#+a X͞+cV%9W>Q]TkY-gLqD艋 \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_105_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_105_5.dat new file mode 100755 index 0000000000000000000000000000000000000000..bc8798c64015b6595105a07645625a572c839c0c GIT binary patch literal 224 zcmV<603ZK&+U=S_3dA4~1n<6P`v0HHC0WDb$gbEE0yT;RFVdN!?Sm0#$x+d(-M&MH zb#RhAbEHXz@r$NR=-U3QPci-P|L%XEWG%ZUmG!BgbTK0LXu8R?&L*=*eAagQlk8Qg zsjyG52YayhPWDE5FUx?-`>CeAIbz1%-fVQSH_Cfi23+1R)r9Zid)&FO2Yavwd*nUx z9(fPn!}qvzVGs6T5BA7=v+3UUic`wTHUd>xK aP2TVA&wEd3Dq=6}&i9BW^E?1$d?(3j3x3!D literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_109_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_109_5.dat new file mode 100755 index 0000000000000000000000000000000000000000..25a394402fb8b1d0ebf02b85473522a21235736d GIT binary patch literal 211 zcmb=p^>&scSAzi$%lk^-Pyf$XZV2j*s`A*zF?pKO&MOlNBNgB2F4Ef5es`YjsdLA3 z1GmLzcQ4u+7wuKIGk@pLkJ~=o2#t=t|EsmEP`E4gbGg?&F(KW|<)*sao-4A2zw@dk z3W3o7Ln%e~oL=noQ``Tq@Y3g3w~o|0t6%gi0ZRhqKTiJP`A5a7)9;Xq4;a-6*>}!& z0V-}7~CRaH?+kXgI=m1Q4e>}I|^`+f56tjphW?I(xcpB?4RQpn(Z H=>0 0 0 Ofz3=>0 0 0.3Z$׷8\pw4:Zp:qX 7 \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_121_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_121_5.dat new file mode 100755 index 0000000000000000000000000000000000000000..9bb5c4158981760f99c1831c03ccf11a664e6053 GIT binary patch literal 256 zcmb=p^>%h3*AWFCm;c4Hp8T7ByMNb~P0`w->X-Iu1WibCj$>!PxYtGF(Dx08H@Bz! ziL_Mtn}1F*CX2(7=TOEDEbWKm# z7n3IwXBH>TO_bgSLC0^-+rs}kq59sv;^g0Fytmmr`w)|MuCvr_`^DT6m?EIk`)9ws z`TOnWP2bl(=@oqXJ10zcK~pjRkXgEU#Yr9g&b-C9Uzp__hM;esZ;0LBZFwcK^7XPb o>!)^ZQ#aoWuYBxxGe&=>)49shxbEDe|L3eseDs4sW73s<0HpnlZvX%Q literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_125_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_125_5.dat new file mode 100755 index 0000000000..2161c50a45 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_125_5.dat @@ -0,0 +1,2 @@ +xA + E&fc;S$?؏Q4YahûyJ}9g==li.;nh_wz.qCWȧy uPk;<<<|*q, mkWqNl% yyyy^2䰅sX|aaaa3ϙ9lH<<<<̿웁[n`Tq8^vy \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_129_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_129_5.dat new file mode 100755 index 0000000000000000000000000000000000000000..f0c1d65020884c517fcba84b94191eaac350aaa5 GIT binary patch literal 259 zcmb=p^>(%)-(dqD=JNe+fBrw;rBWIY;l}PP(iGgY;6uuWT>&yOg;TY((|V7;4|rHz z$aHnxR5q=ee-{;mpS}6Fjz8)5|GBsR+cqxEkvegY{WD8>{;97#wkaFeC|)-I_*?FJ zdDOO73mlv68dxKM=*5vkTKXxHkABpxay&a{NAkk7uziscPg`}IW$x!MWZc)F|A2b~ z0~9@B{lrw+xCbP5#sezq5T~F&LHq>w52k$wdRTBP%uhX7dcHJE;G5lvJnQRETQ@vs deIFa2`wqnRlbR99G23{{r2|{+4tV*M007v5c@F>p literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_133_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_133_5.dat new file mode 100755 index 0000000000..46be8b0948 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_133_5.dat @@ -0,0 +1,2 @@ +xA +0 DѽOcr]4%1mCTxΜ[Dv={FEϏq?ݿ9keѭ}'2^c4G:3=JK-F0`#Hw'#<{~Z4 :BG舻F0`G~:`#?#tw-`#?##t#F0r}Q}eR;M/k1mX=hsH"k M:3qOW}9ԖIH1G;- sڶ?[%M + v#;zg^3d}69Ψޙ@7҄#gv`;׳ީ\$wlv`v;ލ}7wߑa;vkA#gv`=N2wxgWӤ@n?c}SQ:Zd?+9vz)P \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_141_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_141_5.dat new file mode 100755 index 0000000000000000000000000000000000000000..60c1a8e81acb9a104b1003fe8c362588b053abac GIT binary patch literal 297 zcmb=p_4dw1u0sYqtq*_O`hNbW|NLx;(ly@Fw~{rHnwy%g%&1?U=@uQmHZAgYPUWvV zSwa8P%uLts{p{|I6Ff?XUZ>?fOlh?Hu<*Lhj^j-g&%KHRklY zHSXJ%GR-^MUl+M`k&@0?^TfhtO`k-r;2DQRmi;?;$r6o)IJ{82> zzVP^R$E^#0m%8OX_%_jPZqDCVlYVV;i{Ag?{rvsKSBqQ(C$718!)g|LXx`R)LEA3< z-^TpyQqr?GFC*0AGYu!M2uzSo(E^bzLvI+;8@4U$WERT)%VrUu|y7`j4jj zp4qEF>;UTg{AOd_*}XaEC*vs]Ŝ_{W!zﶬ/)˙v V6V޻,f1Yb n^o>\O],,b,f1YyVgYYYb,f1+ʳ<˳<˳1YbŬ<+ʳ,,b,f1YyVgYYYb,fukys77}vmb=wsw)tW: \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_161_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_161_5.dat new file mode 100755 index 0000000000..70d5fb008b --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_161_5.dat @@ -0,0 +1,2 @@ +xI +@нDp@ o|m rHk㨤~co^Jzװ#5l¦S_92 [}ZÊ=T2ƀP2[cV衆CYf'-X9>v~usK5`e,2,2,2-///o_q}K\reYfeYf峖o+/,\feYfeYfٳexB.e2,2,̲g+l\r16,2,xKGoxt!_!?`Z2*Db#6mYclucZ_V= zotdf6-&SvZv*GR8D7h;;_}1^MuiAF~^{4Rr^~KwcM;_~Y5%sO3bm!TkuUAgq{`4tJ z=-blf&CknX*3G+da{|L^pSsAbX|zx+~B?%Xj;& z;1;Iu;j^AJCF&Z`Lw*|;|Nba-v0r}8=>>Iv>dx&vf52z;Q-;T)bDuoUlH3!|5cNl~ F6aZ#Wx#j=> literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_169_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_169_5.dat new file mode 100755 index 0000000000..921a770767 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_169_5.dat @@ -0,0 +1 @@ +xj0|/niK'.r:T-m&Zx9yq3rOgvOO_zׯu`]ȷitاtiloc13j9%_g|ԧ)>>ϰ=te&_4=tU}/>>>Or5/u>/g}g}gOsvO}/g}g}ٷGo-w{r_{g}g}g_n=n]4Nkβ_M8m?SF< \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_173_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_173_5.dat new file mode 100755 index 0000000000..f9a6741361 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_173_5.dat @@ -0,0 +1,4 @@ +x[ +0&G1gD)[CzeDѷц=RN6FJm JqP}x s_}GFy; +[;]ek[QbTmy&0 L`̄Y?رw؛ fcVN9&0 L`ׄZ}0=F=F9ANL`&0 L`BzYfI=F9AN&0 L`&0 fIsr ' L`&0 L`<i͒"9AN&0 L`VaBX",Um> +=wZgBΜP !8 \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_177_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_177_5.dat new file mode 100755 index 0000000000..b07c636b62 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_177_5.dat @@ -0,0 +1,11 @@ +xъ0~ܾح uO,"% :$Xui=ѶՃgƸ?Ώq.So~z׉W:=h1cq]Ƕi!r8Ɓ`+X +V jj;8ƁX+ +oV`+X +V?[1^h-ֳ5Z;rmS+ +oV`+X +V;Z,YMB+ +V`+X +VZeڦ}r\!W`+X +V`+3Km>SB+ +V`+X +Vc㊛{g;^Qq5ZUݮQL0+*&YDq*6 \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_21_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_21_5.dat new file mode 100755 index 0000000000000000000000000000000000000000..04f97ea6983c85e1cf6e68150ea02bffa30a4f4b GIT binary patch literal 74 zcmV-Q0JZ;k+MSL;3IHGo1n<6P=l`E8qQc53Ts_3F*hoFpjJL@ppoFxt|L%<{=GZy:DR \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_33_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_33_5.dat new file mode 100755 index 0000000000000000000000000000000000000000..726d7fd75da51d6bd7737a41e0120162ad66f42d GIT binary patch literal 106 zcmV-w0G0oE+U-+G4gettyYm|Q|Ibavg*ZYTH6AU510hWtK(zoJYG1SgT>VVIrQ<*s zI~>0{7>;PQoL380wan9y;(obh5mL~qPG^u~gT@LOIlD;&&=vfsD8 MtZP z6Mwy=xtNYJP8P5*q$8GTpmRmFxG+$uUgBJr@iKnar8ktiR$tD;xa+0O#TdC4Bt{5~ JuLf_z1Mr1%Fq8lQ literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_41_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_41_5.dat new file mode 100755 index 0000000000..e07c6172a0 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_41_5.dat @@ -0,0 +1,2 @@ +xTA + 5?7XMtxҴx ?@7@~"N$Sɰ{+CA'r\Pp<ޏ- ͺ:S3sԉۻީz#qw > \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_45_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_45_5.dat new file mode 100755 index 0000000000..5168a17f9e --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_45_5.dat @@ -0,0 +1 @@ +xUA 5?U:N&Z":;4P1=bNvSGM1˛n'(κ J{Eѵs] ,sq \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_49_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_49_5.dat new file mode 100755 index 0000000000000000000000000000000000000000..9f3f3cd7d60bcba44d9223f52747c1cc9a9feb52 GIT binary patch literal 146 zcmV;D0B!$x+U-_B3cw%?yZ1HC|3A0G94cx|E9_(x5y8cz*3P{E3|?W)+(E}-2WX#i zfHZ}}1&-ONsSXt<99&1;WS;^gJWwdf{AS1((&~d%`->c<{A($VpSL1s{ zRx%t39rs?{9Nck;b+FN)RKeb>Q2ppQ^!)JYKLg9b6OOo!Q?K^%$i!U415hQ$TboHr A)c^nh literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_53_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_53_5.dat new file mode 100755 index 0000000000..449807bae1 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_53_5.dat @@ -0,0 +1 @@ +xVA " zYf5ƐJC A;l\,dR. \(e_ еaNi5\żaLP(;2שjN6O u+l{y6od^ C[%  \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_57_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_57_5.dat new file mode 100755 index 0000000000..c7dd81f390 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_57_5.dat @@ -0,0 +1,2 @@ +xVA + 5?NlZHAbBZ0a Md`1z'"<Ր19nvͨ. )bݻ~;9Z#tB~ \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_65_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_65_5.dat new file mode 100755 index 0000000000000000000000000000000000000000..ecd9380682523494a1f5577c6e22eff92571b446 GIT binary patch literal 163 zcmV;U09^lg+U-|C3IHJpy!#sG|DW4SODL(th;FD_Ol!+`ptx^N zLEU#+P+TP~hiEM?kDlMYCn2{Nokr`dh z=(A4`MyHt}6_aUUXNJM(G}onKt|Qlx8JUq8UCD^KPfsl&$;;O(L#`i%q&M<+dnDc& Vax;Tki-1v3l_b|f_yg_whE8s3Pu>6k literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_73_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_73_5.dat new file mode 100755 index 0000000000000000000000000000000000000000..00001176d018bd72f9f2128f2f0bf775aab99aa8 GIT binary patch literal 184 zcmV;p07w6L+U;0d4uBvG``>BS`(MrYR@be?`f zXg4__uzEOkK6dEjx2oLg#9{`gm`?j=wt4_|87ivOHqbF%PAmBX8kw9Db&_}T?ycUn z`rYuGA*p6w>oim|H}6{gZul*glk!e^C-3Cl6W;y1W(Io8^nRzjQ{KtDFL+1JRVtmB msmjs2lzG8{6JIm^VkS0TGiPwh(`lA>@p#INaS{u_o$71/34Ϊz^'[FyglgM>OTL4ϔ{&3Wy*ʧb*`<3;Vo0/s6n0ya[mcE \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_81_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_81_5.dat new file mode 100755 index 0000000000..71215e9525 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_81_5.dat @@ -0,0 +1,3 @@ +x + C~M?tzU4" }tMX2|.ɋ˙F\~m4Xu +ٔ, w:EƄ>X̯=_]g>>zמ/)5ךkkkZsXXY{ܮ}~mt:S#&;U#) \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_85_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_85_5.dat new file mode 100755 index 0000000000000000000000000000000000000000..09cf0e28127934862f45b0f155ede22e48833cd2 GIT binary patch literal 186 zcmV;r07d_J+U=P^3V<*S1n+rG^8e3yuu_X!YO*T`o0cLu)Fn>SVBDKU)x4XXo;}Lz zW!EUrTdh$X>snv=m*blAkGsN*t+gK4zOfzOFOb9L?@mq&=-FzkwKE)0V`(MDRSbv$ zF(3xMXCUdfAl$J#15Y(!+RxHTimUQ~@_-l+17bi7s2)%~pgf>FAO^&M82AqZz<$9g ouBdLo$N>Dm_MjDe$NHpal&=TaS~+q3j)5w#tg{8Q4HYg~22k``-v9sr literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_89_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_89_5.dat new file mode 100755 index 0000000000..5fff530696 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_89_5.dat @@ -0,0 +1,2 @@ +x + 45enpQ Gcfl^^;;b5;`kU͹߮j`NsO=\[a6~nLD? !6uF%w*Ȭkf77SĆbÆXodw_—mbClNۙ ck&YVoܡ׷BעبAl6 Jjx \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_93_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_93_5.dat new file mode 100755 index 0000000000..ec4240bd37 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_93_5.dat @@ -0,0 +1,2 @@ +xK +0 D>&&  fP^8BY5s(imҮ=f3/wۧEyYQwf[} [90303ef̙3'3=,ͼwxDַ.,;s%g,,,=Rὓ7uKKTD<(n lYhV۹sޕyPEtyY]ns ;ss,!LkԅcbL12cX91Z#XEn#;svT~L~LR11vs.1111J1&؍Ń111J1&ƞg KLƪjlk{gڞ5K1/ǐ~,ac$ \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_105_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_105_6.dat new file mode 100755 index 0000000000..a58fec749a --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_105_6.dat @@ -0,0 +1,3 @@ +xQ +@ DskBZ#o)Sd}Gܷl쯯^)G]S4S?#BZ:+{sHKNiI!me1 +RWe9!``Uyˀu:檞U=w-oԺwB}cMK蹰{{=y蹰{{=y蹰{wScaoi'fyO=CyO=Cy[{S޻=;|v4}ϯ20 \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_109_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_109_6.dat new file mode 100755 index 0000000000..be7b4749e0 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_109_6.dat @@ -0,0 +1 @@ +xA0 ~ρDBHCHV20nuol쯯˻=ۢs9[l'?7R" &2:7QqX_n ]$՚EIY*Lq0 0 0{LJз(s\ɳwX-7^ItIII$~?N0 0 0O'Itg7L$L-Iuzrfr M^'}(O~R]1YLĞu9Qӕ \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_113_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_113_6.dat new file mode 100755 index 0000000000..397f527417 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_113_6.dat @@ -0,0 +1,3 @@ +x E5NՉbF6on,m>gS9RWcǕ9&%1_cx= GR^w-z?dzv=,}ԥ?ǹژ:9m==@U䲉UXUXVe~by4Wi:e=+3}u4V%s*ZZGM&;I{^hb=04yIS((dxvZSQzT-ftM{l(nX+lN_8ODT@5*n+ z7_UD(asGDJ=LTuwGLL68OD>x%-!8VhX~z9)s_}nsM7#IRuTu5NlE1M}DE!~LqHV#? z>Qf#_+}0{yXSc>}`Oi7KFW*{qHhjb7t7W#OJ7->RxEgr--0aihQri-x!RWX2w$z_x z-{(K`{`B@#S%`O<>9f@NPe0F}o}Zti{rTɼÆ$<>Ov'Cytaaaa~|'9liΣ<<<<Iæ<:<<<ٕWDzy:.z= ݓʯ sVöE=ll_k0_#vίmj \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_129_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_129_6.dat new file mode 100755 index 0000000000000000000000000000000000000000..b4695c3ff0d8ebd6925d8af5e0b403d5974ed46b GIT binary patch literal 310 zcmV-60m=S&+U=Xs3d1l6Mc@4z{r{iaU`b3tw}-Y`#z{*Vj)K^nc<+O%V_D@=U$Xty z&tEmqAcJ?j?v86TK^YOR_hi^6CyeFRR#Fmw^kkN5f+OzB@3mz7YhU+_46fxbXrKqJ8dEWiRRzyf{&zkpxBFVGk0 z3uq)*fCX591z5l@;1}=<_yzg`eF2RG3$OqSumB7A1^fbj0lz?Bpf8}2U;!3j0T%w4 zg_beb0}DNqzB>!qv~!(b=$i3#ztA<~>3$)bcdqjbP4mv1Uw8})+SVD`X8*k}X!oJ` I0%mGk`PF`vEdT%j literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_133_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_133_6.dat new file mode 100755 index 0000000000000000000000000000000000000000..40911dc575dbbb17a4a057a46521c47b159138ca GIT binary patch literal 296 zcmb=p^>&UUU$X%Z%lo?CzyHr~4f5p772*@!y+W#4z-aoFvniX|)r-4>+}{b_t=?z1 za^B%Uo6C=?Bf5p+A4mDPRY%qZb+7;LH~IgZx!WufT|#LdH+*}lvf|jbWz$U;MJdj?+L60%T4~Y&jvfg5!>uf4f9dJsH`X7Hoj9}k^e4rC zS0C4C)Ev*0lXY~zAowlHfJqsIn&c1mKXCt`{DQ-(sSmClB=SSBgaxSfM)pCdp>-_w zAk_;%Y9X4Lls֪r666rҟ=vڲWy -' +Ο;q tQE>U϶f곭xN]Tc(s❮7tAw`v`v`v`kvwfwt;];;;;;!ޙ;ao];l;;;÷| ʷ(3}l.?"މr};\}S-Aw<9;EV'ם \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_141_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_141_6.dat new file mode 100755 index 0000000000..0340409a46 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_141_6.dat @@ -0,0 +1,10 @@ +xa F4/c]زȐ[=[E럓sm,fn/|kj\j?g[q(NOZc5SGGP[oMVָfvL<)r%zGej;*ZC7u5PHroFXT;2T zErAz-@(#{w+P}?wqtMJ3uDW8^Zp>M}^Fr0Lpp6sHMCtS0T=zSWcURlGh1q8m6XBZP z)<+zU-@Sal+U99%rKBCNUob7{wNzPiPzs9vGH3tDF8^{p;9$q2z5quaE#u5AU!HIYpjb0cSa)^E8M?_KmSO6tj)==bYa lKD}5p(yFJjBc0i__3AFOq}<9%1{c=3R{)onxM=_Y literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_149_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_149_6.dat new file mode 100755 index 0000000000..69e9883504 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_149_6.dat @@ -0,0 +1,2 @@ +x +0 E5f478UD62zmpߞk8}GӕN}?}W:guWAˌ7 ~;)Ø{̪ǿ+WPOۢrE\jwG)y˖wAAAAM%2`2dl|p}eqP : e j想=lT5ɠ : G{ddAtDaaaaa&أ&w : 1-MRb0Wf uz5&YiIѺNрAPQSL}4 \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_153_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_153_6.dat new file mode 100755 index 0000000000000000000000000000000000000000..3ab6130edd889298419b702ce1dcfddfda30cf2f GIT binary patch literal 367 zcmb=p_4ZC+-eCoSmWSnj%-{FNXq%daEZUK`+NjemCo*t?dCFURVYiCx+AY&AWaXz; z#Mw80;rtkb0d8YE*wD7uX)inq9ZVuZUFDP(T@8+N1 zd)6dxd%yYT`R@txHxIga-#%q{p=18$LjAv|)HMzi=qI9z_Q`c5T9@Wu3#VwUI?XqmF<#Ov^FVyZX znO%a=0o3e&KJ815aoXF1m16oYp6*fD?Jm?ru;I^wH)p;*n7!8a0qfl_O<0VW z=e+S-gZL9m5s=WóCaz6U~һ{`nݻdvVy~rZ"qk{>g$XKU}m\bjaGx,f1Yb]z̞^.5[?嬜r,f1YY>grVb,f1Y>g,rVmYb,f,|VY9Yb,f,|VY9+g1Ybً ̦M7>2{9z϶hm3l|9xټ#f#x6 -v%N' \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_161_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_161_6.dat new file mode 100755 index 0000000000000000000000000000000000000000..ecec68b1ac0f7ae45685bed5d752ef54a48f0f3e GIT binary patch literal 399 zcmb=p_4aOH-eCm+*NgRG%)jr)tiI;gamK_~Was)*2d9a$rlzvVFd(`Vxt}K4Lw&$1iobzS93uRn)?%d|f?w*sGT%NE> z{5IRU4qg|XWXaPJc9Wm(u=|#>w)ceHw=b*9PWtu#%$j?0UgYIfVgE|yed#NsPfj0ye)_BM^UK-)PTmj)Dk+(%2Qn6>@Mqw( zr>Eaf(4VdSY0^ghT|OXlU~(X3o3@I6N-cI>vvvVU=j&u7z0uK%@n+#iZgHXOO4Lz* zS5klalWD4FYUNJv>X3bV?!C6tj6Zkhb=|q?)qi3p?yJdny>uh|S+%y9&hsv>7uDKn Qv3e2bEbSR&we7b907^~O^#A|> literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_165_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_165_6.dat new file mode 100755 index 0000000000000000000000000000000000000000..d641dfa3dde2565c932c7234b4521d35c4a1af0d GIT binary patch literal 400 zcmb=p_4e+=tY!lV)(ihDXFd5FpVz$dq?^)Rf%2utrx}%SOWEwLn>i_J`_iSlTlG_a z?5xW@^>fafr_X<_`MyU#`bPG>>Gu53mUJ)QUoZ3jlYiaKTYL1ypC5hw`u4`@;aj#{ z65c&WV*jT2f7ha_kN&Vtzwen{t6%P~`!n?0+`iwtWbQAWZ<=}1xMrHJarBp;Hbut9$M7_G_mM zcbUq6j_Pl_yVJGrbfKl*aj-TBao%v9T>btkn@|5AAOFSPyYnPS{;9>|9^-RRg7etb z$M3`Xce6j+WoQnP+#v(96>7#EW6f8E-w#x%VH*A2r>p9Yt-(CciI0n7`gVuGb;wx~J!=SaTlS=q~KG>㵾+'{է_Vˊx-J<ӛܗr_>>i;rO}/g}g}ٿ}}xO}/g}g}ٷGo/{{r_>>o/z^#}g}ٿd'ʳ|QRNS3YڳZ'msEǷj5 \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_173_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_173_6.dat new file mode 100755 index 0000000000..95fa97c7bc --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_173_6.dat @@ -0,0 +1 @@ +xa09MrM S*:a_-5hh_)uZ֭[loےsmKN{H?x`l#f9>ڟ[eЄώߓ ?^m*/Kmhy%v-nKlkKL`&& g5(gwxYܞa¬pVcZ[#O=SN9&0 L`DŽ 'tjj]QN9&0 L`sYRc@QN9 L`&0 L`"YRc\ ' r&0 L`EH9AN&0 L`& 7p6`|hms R5Ƙȉ k\X/ )g9 \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_177_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_177_6.dat new file mode 100755 index 0000000000..e9f0476f01 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_177_6.dat @@ -0,0 +1,14 @@ +xn {ڤ*4v۴u1{f{_,,K9o 4ǵ7lniJiggir<-MG + + + +xuV+zRCr9+Gq6QWb"Qe"WL+ + XXXXX/|~j,nmuMۤ+ + XXXXXYa,X;M+ + XXXXXe)oӘf|5H늚7/D \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_21_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_21_6.dat new file mode 100755 index 0000000000..6bd505b4ae --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_21_6.dat @@ -0,0 +1 @@ +xڝQ C9M{i]X1- C!D7 W ٜ&rD)~]<M 3(>{A aS \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_25_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_25_6.dat new file mode 100755 index 0000000000..d45083aab3 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_25_6.dat @@ -0,0 +1 @@ +xڝQA 52)e+(XmZt*(ڹ;tJ<峂_ڤ3oڴ"̢azh}&qvSG֙,-J4}oS[}w \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_29_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_29_6.dat new file mode 100755 index 0000000000..0408e22400 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_29_6.dat @@ -0,0 +1,3 @@ +xRA +0 XcL(4EԈB +8Cܾ޳nM+lǝՆO1]&ڍ4UD-6-$:6dZ?ylf? 8?߲bTQ5IjGy*#_+5FlYF0i|O3{~#Ory&Utx49Pb}M!qqU eGB4z0Ua0$-m+;~$`<#o+3+E#9Qgs1fkI!%T**fq5 literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_37_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_37_6.dat new file mode 100755 index 0000000000..b37ff0ab68 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_37_6.dat @@ -0,0 +1 @@ +xA &T `jB{{Ok%tdtlP>c!Jq2?sR|6OyXBED_#CJGJ|{4?o|# zWi31{IkRWty7H{19EeICVF#bFBLK`Sqohq!P3c{Hxke=hL*^psZ< literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_45_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_45_6.dat new file mode 100755 index 0000000000000000000000000000000000000000..a7da7ee0297b6371bd3ebf57467f009712e40cd6 GIT binary patch literal 189 zcmV;u07CzG+U-?A4#Xe`yz?6P|L3|%6(}8a+S4YxXiTkkisJyP!>lt6lk}|T(tQNG zKksqJ*w!?R9dPAm82jdF@w%LHYmk%ah`(8LQqB>(=edn(GCywV&RxRO?k3YHLkmWk z*aT+KWsqQ$<3Gg7PA4uT3C+}K0drYmWeGYb>)1(p7ta_qgP{3eCtn>q+w^_mWM3IF rC#dT5I$?6GuTnWSVh7coK6Z$@lVax;CrT-Ws>PubeR7!xL?VGyGa6i_ literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_49_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_49_6.dat new file mode 100755 index 0000000000..64ded70938 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_49_6.dat @@ -0,0 +1,2 @@ +xVQ i{K&YX؁0̦!=tn&fpWL +`/<ϓKVrU\1bGp@ӑ&fN/+ƽq`AWSRGp_5||zxν:J+WEm4h43tܭ.Ag`\ʤ*F \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_53_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_53_6.dat new file mode 100755 index 0000000000000000000000000000000000000000..9139e325901037a409a2a2d317141cbd9bb27807 GIT binary patch literal 195 zcmV;!06hPA+U=In4uBvCMc?@v`Tw7*ElsG)6&_ona}B4=6R_J_H(gnqCg!_cThR(n zojZKuSh^TEmcRQQKZxaCGEO}_fJWLvh7ncHkT}m%gO65*3_i=>Wv5I&JTu%XJ;$_U zq9afxnshi?de~<88-}PKQlCVXOM8jnene{dW{CPB^@+QK_CphY=i#p94>Qav8_#g2 xe}y4d8>?E5v<_ݏ`8G1`B`;+}&s]<iK'l'9%.7 \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_65_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_65_6.dat new file mode 100755 index 0000000000..550fc8fe7c --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_65_6.dat @@ -0,0 +1 @@ +xWQ i{KNLk?e$Qik41{`+!ڮM ? 1b8 .^wsnFj5EaQX|=w@2v<ŋŞ|4w\UXBQz+TTcBz/48,5`ȱ OV$ \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_69_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_69_6.dat new file mode 100755 index 0000000000..a3e4fa0f28 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_69_6.dat @@ -0,0 +1 @@ +xK @dTh hLSSEq eY@<+*|窮 %>z*7e6QS`.>sE '%@[6@P0h aFxtpl2 Q-g1Nfeo^0FdT>N_OwG3ug {3<[Ժ b?'6^ \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_73_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_73_6.dat new file mode 100755 index 0000000000000000000000000000000000000000..ab71b70ae59ccfb23e9c4185b1e919d531fb040f GIT binary patch literal 230 zcmV6w!~0Dh_U!#`ksFaQ7m literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_77_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_77_6.dat new file mode 100755 index 0000000000..ad5a660e30 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_77_6.dat @@ -0,0 +1 @@ +x E۲iVa.FpSTY4q~z=:͒ 6m8:#0PضiDy:2Š'Zs&}滜\r0\ŚXw;iPȔL)Seԕ{hDu9LbJSS))gZ{e)qJdLw+#3-V0շljڠS-S 9=ݯ5PPq1M?g \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_81_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_81_6.dat new file mode 100755 index 0000000000..28a6d0752a --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_81_6.dat @@ -0,0 +1,3 @@ +xQ0D9 rRLvk`0 ;i6\|_cc1huio#2}x*.Yt& +ְq/K;3ve̢ȊAH?`]5Kw!}{Zû߲W +yⷾ^_ykk^Kתb-bYSڸ'֜Nu#MfHSQ?|]IAiMyyuW \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_85_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_85_6.dat new file mode 100755 index 0000000000000000000000000000000000000000..d5403e49a80cd825886b9cf067587297c22542b2 GIT binary patch literal 229 zcmVz+U=Q93IibwME~bB$^9?aLbYnE?lu`e3KJJuW7jbno{1 z6z!*@KIP;y_0^MY?(1Uq+xo)SueFNlpSwk~3kHJTZW;Jhe~*j0f6i>Ev*&T`9<+0j zJ6S`Ia)zC1@aibjTa>+X>cWZvF(3xSz)1!u`9P<`={Ia;V3iYwbtJu->xkU@m%8GB z;(!ntduS+U=Q93dA4`ME`S|1p|)V-|IyeM8#yz&a$P<5H8u1j zJvs=^zR6hA`VPFkrR{fvn^+GR9ksLtHTXo64YENt$i~~)fawi)LW)84hWko>3Nllc zx#paEz7d+7rFOyurM|GlguB4YENtj{*X=Ev^DF}ptAKX6@RpX4vjZjklMsHWS3nR)a*F|K!unUJMmO!Vn} zoTXx;{$pvl+nqFvSzoF-QmYM8VkABAis-2UASHPxZ{Wiz40ft)3ZUf;xjbgSiRJfEh3YX28r%%+%_c*_vLo*0<$@ z23~1k)EU$n%uQeh%zzm%^YhH4w^!eo$?ptXW}>e4xt^)$QRDw9atl$NB&!*=1='#`+bl]Z \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_101_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_101_7.dat new file mode 100755 index 0000000000..1f6bc51291 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_101_7.dat @@ -0,0 +1 @@ +xQ C}rm`fjT#54'tfaЇo$cmOJ23c<6Xn0F ) \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_105_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_105_7.dat new file mode 100755 index 0000000000..6b0cacfe94 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_105_7.dat @@ -0,0 +1,2 @@ +xA +0EFaMҙNPx)pQ_~|ñ(bF$.aoWGNPUǖM%{oHQUlִL^>+m#{{eo&Y2soM)gncO9sZ3wo+{=f.zޣ{{=zGcskCQϞp^&{^NʷU e5}EwGn+o \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_109_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_109_7.dat new file mode 100755 index 0000000000..9875cbe8ce --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_109_7.dat @@ -0,0 +1,2 @@ +xA +0 D9Mr}* _x-d:"NJ-k"⨚d{ջגɬ|'rQ5+ s)c7-1nn햺qɔJtg^ʉw̘Ň-?*&Mm@ee5^ +c + +,b\13j4TZfŢo* + + +:Ut* + + +Xů0"%6ed 8rS NsUnk5XejުuVXg,l`u!hXZ\VlM|[ͬ0 0 0 #hF'c]i>Hataaa~<ÆzyqkO0 0 0 㪞Faaa)2˰fÒ%z8tO=3=3:cw +V$ \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_121_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_121_7.dat new file mode 100755 index 0000000000..d5d577f7ee --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_121_7.dat @@ -0,0 +1,2 @@ +x[ + Edi;^az,#6ƞ^rh&^amY9_غ5Cr6t^^WlEز~ɿ|MmmS}( ۰ ۰ ۰}mQ]ZVq]vѲ"M1fG, qBmtaaazeF3cxIDmtaaamný$n۰ ۰ ۰ ۏa[}`[yޖ޻)n<4K/Oslnlm/G \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_125_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_125_7.dat new file mode 100755 index 0000000000000000000000000000000000000000..f9ec0887b05d63190cd6ebe1bb181252db240a5b GIT binary patch literal 288 zcmb=p^>(%+-(dwFmiND<{`^0`b*j_CTqezxZ{^rj;wKcm=?PmSu=Glz<7VlslV=Y- zO8>Uu%kQn~TjKuaTl~3{$o%&7)#h8}=22b&;klFIREslqHa`A!?#mmI&via`Hb+0Y z@=dof=(6k43w+wj81s{h(%)-(dv-mfy8!@BX(Jcim*@*T8k(?P5abBdPTV55E<2nWM}-KktRmgh+FK z(J5XM>l-trubloFW*NMH^}mg5a-3Y>F9fC9#l&wam?F3O`BH_m{d}fVk6)~v_I&Hz zHJ=+cdHrqItm^WLCC8c$D{tL(Yf{g&%x7Q9 zubFPXVwyI0pF{it{Ra~h7{x%SagRfsg8l^Y7u;W1ZG?0VFoDnn@e|xnSU)k2i@PJtN|E!a-ww)M{dF^|hGkUkCJ!|~{M9*8VnoW7a`o8vg bU+c{{(Mi0VI}n?>ZnFz5Vih(RRyE z|8~uM{F%Y??j(=+_U|&*MxD6{EXooGIou3B+4rdIt<3u>@m`ky)yCCP{twlzB)65{ zxt83x+o|t_pyi`jZivc5?msx>A1W8Q07ZMC+7Tk}qrk>B3EF|xO$4cfDymCT`D{~t z*yCE}H)iGgttY-t-17O&+Wsls{yaWkCr)~=oXRV&$sJJi`D}ZpWzpFaOJp>)b53$^ I_LZ6o0K%<<o[l +Ο +07։Vl;b7fMS;1LCvR|KMH +#Н(Sqd \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_141_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_141_7.dat new file mode 100755 index 0000000000..98dffab003 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_141_7.dat @@ -0,0 +1 @@ +xA E= rITY@Ä0!|1tbG0ԗѤs2Z/oa\qzOnҋMntX"KmeM}CpPL^S0S0S0SL )ǔژY߾%b,Sl?zC)tLLLLI2zRXh@)tLLLLI1zbϷB)~0S0S0S07)|B)))3ՖL% tfwM*:~hZsnc$1UTtJg8OYE \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_145_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_145_7.dat new file mode 100755 index 0000000000..4aa2bac117 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_145_7.dat @@ -0,0 +1,2 @@ +x + E5?W6Z-^2qbGX6(Ɖu"LbbGuμGk:HwA[jmHݞ3OkQ{l|TEm JfL?2"&)kRfc̉F,z=5X5X5X7F\pUs#5X5X5XFdYk!a ` ` `o8ct ]CC gM5[N%khZp?Iܣϲ^n$Y7AZP[ fȓ0 \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_149_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_149_7.dat new file mode 100755 index 0000000000..809f005543 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_149_7.dat @@ -0,0 +1 @@ +xn {? uچ2G$ncFKb3֪tPc ̥7[?9:['9'*Ӗ Gah_/z+6XB>2qYJ0黏Bfa 1 9c7G Ol,^꽓3A:H  1A b0X4%٫#d>&C  1A b+g嬜,f1Yb|Y>+g嬜,f1Ylio.\Ɲo=gϙ-yk_TA \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_161_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_161_7.dat new file mode 100755 index 0000000000..35ba8ff488 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_161_7.dat @@ -0,0 +1 @@ +xю y/皨E)Ʈ1~493,˵+ZT=ZeC.~iߏ&>,6e~,lW] 2\;׵2j"e,rXݵV(c쵵ZӖ18ީ/,'t.ee,cX2߱,_|yt|]t.cX2e,cy/ɗys.eLe,cX2lo|Z{+2]bl,cX2e{+֊[A2]X2e,c9CXxJ+6rf>h5o6qcIQd2nm=Y_6}PsM~AQyzW1wahnqx4i9#trl79 zGTt{n-#Tkq_|`@8b@BO^-&#lQzJI&bdA?QLEw|ZnT7O=OKRfSrJL~d`Aw9u`O=epcc%X?f~g1U{2P4x`u+VC z_4Xxs=ND}`cY4xm literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_173_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_173_7.dat new file mode 100755 index 0000000000..3b51371252 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_173_7.dat @@ -0,0 +1 @@ +xj0 {=M/ RbHrj\=,mcoZ8;mm0b_ߚt$~Wu5ǚNn'?df2(oG,L6_{z? 9wq9 S!ƌ9o2 L`pOEnv8Lv8L UY"d} Kr ' L`&0 L`B'f:O8TcTc\kr ' L`&0 L`BYgIkr ' L`&0 L`"YRc\ ' r&0 L`EڳH9AN&0 L`&'!;q+Yl*ܳ=؀9j[2 Ms¤Ԅ* \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_177_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_177_7.dat new file mode 100755 index 0000000000000000000000000000000000000000..068477c92af080afb53465a651dd7d1815d562c2 GIT binary patch literal 407 zcmb=p_4e+?tY!lS)(hWjd_VoS4`&P8oD}qI!Lz*MP5ODNZExOG1+MwJb8D;p-LO8! z?c3h{D>>rL|Ffpgd7A6Xc6+Psn~m@D{rRxH?)O~%;+lWWo2OpY#{~EVH=u6 z*CeoNM;zn|YxyENS5&k1Xxh;g$7WrP-Y%=Xxj1^=`WtIKUTA-NKSlcq*G{K(1-ys2 zkTw0`)@J(M6-dz@>7=A*us5M&v*{}_V zikH1R`f1L}yvxPQidw;HV6KBY)F%BE(1c~reV&3`GXd;yxF5O}ivA8Q?Gyc+m*}Yd zYKdyZw)H0}-4ng5+!L?o?u!;XT{?T~wJmv{XFN90&I($0Ldf#T-Q#!vG0tQ&@c{s0 CYRvKg literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_21_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_21_7.dat new file mode 100755 index 0000000000..4f9f1386d6 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_21_7.dat @@ -0,0 +1,4 @@ +xڝQ +0 B=r]-?]Rl2nc +[nA".j+i +~x3;QIQH8R҈G"z,&;'o97%P8%6oǽ;]NWn[f7v \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_29_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_29_7.dat new file mode 100755 index 0000000000..e3d7391b7d --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_29_7.dat @@ -0,0 +1,2 @@ +xR9 QpX$lŲf!I2pgSMZj"te0#ԛ`_1-cha~/Eh4"~ \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_37_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_37_7.dat new file mode 100755 index 0000000000000000000000000000000000000000..87d9a1a9d2c57b346aa4a8a2e03754a7f7668ab3 GIT binary patch literal 122 zcmV-=0EPc}+U-<34nQFY+c^!r|K-dh{-xmXMloTrDOS>|wt{nN$p8UIKT#gc;o<{l zE|1OO;;@B_*1sBYail&HAVaMSznZ$V?+Zst%JX#@k^|V&_%v=tL+(dx9wGsNg^L)? cH0CK`8l%kV8sp6AdyF;KJ?hQK0&cYgLugGq!2kdN literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_41_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_41_7.dat new file mode 100755 index 0000000000..8acec04f0f --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_41_7.dat @@ -0,0 +1 @@ +xTA 5[fDY(O^bR3/~t/L"7SQQ5j\Sib#Նȏ+ǣw#zx?㽧A-wu曑Y7$b.%A;wRoxG}? \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_45_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_45_7.dat new file mode 100755 index 0000000000000000000000000000000000000000..dbba31d0bfddcbed98ea5d7a9f6ee59f976e0421 GIT binary patch literal 173 zcmV;e08;;W+U-@*3cw%;e7~=;|Npr$DAUrsy$v=4K{1olOOYWhIYuypTZ?rVorcG` ziBe#%j*Q*dv)kOh(x@276Nxd&JP9U=2L~+ngMKq>jn}9{jK*jHRSM|5x5n%q5&j`e zsyWelhq7a3OJw7iYkdjosJYWIS!tZw|D3#JI49hv+1NkUa3?Dz bQL;xvA(=V?*)bSkL5FpXakG_S`^qkH-^P*%H1XEuNsLnfvAu` z`*BEI8A_NDBQX*qG5P?bYCW1Y9JBiGIs;FK@Oe8gF(1@>hQVmp28DF`j`Xh+BQX-A zPcoX-fMZmg?C*@cp*&oVxZ$(2Gpy+uA|bgw1Aot87`^rkIYy2Bj`)lr?t+Z`0Y=4# E+IH1p%m4rY literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_73_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_73_7.dat new file mode 100755 index 0000000000000000000000000000000000000000..f414e4a5877861bd45e439125a47c52b70a0ca12 GIT binary patch literal 221 zcmV<303!c*+U;1;4#OY_e7~=O|NpsVOKWw-svJ$a1xQMLKjmF z$Fo{kZk}5~sTj*XCvO!sEAx=sT{$)7Ae9TQEOc4^f@wQ)ea)wu7H%}{#MK=l@)ASD zb^%LDMOj|2h)%FTj!en)$C=J-W^O$fr&vx@-|THnt7dOwYNfBvQ>ukv o40T%96U5*sI{`_>S?}(:yTl{G&E\6}"AX XϬQSkr0$!s zmX(et@ z$j}>#8;TpsDPluxhz+rk$AtTL3lG`X3UJ4`446UdL}WW&@+vhnbfN!;F1qj9*+b^XNIk3 z`ZsZ(ai8(tWM<5anK3hF=1a_o^~_APRcC!GK5O7o1G~?-&v25BNzV0+I1 literal 0 HcmV?d00001 diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_97_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_97_7.dat new file mode 100755 index 0000000000..b277368bdb --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_97_7.dat @@ -0,0 +1,2 @@ +x0ܲD[V.KI`'sxy$xx"= O^&pbYʬ$Kݣ8K eÌ 3:$bI,%$Yb襶 +M)T %q綦EX}jlTTKbI,%,_e[%V]R];N,%$%V@`%pam(}_%%۸4='u)ai.;M> \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/composer.json b/vendor/aferrandini/phpqrcode/composer.json new file mode 100644 index 0000000000..ff79356716 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/composer.json @@ -0,0 +1,21 @@ +{ + "name": "aferrandini/phpqrcode", + "description": "PHPQRCode porting and changed for PHP 5.3 compatibility", + "keywords": ["php", "qrcode", "barcode"], + "homepage": "https://github.com/aferrandini/PHPQRCode", + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "Ariel Ferrandini", + "email": "arielferrandini@gmail.com", + "homepage": "http://www.ferrandini.com/" + } + ], + "require": { + "php": ">=5.3.0" + }, + "autoload": { + "psr-0": { "PHPQRCode": "lib/" } + } +} diff --git a/vendor/aferrandini/phpqrcode/lib/PHPQRCode.php b/vendor/aferrandini/phpqrcode/lib/PHPQRCode.php new file mode 100644 index 0000000000..e96c5e3df0 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/lib/PHPQRCode.php @@ -0,0 +1,42 @@ +width = $width; + $this->frame = $frame; + $this->x = $width - 1; + $this->y = $width - 1; + $this->dir = -1; + $this->bit = -1; + } + + //---------------------------------------------------------------------- + public function setFrameAt($at, $val) + { + $this->frame[$at['y']][$at['x']] = chr($val); + } + + //---------------------------------------------------------------------- + public function getFrameAt($at) + { + return ord($this->frame[$at['y']][$at['x']]); + } + + //---------------------------------------------------------------------- + public function next() + { + do { + + if($this->bit == -1) { + $this->bit = 0; + return array('x'=>$this->x, 'y'=>$this->y); + } + + $x = $this->x; + $y = $this->y; + $w = $this->width; + + if($this->bit == 0) { + $x--; + $this->bit++; + } else { + $x++; + $y += $this->dir; + $this->bit--; + } + + if($this->dir < 0) { + if($y < 0) { + $y = 0; + $x -= 2; + $this->dir = 1; + if($x == 6) { + $x--; + $y = 9; + } + } + } else { + if($y == $w) { + $y = $w - 1; + $x -= 2; + $this->dir = -1; + if($x == 6) { + $x--; + $y -= 8; + } + } + } + if($x < 0 || $y < 0) return null; + + $this->x = $x; + $this->y = $y; + + } while(ord($this->frame[$y][$x]) & 0x80); + + return array('x'=>$x, 'y'=>$y); + } + +} ; \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRbitstream.php b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRbitstream.php new file mode 100755 index 0000000000..93606f13f9 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRbitstream.php @@ -0,0 +1,182 @@ + + * + * PHP QR Code is distributed under LGPL 3 + * Copyright (C) 2010 Dominik Dzienia + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +namespace PHPQRCode; + +class QRbitstream { + + public $data = array(); + + //---------------------------------------------------------------------- + public function size() + { + return count($this->data); + } + + //---------------------------------------------------------------------- + public function allocate($setLength) + { + $this->data = array_fill(0, $setLength, 0); + return 0; + } + + //---------------------------------------------------------------------- + public static function newFromNum($bits, $num) + { + $bstream = new QRbitstream(); + $bstream->allocate($bits); + + $mask = 1 << ($bits - 1); + for($i=0; $i<$bits; $i++) { + if($num & $mask) { + $bstream->data[$i] = 1; + } else { + $bstream->data[$i] = 0; + } + $mask = $mask >> 1; + } + + return $bstream; + } + + //---------------------------------------------------------------------- + public static function newFromBytes($size, $data) + { + $bstream = new QRbitstream(); + $bstream->allocate($size * 8); + $p=0; + + for($i=0; $i<$size; $i++) { + $mask = 0x80; + for($j=0; $j<8; $j++) { + if($data[$i] & $mask) { + $bstream->data[$p] = 1; + } else { + $bstream->data[$p] = 0; + } + $p++; + $mask = $mask >> 1; + } + } + + return $bstream; + } + + //---------------------------------------------------------------------- + public function append(QRbitstream $arg) + { + if (is_null($arg)) { + return -1; + } + + if($arg->size() == 0) { + return 0; + } + + if($this->size() == 0) { + $this->data = $arg->data; + return 0; + } + + $this->data = array_values(array_merge($this->data, $arg->data)); + + return 0; + } + + //---------------------------------------------------------------------- + public function appendNum($bits, $num) + { + if ($bits == 0) + return 0; + + $b = QRbitstream::newFromNum($bits, $num); + + if(is_null($b)) + return -1; + + $ret = $this->append($b); + unset($b); + + return $ret; + } + + //---------------------------------------------------------------------- + public function appendBytes($size, $data) + { + if ($size == 0) + return 0; + + $b = QRbitstream::newFromBytes($size, $data); + + if(is_null($b)) + return -1; + + $ret = $this->append($b); + unset($b); + + return $ret; + } + + //---------------------------------------------------------------------- + public function toByte() + { + + $size = $this->size(); + + if($size == 0) { + return array(); + } + + $data = array_fill(0, (int)(($size + 7) / 8), 0); + $bytes = (int)($size / 8); + + $p = 0; + + for($i=0; $i<$bytes; $i++) { + $v = 0; + for($j=0; $j<8; $j++) { + $v = $v << 1; + $v |= $this->data[$p]; + $p++; + } + $data[$i] = $v; + } + + if($size & 7) { + $v = 0; + for($j=0; $j<($size & 7); $j++) { + $v = $v << 1; + $v |= $this->data[$p]; + $p++; + } + $data[$bytes] = $v; + } + + return $data; + } + +} \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRcode.php b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRcode.php new file mode 100644 index 0000000000..08b6024358 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRcode.php @@ -0,0 +1,158 @@ +getVersion() < 0 || $input->getVersion() > Constants::QRSPEC_VERSION_MAX) { + throw new Exception('wrong version'); + } + if($input->getErrorCorrectionLevel() > Constants::QR_ECLEVEL_H) { + throw new Exception('wrong level'); + } + + $raw = new QRrawcode($input); + + QRtools::markTime('after_raw'); + + $version = $raw->version; + $width = QRspec::getWidth($version); + $frame = QRspec::newFrame($version); + + $filler = new FrameFiller($width, $frame); + if(is_null($filler)) { + return NULL; + } + + // inteleaved data and ecc codes + for($i=0; $i<$raw->dataLength + $raw->eccLength; $i++) { + $code = $raw->getCode(); + $bit = 0x80; + for($j=0; $j<8; $j++) { + $addr = $filler->next(); + $filler->setFrameAt($addr, 0x02 | (($bit & $code) != 0)); + $bit = $bit >> 1; + } + } + + QRtools::markTime('after_filler'); + + unset($raw); + + // remainder bits + $j = QRspec::getRemainder($version); + for($i=0; $i<$j; $i++) { + $addr = $filler->next(); + $filler->setFrameAt($addr, 0x02); + } + + $frame = $filler->frame; + unset($filler); + + + // masking + $maskObj = new QRmask(); + if($mask < 0) { + + if (Constants::QR_FIND_BEST_MASK) { + $masked = $maskObj->mask($width, $frame, $input->getErrorCorrectionLevel()); + } else { + $masked = $maskObj->makeMask($width, $frame, (intval(Constants::QR_DEFAULT_MASK) % 8), $input->getErrorCorrectionLevel()); + } + } else { + $masked = $maskObj->makeMask($width, $frame, $mask, $input->getErrorCorrectionLevel()); + } + + if($masked == NULL) { + return NULL; + } + + QRtools::markTime('after_mask'); + + $this->version = $version; + $this->width = $width; + $this->data = $masked; + + return $this; + } + + //---------------------------------------------------------------------- + public function encodeInput(QRinput $input) + { + return $this->encodeMask($input, -1); + } + + //---------------------------------------------------------------------- + public function encodeString8bit($string, $version, $level) + { + if(string == NULL) { + throw new Exception('empty string!'); + return NULL; + } + + $input = new QRinput($version, $level); + if($input == NULL) return NULL; + + $ret = $input->append($input, Constants::QR_MODE_8, strlen($string), str_split($string)); + if($ret < 0) { + unset($input); + return NULL; + } + return $this->encodeInput($input); + } + + //---------------------------------------------------------------------- + public function encodeString($string, $version, $level, $hint, $casesensitive) + { + + if($hint != Constants::QR_MODE_8 && $hint != Constants::QR_MODE_KANJI) { + throw new Exception('bad hint'); + return NULL; + } + + $input = new QRinput($version, $level); + if($input == NULL) return NULL; + + $ret = QRsplit::splitStringToQRinput($string, $input, $hint, $casesensitive); + if($ret < 0) { + return NULL; + } + + return $this->encodeInput($input); + } + + //---------------------------------------------------------------------- + public static function png($text, $outfile = false, $level = Constants::QR_ECLEVEL_L, $size = 3, $margin = 4, $saveandprint=false) + { + $enc = QRencode::factory($level, $size, $margin); + return $enc->encodePNG($text, $outfile, $saveandprint=false); + } + + //---------------------------------------------------------------------- + public static function text($text, $outfile = false, $level = Constants::QR_ECLEVEL_L, $size = 3, $margin = 4) + { + $enc = QRencode::factory($level, $size, $margin); + return $enc->encode($text, $outfile); + } + + //---------------------------------------------------------------------- + public static function raw($text, $outfile = false, $level = Constants::QR_ECLEVEL_L, $size = 3, $margin = 4) + { + $enc = QRencode::factory($level, $size, $margin); + return $enc->encodeRAW($text, $outfile); + } +} diff --git a/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRencode.php b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRencode.php new file mode 100755 index 0000000000..d05ab6b7b0 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRencode.php @@ -0,0 +1,137 @@ + + * + * PHP QR Code is distributed under LGPL 3 + * Copyright (C) 2010 Dominik Dzienia + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +namespace PHPQRCode; + +use Exception; + +class QRencode { + + public $casesensitive = true; + public $eightbit = false; + + public $version = 0; + public $size = 3; + public $margin = 4; + + public $structured = 0; // not supported yet + + public $level = Constants::QR_ECLEVEL_L; + public $hint = Constants::QR_MODE_8; + + //---------------------------------------------------------------------- + public static function factory($level = Constants::QR_ECLEVEL_L, $size = 3, $margin = 4) + { + $enc = new QRencode(); + $enc->size = $size; + $enc->margin = $margin; + + switch ($level.'') { + case '0': + case '1': + case '2': + case '3': + $enc->level = $level; + break; + case 'l': + case 'L': + $enc->level = Constants::QR_ECLEVEL_L; + break; + case 'm': + case 'M': + $enc->level = Constants::QR_ECLEVEL_M; + break; + case 'q': + case 'Q': + $enc->level = Constants::QR_ECLEVEL_Q; + break; + case 'h': + case 'H': + $enc->level = Constants::QR_ECLEVEL_H; + break; + } + + return $enc; + } + + //---------------------------------------------------------------------- + public function encodeRAW($intext, $outfile = false) + { + $code = new QRcode(); + + if($this->eightbit) { + $code->encodeString8bit($intext, $this->version, $this->level); + } else { + $code->encodeString($intext, $this->version, $this->level, $this->hint, $this->casesensitive); + } + + return $code->data; + } + + //---------------------------------------------------------------------- + public function encode($intext, $outfile = false) + { + $code = new QRcode(); + + if($this->eightbit) { + $code->encodeString8bit($intext, $this->version, $this->level); + } else { + $code->encodeString($intext, $this->version, $this->level, $this->hint, $this->casesensitive); + } + + QRtools::markTime('after_encode'); + + if ($outfile!== false) { + file_put_contents($outfile, join("\n", QRtools::binarize($code->data))); + } else { + return QRtools::binarize($code->data); + } + } + + //---------------------------------------------------------------------- + public function encodePNG($intext, $outfile = false,$saveandprint=false) + { + try { + ob_start(); + $tab = $this->encode($intext); + $err = ob_get_contents(); + ob_end_clean(); + + if ($err != '') + QRtools::log($outfile, "ERROR: " . $err); + + $maxSize = (int)(Constants::QR_PNG_MAXIMUM_SIZE / (count($tab)+2*$this->margin)); + + QRimage::png($tab, $outfile, min(max(1, $this->size), $maxSize), $this->margin,$saveandprint); + } catch (Exception $e) { + echo $e->getMessage(); + die(); + + QRtools::log($outfile, $e->getMessage()); + } + } +} diff --git a/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRimage.php b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRimage.php new file mode 100755 index 0000000000..430a16f842 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRimage.php @@ -0,0 +1,95 @@ + + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +namespace PHPQRCode; + +class QRimage { + + //---------------------------------------------------------------------- + public static function png($frame, $filename = false, $pixelPerPoint = 4, $outerFrame = 4,$saveandprint=FALSE) + { + $image = self::image($frame, $pixelPerPoint, $outerFrame); + + if ($filename === false) { + Header("Content-type: image/png"); + ImagePng($image); + } else { + if($saveandprint===TRUE){ + ImagePng($image, $filename); + header("Content-type: image/png"); + ImagePng($image); + }else{ + ImagePng($image, $filename); + } + } + + ImageDestroy($image); + } + + //---------------------------------------------------------------------- + public static function jpg($frame, $filename = false, $pixelPerPoint = 8, $outerFrame = 4, $q = 85) + { + $image = self::image($frame, $pixelPerPoint, $outerFrame); + + if ($filename === false) { + Header("Content-type: image/jpeg"); + ImageJpeg($image, null, $q); + } else { + ImageJpeg($image, $filename, $q); + } + + ImageDestroy($image); + } + + //---------------------------------------------------------------------- + private static function image($frame, $pixelPerPoint = 4, $outerFrame = 4) + { + $h = count($frame); + $w = strlen($frame[0]); + + $imgW = $w + 2*$outerFrame; + $imgH = $h + 2*$outerFrame; + + $base_image =ImageCreate($imgW, $imgH); + + $col[0] = ImageColorAllocate($base_image,255,255,255); + $col[1] = ImageColorAllocate($base_image,0,0,0); + + imagefill($base_image, 0, 0, $col[0]); + + for($y=0; $y<$h; $y++) { + for($x=0; $x<$w; $x++) { + if ($frame[$y][$x] == '1') { + ImageSetPixel($base_image,$x+$outerFrame,$y+$outerFrame,$col[1]); + } + } + } + + $target_image =ImageCreate($imgW * $pixelPerPoint, $imgH * $pixelPerPoint); + ImageCopyResized($target_image, $base_image, 0, 0, 0, 0, $imgW * $pixelPerPoint, $imgH * $pixelPerPoint, $imgW, $imgH); + ImageDestroy($base_image); + + return $target_image; + } +} \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRinput.php b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRinput.php new file mode 100755 index 0000000000..8bdd21e719 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRinput.php @@ -0,0 +1,486 @@ + + * + * PHP QR Code is distributed under LGPL 3 + * Copyright (C) 2010 Dominik Dzienia + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +namespace PHPQRCode; + +use Exception; + +class QRinput { + + public $items; + + private $version; + private $level; + + //---------------------------------------------------------------------- + public function __construct($version = 0, $level = Constants::QR_ECLEVEL_L) + { + if ($version < 0 || $version > Constants::QRSPEC_VERSION_MAX || $level > Constants::QR_ECLEVEL_H) { + throw new Exception('Invalid version no'); + return NULL; + } + + $this->version = $version; + $this->level = $level; + } + + //---------------------------------------------------------------------- + public function getVersion() + { + return $this->version; + } + + //---------------------------------------------------------------------- + public function setVersion($version) + { + if($version < 0 || $version > Constants::QRSPEC_VERSION_MAX) { + throw new Exception('Invalid version no'); + return -1; + } + + $this->version = $version; + + return 0; + } + + //---------------------------------------------------------------------- + public function getErrorCorrectionLevel() + { + return $this->level; + } + + //---------------------------------------------------------------------- + public function setErrorCorrectionLevel($level) + { + if($level > Constants::QR_ECLEVEL_H) { + throw new Exception('Invalid ECLEVEL'); + return -1; + } + + $this->level = $level; + + return 0; + } + + //---------------------------------------------------------------------- + public function appendEntry(QRinputItem $entry) + { + $this->items[] = $entry; + } + + //---------------------------------------------------------------------- + public function append($mode, $size, $data) + { + try { + $entry = new QRinputItem($mode, $size, $data); + $this->items[] = $entry; + return 0; + } catch (Exception $e) { + return -1; + } + } + + //---------------------------------------------------------------------- + + public function insertStructuredAppendHeader($size, $index, $parity) + { + if( $size > Constants::MAX_STRUCTURED_SYMBOLS ) { + throw new Exception('insertStructuredAppendHeader wrong size'); + } + + if( $index <= 0 || $index > Constants::MAX_STRUCTURED_SYMBOLS ) { + throw new Exception('insertStructuredAppendHeader wrong index'); + } + + $buf = array($size, $index, $parity); + + try { + $entry = new QRinputItem(Constants::QR_MODE_STRUCTURE, 3, buf); + array_unshift($this->items, $entry); + return 0; + } catch (Exception $e) { + return -1; + } + } + + //---------------------------------------------------------------------- + public function calcParity() + { + $parity = 0; + + foreach($this->items as $item) { + if($item->mode != Constants::QR_MODE_STRUCTURE) { + for($i=$item->size-1; $i>=0; $i--) { + $parity ^= $item->data[$i]; + } + } + } + + return $parity; + } + + //---------------------------------------------------------------------- + public static function checkModeNum($size, $data) + { + for($i=0; $i<$size; $i++) { + if((ord($data[$i]) < ord('0')) || (ord($data[$i]) > ord('9'))){ + return false; + } + } + + return true; + } + + //---------------------------------------------------------------------- + public static function estimateBitsModeNum($size) + { + $w = (int)$size / 3; + $bits = $w * 10; + + switch($size - $w * 3) { + case 1: + $bits += 4; + break; + case 2: + $bits += 7; + break; + default: + break; + } + + return $bits; + } + + //---------------------------------------------------------------------- + public static $anTable = array( + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 + ); + + //---------------------------------------------------------------------- + public static function lookAnTable($c) + { + return (($c > 127)?-1:self::$anTable[$c]); + } + + //---------------------------------------------------------------------- + public static function checkModeAn($size, $data) + { + for($i=0; $i<$size; $i++) { + if (self::lookAnTable(ord($data[$i])) == -1) { + return false; + } + } + + return true; + } + + //---------------------------------------------------------------------- + public static function estimateBitsModeAn($size) + { + $w = (int)($size / 2); + $bits = $w * 11; + + if($size & 1) { + $bits += 6; + } + + return $bits; + } + + //---------------------------------------------------------------------- + public static function estimateBitsMode8($size) + { + return $size * 8; + } + + //---------------------------------------------------------------------- + public function estimateBitsModeKanji($size) + { + return (int)(($size / 2) * 13); + } + + //---------------------------------------------------------------------- + public static function checkModeKanji($size, $data) + { + if($size & 1) + return false; + + for($i=0; $i<$size; $i+=2) { + $val = (ord($data[$i]) << 8) | ord($data[$i+1]); + if( $val < 0x8140 + || ($val > 0x9ffc && $val < 0xe040) + || $val > 0xebbf) { + return false; + } + } + + return true; + } + + /*********************************************************************** + * Validation + **********************************************************************/ + + public static function check($mode, $size, $data) + { + if($size <= 0) + return false; + + switch($mode) { + case Constants::QR_MODE_NUM: return self::checkModeNum($size, $data); break; + case Constants::QR_MODE_AN: return self::checkModeAn($size, $data); break; + case Constants::QR_MODE_KANJI: return self::checkModeKanji($size, $data); break; + case Constants::QR_MODE_8: return true; break; + case Constants::QR_MODE_STRUCTURE: return true; break; + + default: + break; + } + + return false; + } + + + //---------------------------------------------------------------------- + public function estimateBitStreamSize($version) + { + $bits = 0; + + foreach($this->items as $item) { + $bits += $item->estimateBitStreamSizeOfEntry($version); + } + + return $bits; + } + + //---------------------------------------------------------------------- + public function estimateVersion() + { + $version = 0; + $prev = 0; + do { + $prev = $version; + $bits = $this->estimateBitStreamSize($prev); + $version = QRspec::getMinimumVersion((int)(($bits + 7) / 8), $this->level); + if ($version < 0) { + return -1; + } + } while ($version > $prev); + + return $version; + } + + //---------------------------------------------------------------------- + public static function lengthOfCode($mode, $version, $bits) + { + $payload = $bits - 4 - QRspec::lengthIndicator($mode, $version); + switch($mode) { + case Constants::QR_MODE_NUM: + $chunks = (int)($payload / 10); + $remain = $payload - $chunks * 10; + $size = $chunks * 3; + if($remain >= 7) { + $size += 2; + } else if($remain >= 4) { + $size += 1; + } + break; + case Constants::QR_MODE_AN: + $chunks = (int)($payload / 11); + $remain = $payload - $chunks * 11; + $size = $chunks * 2; + if($remain >= 6) + $size++; + break; + case Constants::QR_MODE_8: + $size = (int)($payload / 8); + break; + case Constants::QR_MODE_KANJI: + $size = (int)(($payload / 13) * 2); + break; + case Constants::QR_MODE_STRUCTURE: + $size = (int)($payload / 8); + break; + default: + $size = 0; + break; + } + + $maxsize = QRspec::maximumWords($mode, $version); + if($size < 0) $size = 0; + if($size > $maxsize) $size = $maxsize; + + return $size; + } + + //---------------------------------------------------------------------- + public function createBitStream() + { + $total = 0; + + foreach($this->items as $item) { + $bits = $item->encodeBitStream($this->version); + + if($bits < 0) + return -1; + + $total += $bits; + } + + return $total; + } + + //---------------------------------------------------------------------- + public function convertData() + { + $ver = $this->estimateVersion(); + if($ver > $this->getVersion()) { + $this->setVersion($ver); + } + + for(;;) { + $bits = $this->createBitStream(); + + if($bits < 0) + return -1; + + $ver = QRspec::getMinimumVersion((int)(($bits + 7) / 8), $this->level); + if($ver < 0) { + throw new Exception('WRONG VERSION'); + return -1; + } else if($ver > $this->getVersion()) { + $this->setVersion($ver); + } else { + break; + } + } + + return 0; + } + + //---------------------------------------------------------------------- + public function appendPaddingBit(&$bstream) + { + $bits = $bstream->size(); + $maxwords = QRspec::getDataLength($this->version, $this->level); + $maxbits = $maxwords * 8; + + if ($maxbits == $bits) { + return 0; + } + + if ($maxbits - $bits < 5) { + return $bstream->appendNum($maxbits - $bits, 0); + } + + $bits += 4; + $words = (int)(($bits + 7) / 8); + + $padding = new QRbitstream(); + $ret = $padding->appendNum($words * 8 - $bits + 4, 0); + + if($ret < 0) + return $ret; + + $padlen = $maxwords - $words; + + if($padlen > 0) { + + $padbuf = array(); + for($i=0; $i<$padlen; $i++) { + $padbuf[$i] = ($i&1)?0x11:0xec; + } + + $ret = $padding->appendBytes($padlen, $padbuf); + + if($ret < 0) + return $ret; + + } + + $ret = $bstream->append($padding); + + return $ret; + } + + //---------------------------------------------------------------------- + public function mergeBitStream() + { + if($this->convertData() < 0) { + return null; + } + + $bstream = new QRbitstream(); + + foreach($this->items as $item) { + $ret = $bstream->append($item->bstream); + if($ret < 0) { + return null; + } + } + + return $bstream; + } + + //---------------------------------------------------------------------- + public function getBitStream() + { + + $bstream = $this->mergeBitStream(); + + if($bstream == null) { + return null; + } + + $ret = $this->appendPaddingBit($bstream); + if($ret < 0) { + return null; + } + + return $bstream; + } + + //---------------------------------------------------------------------- + public function getByteStream() + { + $bstream = $this->getBitStream(); + if($bstream == null) { + return null; + } + + return $bstream->toByte(); + } +} + + diff --git a/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRinputItem.php b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRinputItem.php new file mode 100644 index 0000000000..1e5eb18de2 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRinputItem.php @@ -0,0 +1,246 @@ +mode = $mode; + $this->size = $size; + $this->data = $setData; + $this->bstream = $bstream; + } + + //---------------------------------------------------------------------- + public function encodeModeNum($version) + { + try { + + $words = (int)($this->size / 3); + $bs = new QRbitstream(); + + $val = 0x1; + $bs->appendNum(4, $val); + $bs->appendNum(QRspec::lengthIndicator(Constants::QR_MODE_NUM, $version), $this->size); + + for($i=0; $i<$words; $i++) { + $val = (ord($this->data[$i*3 ]) - ord('0')) * 100; + $val += (ord($this->data[$i*3+1]) - ord('0')) * 10; + $val += (ord($this->data[$i*3+2]) - ord('0')); + $bs->appendNum(10, $val); + } + + if($this->size - $words * 3 == 1) { + $val = ord($this->data[$words*3]) - ord('0'); + $bs->appendNum(4, $val); + } else if($this->size - $words * 3 == 2) { + $val = (ord($this->data[$words*3 ]) - ord('0')) * 10; + $val += (ord($this->data[$words*3+1]) - ord('0')); + $bs->appendNum(7, $val); + } + + $this->bstream = $bs; + return 0; + + } catch (Exception $e) { + return -1; + } + } + + //---------------------------------------------------------------------- + public function encodeModeAn($version) + { + try { + $words = (int)($this->size / 2); + $bs = new QRbitstream(); + + $bs->appendNum(4, 0x02); + $bs->appendNum(QRspec::lengthIndicator(Constants::QR_MODE_AN, $version), $this->size); + + for($i=0; $i<$words; $i++) { + $val = (int)QRinput::lookAnTable(ord($this->data[$i*2 ])) * 45; + $val += (int)QRinput::lookAnTable(ord($this->data[$i*2+1])); + + $bs->appendNum(11, $val); + } + + if($this->size & 1) { + $val = QRinput::lookAnTable(ord($this->data[$words * 2])); + $bs->appendNum(6, $val); + } + + $this->bstream = $bs; + return 0; + + } catch (Exception $e) { + return -1; + } + } + + //---------------------------------------------------------------------- + public function encodeMode8($version) + { + try { + $bs = new QRbitstream(); + + $bs->appendNum(4, 0x4); + $bs->appendNum(QRspec::lengthIndicator(Constants::QR_MODE_8, $version), $this->size); + + for($i=0; $i<$this->size; $i++) { + $bs->appendNum(8, ord($this->data[$i])); + } + + $this->bstream = $bs; + return 0; + + } catch (Exception $e) { + return -1; + } + } + + //---------------------------------------------------------------------- + public function encodeModeKanji($version) + { + try { + + $bs = new QRbitstream(); + + $bs->appendNum(4, 0x8); + $bs->appendNum(QRspec::lengthIndicator(Constants::QR_MODE_KANJI, $version), (int)($this->size / 2)); + + for($i=0; $i<$this->size; $i+=2) { + $val = (ord($this->data[$i]) << 8) | ord($this->data[$i+1]); + if($val <= 0x9ffc) { + $val -= 0x8140; + } else { + $val -= 0xc140; + } + + $h = ($val >> 8) * 0xc0; + $val = ($val & 0xff) + $h; + + $bs->appendNum(13, $val); + } + + $this->bstream = $bs; + return 0; + + } catch (Exception $e) { + return -1; + } + } + + //---------------------------------------------------------------------- + public function encodeModeStructure() + { + try { + $bs = new QRbitstream(); + + $bs->appendNum(4, 0x03); + $bs->appendNum(4, ord($this->data[1]) - 1); + $bs->appendNum(4, ord($this->data[0]) - 1); + $bs->appendNum(8, ord($this->data[2])); + + $this->bstream = $bs; + return 0; + + } catch (Exception $e) { + return -1; + } + } + + //---------------------------------------------------------------------- + public function estimateBitStreamSizeOfEntry($version) + { + $bits = 0; + + if($version == 0) + $version = 1; + + switch($this->mode) { + case Constants::QR_MODE_NUM: $bits = QRinput::estimateBitsModeNum($this->size); break; + case Constants::QR_MODE_AN: $bits = QRinput::estimateBitsModeAn($this->size); break; + case Constants::QR_MODE_8: $bits = QRinput::estimateBitsMode8($this->size); break; + case Constants::QR_MODE_KANJI: $bits = QRinput::estimateBitsModeKanji($this->size);break; + case Constants::QR_MODE_STRUCTURE: return Constants::STRUCTURE_HEADER_BITS; + default: + return 0; + } + + $l = QRspec::lengthIndicator($this->mode, $version); + $m = 1 << $l; + $num = (int)(($this->size + $m - 1) / $m); + + $bits += $num * (4 + $l); + + return $bits; + } + + //---------------------------------------------------------------------- + public function encodeBitStream($version) + { + try { + + unset($this->bstream); + $words = QRspec::maximumWords($this->mode, $version); + + if($this->size > $words) { + + $st1 = new QRinputItem($this->mode, $words, $this->data); + $st2 = new QRinputItem($this->mode, $this->size - $words, array_slice($this->data, $words)); + + $st1->encodeBitStream($version); + $st2->encodeBitStream($version); + + $this->bstream = new QRbitstream(); + $this->bstream->append($st1->bstream); + $this->bstream->append($st2->bstream); + + unset($st1); + unset($st2); + + } else { + + $ret = 0; + + switch($this->mode) { + case Constants::QR_MODE_NUM: $ret = $this->encodeModeNum($version); break; + case Constants::QR_MODE_AN: $ret = $this->encodeModeAn($version); break; + case Constants::QR_MODE_8: $ret = $this->encodeMode8($version); break; + case Constants::QR_MODE_KANJI: $ret = $this->encodeModeKanji($version);break; + case Constants::QR_MODE_STRUCTURE: $ret = $this->encodeModeStructure(); break; + + default: + break; + } + + if($ret < 0) + return -1; + } + + return $this->bstream->size(); + + } catch (Exception $e) { + return -1; + } + } +} diff --git a/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRmask.php b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRmask.php new file mode 100755 index 0000000000..2be76f4710 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRmask.php @@ -0,0 +1,325 @@ + + * + * PHP QR Code is distributed under LGPL 3 + * Copyright (C) 2010 Dominik Dzienia + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +namespace PHPQRCode; + +class QRmask { + + public $runLength = array(); + + //---------------------------------------------------------------------- + public function __construct() + { + $this->runLength = array_fill(0, Constants::QRSPEC_WIDTH_MAX + 1, 0); + } + + //---------------------------------------------------------------------- + public function writeFormatInformation($width, &$frame, $mask, $level) + { + $blacks = 0; + $format = QRspec::getFormatInfo($mask, $level); + + for($i=0; $i<8; $i++) { + if($format & 1) { + $blacks += 2; + $v = 0x85; + } else { + $v = 0x84; + } + + $frame[8][$width - 1 - $i] = chr($v); + if($i < 6) { + $frame[$i][8] = chr($v); + } else { + $frame[$i + 1][8] = chr($v); + } + $format = $format >> 1; + } + + for($i=0; $i<7; $i++) { + if($format & 1) { + $blacks += 2; + $v = 0x85; + } else { + $v = 0x84; + } + + $frame[$width - 7 + $i][8] = chr($v); + if($i == 0) { + $frame[8][7] = chr($v); + } else { + $frame[8][6 - $i] = chr($v); + } + + $format = $format >> 1; + } + + return $blacks; + } + + //---------------------------------------------------------------------- + public function mask0($x, $y) { return ($x+$y)&1; } + public function mask1($x, $y) { return ($y&1); } + public function mask2($x, $y) { return ($x%3); } + public function mask3($x, $y) { return ($x+$y)%3; } + public function mask4($x, $y) { return (((int)($y/2))+((int)($x/3)))&1; } + public function mask5($x, $y) { return (($x*$y)&1)+($x*$y)%3; } + public function mask6($x, $y) { return ((($x*$y)&1)+($x*$y)%3)&1; } + public function mask7($x, $y) { return ((($x*$y)%3)+(($x+$y)&1))&1; } + + //---------------------------------------------------------------------- + private function generateMaskNo($maskNo, $width, $frame) + { + $bitMask = array_fill(0, $width, array_fill(0, $width, 0)); + + for($y=0; $y<$width; $y++) { + for($x=0; $x<$width; $x++) { + if(ord($frame[$y][$x]) & 0x80) { + $bitMask[$y][$x] = 0; + } else { + $maskFunc = call_user_func(array($this, 'mask'.$maskNo), $x, $y); + $bitMask[$y][$x] = ($maskFunc == 0)?1:0; + } + + } + } + + return $bitMask; + } + + //---------------------------------------------------------------------- + public static function serial($bitFrame) + { + $codeArr = array(); + + foreach ($bitFrame as $line) + $codeArr[] = join('', $line); + + return gzcompress(join("\n", $codeArr), 9); + } + + //---------------------------------------------------------------------- + public static function unserial($code) + { + $codeArr = array(); + + $codeLines = explode("\n", gzuncompress($code)); + foreach ($codeLines as $line) + $codeArr[] = str_split($line); + + return $codeArr; + } + + //---------------------------------------------------------------------- + public function makeMaskNo($maskNo, $width, $s, &$d, $maskGenOnly = false) + { + $b = 0; + $bitMask = array(); + + $fileName = Constants::QR_CACHE_DIR.'mask_'.$maskNo.DIRECTORY_SEPARATOR.'mask_'.$width.'_'.$maskNo.'.dat'; + + if (Constants::QR_CACHEABLE) { + if (file_exists($fileName)) { + $bitMask = self::unserial(file_get_contents($fileName)); + } else { + $bitMask = $this->generateMaskNo($maskNo, $width, $s, $d); + if (!file_exists(Constants::QR_CACHE_DIR.'mask_'.$maskNo)) + mkdir(Constants::QR_CACHE_DIR.'mask_'.$maskNo); + file_put_contents($fileName, self::serial($bitMask)); + } + } else { + $bitMask = $this->generateMaskNo($maskNo, $width, $s, $d); + } + + if ($maskGenOnly) + return; + + $d = $s; + + for($y=0; $y<$width; $y++) { + for($x=0; $x<$width; $x++) { + if($bitMask[$y][$x] == 1) { + $d[$y][$x] = chr(ord($s[$y][$x]) ^ (int)$bitMask[$y][$x]); + } + $b += (int)(ord($d[$y][$x]) & 1); + } + } + + return $b; + } + + //---------------------------------------------------------------------- + public function makeMask($width, $frame, $maskNo, $level) + { + $masked = array_fill(0, $width, str_repeat("\0", $width)); + $this->makeMaskNo($maskNo, $width, $frame, $masked); + $this->writeFormatInformation($width, $masked, $maskNo, $level); + + return $masked; + } + + //---------------------------------------------------------------------- + public function calcN1N3($length) + { + $demerit = 0; + + for($i=0; $i<$length; $i++) { + + if($this->runLength[$i] >= 5) { + $demerit += (Constants::N1 + ($this->runLength[$i] - 5)); + } + if($i & 1) { + if(($i >= 3) && ($i < ($length-2)) && ($this->runLength[$i] % 3 == 0)) { + $fact = (int)($this->runLength[$i] / 3); + if(($this->runLength[$i-2] == $fact) && + ($this->runLength[$i-1] == $fact) && + ($this->runLength[$i+1] == $fact) && + ($this->runLength[$i+2] == $fact)) { + if(($this->runLength[$i-3] < 0) || ($this->runLength[$i-3] >= (4 * $fact))) { + $demerit += Constants::N3; + } else if((($i+3) >= $length) || ($this->runLength[$i+3] >= (4 * $fact))) { + $demerit += Constants::N3; + } + } + } + } + } + return $demerit; + } + + //---------------------------------------------------------------------- + public function evaluateSymbol($width, $frame) + { + $head = 0; + $demerit = 0; + + for($y=0; $y<$width; $y++) { + $head = 0; + $this->runLength[0] = 1; + + $frameY = $frame[$y]; + + if ($y>0) + $frameYM = $frame[$y-1]; + + for($x=0; $x<$width; $x++) { + if(($x > 0) && ($y > 0)) { + $b22 = ord($frameY[$x]) & ord($frameY[$x-1]) & ord($frameYM[$x]) & ord($frameYM[$x-1]); + $w22 = ord($frameY[$x]) | ord($frameY[$x-1]) | ord($frameYM[$x]) | ord($frameYM[$x-1]); + + if(($b22 | ($w22 ^ 1))&1) { + $demerit += Constants::N2; + } + } + if(($x == 0) && (ord($frameY[$x]) & 1)) { + $this->runLength[0] = -1; + $head = 1; + $this->runLength[$head] = 1; + } else if($x > 0) { + if((ord($frameY[$x]) ^ ord($frameY[$x-1])) & 1) { + $head++; + $this->runLength[$head] = 1; + } else { + $this->runLength[$head]++; + } + } + } + + $demerit += $this->calcN1N3($head+1); + } + + for($x=0; $x<$width; $x++) { + $head = 0; + $this->runLength[0] = 1; + + for($y=0; $y<$width; $y++) { + if($y == 0 && (ord($frame[$y][$x]) & 1)) { + $this->runLength[0] = -1; + $head = 1; + $this->runLength[$head] = 1; + } else if($y > 0) { + if((ord($frame[$y][$x]) ^ ord($frame[$y-1][$x])) & 1) { + $head++; + $this->runLength[$head] = 1; + } else { + $this->runLength[$head]++; + } + } + } + + $demerit += $this->calcN1N3($head+1); + } + + return $demerit; + } + + + //---------------------------------------------------------------------- + public function mask($width, $frame, $level) + { + $minDemerit = PHP_INT_MAX; + $bestMaskNum = 0; + $bestMask = array(); + + $checked_masks = array(0,1,2,3,4,5,6,7); + + if (Constants::QR_FIND_FROM_RANDOM !== false) { + + $howManuOut = 8-(Constants::QR_FIND_FROM_RANDOM % 9); + for ($i = 0; $i < $howManuOut; $i++) { + $remPos = rand (0, count($checked_masks)-1); + unset($checked_masks[$remPos]); + $checked_masks = array_values($checked_masks); + } + + } + + $bestMask = $frame; + + foreach($checked_masks as $i) { + $mask = array_fill(0, $width, str_repeat("\0", $width)); + + $demerit = 0; + $blacks = 0; + $blacks = $this->makeMaskNo($i, $width, $frame, $mask); + $blacks += $this->writeFormatInformation($width, $mask, $i, $level); + $blacks = (int)(100 * $blacks / ($width * $width)); + $demerit = (int)((int)(abs($blacks - 50) / 5) * Constants::N4); + $demerit += $this->evaluateSymbol($width, $mask); + + if($demerit < $minDemerit) { + $minDemerit = $demerit; + $bestMask = $mask; + $bestMaskNum = $i; + } + } + + return $bestMask; + } + + //---------------------------------------------------------------------- +} diff --git a/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRrawcode.php b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRrawcode.php new file mode 100644 index 0000000000..25eae7c8d6 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRrawcode.php @@ -0,0 +1,117 @@ +datacode = $input->getByteStream(); + if(is_null($this->datacode)) { + throw new Exception('null input string'); + } + + QRspec::getEccSpec($input->getVersion(), $input->getErrorCorrectionLevel(), $spec); + + $this->version = $input->getVersion(); + $this->b1 = QRspec::rsBlockNum1($spec); + $this->dataLength = QRspec::rsDataLength($spec); + $this->eccLength = QRspec::rsEccLength($spec); + $this->ecccode = array_fill(0, $this->eccLength, 0); + $this->blocks = QRspec::rsBlockNum($spec); + + $ret = $this->init($spec); + if($ret < 0) { + throw new Exception('block alloc error'); + return null; + } + + $this->count = 0; + } + + //---------------------------------------------------------------------- + public function init(array $spec) + { + $dl = QRspec::rsDataCodes1($spec); + $el = QRspec::rsEccCodes1($spec); + $rs = QRrs::init_rs(8, 0x11d, 0, 1, $el, 255 - $dl - $el); + + + $blockNo = 0; + $dataPos = 0; + $eccPos = 0; + for($i=0; $iecccode,$eccPos); + $this->rsblocks[$blockNo] = new QRrsblock($dl, array_slice($this->datacode, $dataPos), $el, $ecc, $rs); + $this->ecccode = array_merge(array_slice($this->ecccode,0, $eccPos), $ecc); + + $dataPos += $dl; + $eccPos += $el; + $blockNo++; + } + + if(QRspec::rsBlockNum2($spec) == 0) + return 0; + + $dl = QRspec::rsDataCodes2($spec); + $el = QRspec::rsEccCodes2($spec); + $rs = QRrs::init_rs(8, 0x11d, 0, 1, $el, 255 - $dl - $el); + + if($rs == NULL) return -1; + + for($i=0; $iecccode,$eccPos); + $this->rsblocks[$blockNo] = new QRrsblock($dl, array_slice($this->datacode, $dataPos), $el, $ecc, $rs); + $this->ecccode = array_merge(array_slice($this->ecccode,0, $eccPos), $ecc); + + $dataPos += $dl; + $eccPos += $el; + $blockNo++; + } + + return 0; + } + + //---------------------------------------------------------------------- + public function getCode() + { + $ret = null; + + if($this->count < $this->dataLength) { + $row = $this->count % $this->blocks; + $col = $this->count / $this->blocks; + if($col >= $this->rsblocks[0]->dataLength) { + $row += $this->b1; + } + $ret = $this->rsblocks[$row]->data[$col]; + } else if($this->count < $this->dataLength + $this->eccLength) { + $row = ($this->count - $this->dataLength) % $this->blocks; + $col = ($this->count - $this->dataLength) / $this->blocks; + $ret = $this->rsblocks[$row]->ecc[$col]; + } else { + return 0; + } + $this->count++; + + return $ret; + } +} diff --git a/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRrs.php b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRrs.php new file mode 100755 index 0000000000..66f0d5e77c --- /dev/null +++ b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRrs.php @@ -0,0 +1,56 @@ + + * + * PHP QR Code is distributed under LGPL 3 + * Copyright (C) 2010 Dominik Dzienia + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +namespace PHPQRCode; + +class QRrs { + + public static $items = array(); + + //---------------------------------------------------------------------- + public static function init_rs($symsize, $gfpoly, $fcr, $prim, $nroots, $pad) + { + foreach(self::$items as $rs) { + if($rs->pad != $pad) continue; + if($rs->nroots != $nroots) continue; + if($rs->mm != $symsize) continue; + if($rs->gfpoly != $gfpoly) continue; + if($rs->fcr != $fcr) continue; + if($rs->prim != $prim) continue; + + return $rs; + } + + $rs = QRrsItem::init_rs_char($symsize, $gfpoly, $fcr, $prim, $nroots, $pad); + array_unshift(self::$items, $rs); + + return $rs; + } +} \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRrsItem.php b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRrsItem.php new file mode 100644 index 0000000000..ce63a8c356 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRrsItem.php @@ -0,0 +1,162 @@ += $this->nn) { + $x -= $this->nn; + $x = ($x >> $this->mm) + ($x & $this->nn); + } + + return $x; + } + + //---------------------------------------------------------------------- + public static function init_rs_char($symsize, $gfpoly, $fcr, $prim, $nroots, $pad) + { + // Common code for intializing a Reed-Solomon control block (char or int symbols) + // Copyright 2004 Phil Karn, KA9Q + // May be used under the terms of the GNU Lesser General Public License (LGPL) + + $rs = null; + + // Check parameter ranges + if($symsize < 0 || $symsize > 8) return $rs; + if($fcr < 0 || $fcr >= (1<<$symsize)) return $rs; + if($prim <= 0 || $prim >= (1<<$symsize)) return $rs; + if($nroots < 0 || $nroots >= (1<<$symsize)) return $rs; // Can't have more roots than symbol values! + if($pad < 0 || $pad >= ((1<<$symsize) -1 - $nroots)) return $rs; // Too much padding + + $rs = new QRrsItem(); + $rs->mm = $symsize; + $rs->nn = (1<<$symsize)-1; + $rs->pad = $pad; + + $rs->alpha_to = array_fill(0, $rs->nn+1, 0); + $rs->index_of = array_fill(0, $rs->nn+1, 0); + + // PHP style macro replacement ;) + $NN =& $rs->nn; + $A0 =& $NN; + + // Generate Galois field lookup tables + $rs->index_of[0] = $A0; // log(zero) = -inf + $rs->alpha_to[$A0] = 0; // alpha**-inf = 0 + $sr = 1; + + for($i=0; $i<$rs->nn; $i++) { + $rs->index_of[$sr] = $i; + $rs->alpha_to[$i] = $sr; + $sr <<= 1; + if($sr & (1<<$symsize)) { + $sr ^= $gfpoly; + } + $sr &= $rs->nn; + } + + if($sr != 1){ + // field generator polynomial is not primitive! + $rs = NULL; + return $rs; + } + + /* Form RS code generator polynomial from its roots */ + $rs->genpoly = array_fill(0, $nroots+1, 0); + + $rs->fcr = $fcr; + $rs->prim = $prim; + $rs->nroots = $nroots; + $rs->gfpoly = $gfpoly; + + /* Find prim-th root of 1, used in decoding */ + for($iprim=1;($iprim % $prim) != 0;$iprim += $rs->nn) + ; // intentional empty-body loop! + + $rs->iprim = (int)($iprim / $prim); + $rs->genpoly[0] = 1; + + for ($i = 0,$root=$fcr*$prim; $i < $nroots; $i++, $root += $prim) { + $rs->genpoly[$i+1] = 1; + + // Multiply rs->genpoly[] by @**(root + x) + for ($j = $i; $j > 0; $j--) { + if ($rs->genpoly[$j] != 0) { + $rs->genpoly[$j] = $rs->genpoly[$j-1] ^ $rs->alpha_to[$rs->modnn($rs->index_of[$rs->genpoly[$j]] + $root)]; + } else { + $rs->genpoly[$j] = $rs->genpoly[$j-1]; + } + } + // rs->genpoly[0] can never be zero + $rs->genpoly[0] = $rs->alpha_to[$rs->modnn($rs->index_of[$rs->genpoly[0]] + $root)]; + } + + // convert rs->genpoly[] to index form for quicker encoding + for ($i = 0; $i <= $nroots; $i++) + $rs->genpoly[$i] = $rs->index_of[$rs->genpoly[$i]]; + + return $rs; + } + + //---------------------------------------------------------------------- + public function encode_rs_char($data, &$parity) + { + $MM =& $this->mm; + $NN =& $this->nn; + $ALPHA_TO =& $this->alpha_to; + $INDEX_OF =& $this->index_of; + $GENPOLY =& $this->genpoly; + $NROOTS =& $this->nroots; + $FCR =& $this->fcr; + $PRIM =& $this->prim; + $IPRIM =& $this->iprim; + $PAD =& $this->pad; + $A0 =& $NN; + + $parity = array_fill(0, $NROOTS, 0); + + for($i=0; $i< ($NN-$NROOTS-$PAD); $i++) { + + $feedback = $INDEX_OF[$data[$i] ^ $parity[0]]; + if($feedback != $A0) { + // feedback term is non-zero + + // This line is unnecessary when GENPOLY[NROOTS] is unity, as it must + // always be for the polynomials constructed by init_rs() + $feedback = $this->modnn($NN - $GENPOLY[$NROOTS] + $feedback); + + for($j=1;$j<$NROOTS;$j++) { + $parity[$j] ^= $ALPHA_TO[$this->modnn($feedback + $GENPOLY[$NROOTS-$j])]; + } + } + + // Shift + array_shift($parity); + if($feedback != $A0) { + array_push($parity, $ALPHA_TO[$this->modnn($feedback + $GENPOLY[0])]); + } else { + array_push($parity, 0); + } + } + } +} \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRrsblock.php b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRrsblock.php new file mode 100644 index 0000000000..c1d01f22ef --- /dev/null +++ b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRrsblock.php @@ -0,0 +1,25 @@ +encode_rs_char($data, $ecc); + + $this->dataLength = $dl; + $this->data = $data; + $this->eccLength = $el; + $this->ecc = $ecc; + } +}; \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRspec.php b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRspec.php new file mode 100755 index 0000000000..d68432604e --- /dev/null +++ b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRspec.php @@ -0,0 +1,586 @@ + + * + * PHP QR Code is distributed under LGPL 3 + * Copyright (C) 2010 Dominik Dzienia + * + * The following data / specifications are taken from + * "Two dimensional symbol -- QR-code -- Basic Specification" (JIS X0510:2004) + * or + * "Automatic identification and data capture techniques -- + * QR Code 2005 bar code symbology specification" (ISO/IEC 18004:2006) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +namespace PHPQRCode; + +class QRspec { + + public static $capacity = array( + array( 0, 0, 0, array( 0, 0, 0, 0)), + array( 21, 26, 0, array( 7, 10, 13, 17)), // 1 + array( 25, 44, 7, array( 10, 16, 22, 28)), + array( 29, 70, 7, array( 15, 26, 36, 44)), + array( 33, 100, 7, array( 20, 36, 52, 64)), + array( 37, 134, 7, array( 26, 48, 72, 88)), // 5 + array( 41, 172, 7, array( 36, 64, 96, 112)), + array( 45, 196, 0, array( 40, 72, 108, 130)), + array( 49, 242, 0, array( 48, 88, 132, 156)), + array( 53, 292, 0, array( 60, 110, 160, 192)), + array( 57, 346, 0, array( 72, 130, 192, 224)), //10 + array( 61, 404, 0, array( 80, 150, 224, 264)), + array( 65, 466, 0, array( 96, 176, 260, 308)), + array( 69, 532, 0, array( 104, 198, 288, 352)), + array( 73, 581, 3, array( 120, 216, 320, 384)), + array( 77, 655, 3, array( 132, 240, 360, 432)), //15 + array( 81, 733, 3, array( 144, 280, 408, 480)), + array( 85, 815, 3, array( 168, 308, 448, 532)), + array( 89, 901, 3, array( 180, 338, 504, 588)), + array( 93, 991, 3, array( 196, 364, 546, 650)), + array( 97, 1085, 3, array( 224, 416, 600, 700)), //20 + array(101, 1156, 4, array( 224, 442, 644, 750)), + array(105, 1258, 4, array( 252, 476, 690, 816)), + array(109, 1364, 4, array( 270, 504, 750, 900)), + array(113, 1474, 4, array( 300, 560, 810, 960)), + array(117, 1588, 4, array( 312, 588, 870, 1050)), //25 + array(121, 1706, 4, array( 336, 644, 952, 1110)), + array(125, 1828, 4, array( 360, 700, 1020, 1200)), + array(129, 1921, 3, array( 390, 728, 1050, 1260)), + array(133, 2051, 3, array( 420, 784, 1140, 1350)), + array(137, 2185, 3, array( 450, 812, 1200, 1440)), //30 + array(141, 2323, 3, array( 480, 868, 1290, 1530)), + array(145, 2465, 3, array( 510, 924, 1350, 1620)), + array(149, 2611, 3, array( 540, 980, 1440, 1710)), + array(153, 2761, 3, array( 570, 1036, 1530, 1800)), + array(157, 2876, 0, array( 570, 1064, 1590, 1890)), //35 + array(161, 3034, 0, array( 600, 1120, 1680, 1980)), + array(165, 3196, 0, array( 630, 1204, 1770, 2100)), + array(169, 3362, 0, array( 660, 1260, 1860, 2220)), + array(173, 3532, 0, array( 720, 1316, 1950, 2310)), + array(177, 3706, 0, array( 750, 1372, 2040, 2430)) //40 + ); + + //---------------------------------------------------------------------- + public static function getDataLength($version, $level) + { + return self::$capacity[$version][Constants::QRCAP_WORDS] - self::$capacity[$version][Constants::QRCAP_EC][$level]; + } + + //---------------------------------------------------------------------- + public static function getECCLength($version, $level) + { + return self::$capacity[$version][Constants::QRCAP_EC][$level]; + } + + //---------------------------------------------------------------------- + public static function getWidth($version) + { + return self::$capacity[$version][Constants::QRCAP_WIDTH]; + } + + //---------------------------------------------------------------------- + public static function getRemainder($version) + { + return self::$capacity[$version][Constants::QRCAP_REMINDER]; + } + + //---------------------------------------------------------------------- + public static function getMinimumVersion($size, $level) + { + + for($i=1; $i<= Constants::QRSPEC_VERSION_MAX; $i++) { + $words = self::$capacity[$i][Constants::QRCAP_WORDS] - self::$capacity[$i][Constants::QRCAP_EC][$level]; + if($words >= $size) + return $i; + } + + return -1; + } + + //###################################################################### + + public static $lengthTableBits = array( + array(10, 12, 14), + array( 9, 11, 13), + array( 8, 16, 16), + array( 8, 10, 12) + ); + + //---------------------------------------------------------------------- + public static function lengthIndicator($mode, $version) + { + if ($mode == Constants::QR_MODE_STRUCTURE) + return 0; + + if ($version <= 9) { + $l = 0; + } else if ($version <= 26) { + $l = 1; + } else { + $l = 2; + } + + return self::$lengthTableBits[$mode][$l]; + } + + //---------------------------------------------------------------------- + public static function maximumWords($mode, $version) + { + if($mode == Constants::QR_MODE_STRUCTURE) + return 3; + + if($version <= 9) { + $l = 0; + } else if($version <= 26) { + $l = 1; + } else { + $l = 2; + } + + $bits = self::$lengthTableBits[$mode][$l]; + $words = (1 << $bits) - 1; + + if($mode == Constants::QR_MODE_KANJI) { + $words *= 2; // the number of bytes is required + } + + return $words; + } + + // Error correction code ----------------------------------------------- + // Table of the error correction code (Reed-Solomon block) + // See Table 12-16 (pp.30-36), JIS X0510:2004. + + public static $eccTable = array( + array(array( 0, 0), array( 0, 0), array( 0, 0), array( 0, 0)), + array(array( 1, 0), array( 1, 0), array( 1, 0), array( 1, 0)), // 1 + array(array( 1, 0), array( 1, 0), array( 1, 0), array( 1, 0)), + array(array( 1, 0), array( 1, 0), array( 2, 0), array( 2, 0)), + array(array( 1, 0), array( 2, 0), array( 2, 0), array( 4, 0)), + array(array( 1, 0), array( 2, 0), array( 2, 2), array( 2, 2)), // 5 + array(array( 2, 0), array( 4, 0), array( 4, 0), array( 4, 0)), + array(array( 2, 0), array( 4, 0), array( 2, 4), array( 4, 1)), + array(array( 2, 0), array( 2, 2), array( 4, 2), array( 4, 2)), + array(array( 2, 0), array( 3, 2), array( 4, 4), array( 4, 4)), + array(array( 2, 2), array( 4, 1), array( 6, 2), array( 6, 2)), //10 + array(array( 4, 0), array( 1, 4), array( 4, 4), array( 3, 8)), + array(array( 2, 2), array( 6, 2), array( 4, 6), array( 7, 4)), + array(array( 4, 0), array( 8, 1), array( 8, 4), array(12, 4)), + array(array( 3, 1), array( 4, 5), array(11, 5), array(11, 5)), + array(array( 5, 1), array( 5, 5), array( 5, 7), array(11, 7)), //15 + array(array( 5, 1), array( 7, 3), array(15, 2), array( 3, 13)), + array(array( 1, 5), array(10, 1), array( 1, 15), array( 2, 17)), + array(array( 5, 1), array( 9, 4), array(17, 1), array( 2, 19)), + array(array( 3, 4), array( 3, 11), array(17, 4), array( 9, 16)), + array(array( 3, 5), array( 3, 13), array(15, 5), array(15, 10)), //20 + array(array( 4, 4), array(17, 0), array(17, 6), array(19, 6)), + array(array( 2, 7), array(17, 0), array( 7, 16), array(34, 0)), + array(array( 4, 5), array( 4, 14), array(11, 14), array(16, 14)), + array(array( 6, 4), array( 6, 14), array(11, 16), array(30, 2)), + array(array( 8, 4), array( 8, 13), array( 7, 22), array(22, 13)), //25 + array(array(10, 2), array(19, 4), array(28, 6), array(33, 4)), + array(array( 8, 4), array(22, 3), array( 8, 26), array(12, 28)), + array(array( 3, 10), array( 3, 23), array( 4, 31), array(11, 31)), + array(array( 7, 7), array(21, 7), array( 1, 37), array(19, 26)), + array(array( 5, 10), array(19, 10), array(15, 25), array(23, 25)), //30 + array(array(13, 3), array( 2, 29), array(42, 1), array(23, 28)), + array(array(17, 0), array(10, 23), array(10, 35), array(19, 35)), + array(array(17, 1), array(14, 21), array(29, 19), array(11, 46)), + array(array(13, 6), array(14, 23), array(44, 7), array(59, 1)), + array(array(12, 7), array(12, 26), array(39, 14), array(22, 41)), //35 + array(array( 6, 14), array( 6, 34), array(46, 10), array( 2, 64)), + array(array(17, 4), array(29, 14), array(49, 10), array(24, 46)), + array(array( 4, 18), array(13, 32), array(48, 14), array(42, 32)), + array(array(20, 4), array(40, 7), array(43, 22), array(10, 67)), + array(array(19, 6), array(18, 31), array(34, 34), array(20, 61)),//40 + ); + + //---------------------------------------------------------------------- + // CACHEABLE!!! + + public static function getEccSpec($version, $level, array &$spec) + { + if (count($spec) < 5) { + $spec = array(0,0,0,0,0); + } + + $b1 = self::$eccTable[$version][$level][0]; + $b2 = self::$eccTable[$version][$level][1]; + $data = self::getDataLength($version, $level); + $ecc = self::getECCLength($version, $level); + + if($b2 == 0) { + $spec[0] = $b1; + $spec[1] = (int)($data / $b1); + $spec[2] = (int)($ecc / $b1); + $spec[3] = 0; + $spec[4] = 0; + } else { + $spec[0] = $b1; + $spec[1] = (int)($data / ($b1 + $b2)); + $spec[2] = (int)($ecc / ($b1 + $b2)); + $spec[3] = $b2; + $spec[4] = $spec[1] + 1; + } + } + + // Alignment pattern --------------------------------------------------- + + // Positions of alignment patterns. + // This array includes only the second and the third position of the + // alignment patterns. Rest of them can be calculated from the distance + // between them. + + // See Table 1 in Appendix E (pp.71) of JIS X0510:2004. + + public static $alignmentPattern = array( + array( 0, 0), + array( 0, 0), array(18, 0), array(22, 0), array(26, 0), array(30, 0), // 1- 5 + array(34, 0), array(22, 38), array(24, 42), array(26, 46), array(28, 50), // 6-10 + array(30, 54), array(32, 58), array(34, 62), array(26, 46), array(26, 48), //11-15 + array(26, 50), array(30, 54), array(30, 56), array(30, 58), array(34, 62), //16-20 + array(28, 50), array(26, 50), array(30, 54), array(28, 54), array(32, 58), //21-25 + array(30, 58), array(34, 62), array(26, 50), array(30, 54), array(26, 52), //26-30 + array(30, 56), array(34, 60), array(30, 58), array(34, 62), array(30, 54), //31-35 + array(24, 50), array(28, 54), array(32, 58), array(26, 54), array(30, 58), //35-40 + ); + + + /** -------------------------------------------------------------------- + * Put an alignment marker. + * @param frame + * @param width + * @param ox,oy center coordinate of the pattern + */ + public static function putAlignmentMarker(array &$frame, $ox, $oy) + { + $finder = array( + "\xa1\xa1\xa1\xa1\xa1", + "\xa1\xa0\xa0\xa0\xa1", + "\xa1\xa0\xa1\xa0\xa1", + "\xa1\xa0\xa0\xa0\xa1", + "\xa1\xa1\xa1\xa1\xa1" + ); + + $yStart = $oy-2; + $xStart = $ox-2; + + for($y=0; $y<5; $y++) { + QRstr::set($frame, $xStart, $yStart+$y, $finder[$y]); + } + } + + //---------------------------------------------------------------------- + public static function putAlignmentPattern($version, &$frame, $width) + { + if($version < 2) + return; + + $d = self::$alignmentPattern[$version][1] - self::$alignmentPattern[$version][0]; + if($d < 0) { + $w = 2; + } else { + $w = (int)(($width - self::$alignmentPattern[$version][0]) / $d + 2); + } + + if($w * $w - 3 == 1) { + $x = self::$alignmentPattern[$version][0]; + $y = self::$alignmentPattern[$version][0]; + self::putAlignmentMarker($frame, $x, $y); + return; + } + + $cx = self::$alignmentPattern[$version][0]; + for($x=1; $x<$w - 1; $x++) { + self::putAlignmentMarker($frame, 6, $cx); + self::putAlignmentMarker($frame, $cx, 6); + $cx += $d; + } + + $cy = self::$alignmentPattern[$version][0]; + for($y=0; $y<$w-1; $y++) { + $cx = self::$alignmentPattern[$version][0]; + for($x=0; $x<$w-1; $x++) { + self::putAlignmentMarker($frame, $cx, $cy); + $cx += $d; + } + $cy += $d; + } + } + + // Version information pattern ----------------------------------------- + + // Version information pattern (BCH coded). + // See Table 1 in Appendix D (pp.68) of JIS X0510:2004. + + // size: [Constants::QRSPEC_VERSION_MAX - 6] + + public static $versionPattern = array( + 0x07c94, 0x085bc, 0x09a99, 0x0a4d3, 0x0bbf6, 0x0c762, 0x0d847, 0x0e60d, + 0x0f928, 0x10b78, 0x1145d, 0x12a17, 0x13532, 0x149a6, 0x15683, 0x168c9, + 0x177ec, 0x18ec4, 0x191e1, 0x1afab, 0x1b08e, 0x1cc1a, 0x1d33f, 0x1ed75, + 0x1f250, 0x209d5, 0x216f0, 0x228ba, 0x2379f, 0x24b0b, 0x2542e, 0x26a64, + 0x27541, 0x28c69 + ); + + //---------------------------------------------------------------------- + public static function getVersionPattern($version) + { + if($version < 7 || $version > Constants::QRSPEC_VERSION_MAX) + return 0; + + return self::$versionPattern[$version -7]; + } + + // Format information -------------------------------------------------- + // See calcFormatInfo in tests/test_qrspec.c (orginal qrencode c lib) + + public static $formatInfo = array( + array(0x77c4, 0x72f3, 0x7daa, 0x789d, 0x662f, 0x6318, 0x6c41, 0x6976), + array(0x5412, 0x5125, 0x5e7c, 0x5b4b, 0x45f9, 0x40ce, 0x4f97, 0x4aa0), + array(0x355f, 0x3068, 0x3f31, 0x3a06, 0x24b4, 0x2183, 0x2eda, 0x2bed), + array(0x1689, 0x13be, 0x1ce7, 0x19d0, 0x0762, 0x0255, 0x0d0c, 0x083b) + ); + + public static function getFormatInfo($mask, $level) + { + if($mask < 0 || $mask > 7) + return 0; + + if($level < 0 || $level > 3) + return 0; + + return self::$formatInfo[$level][$mask]; + } + + // Frame --------------------------------------------------------------- + // Cache of initial frames. + + public static $frames = array(); + + /** -------------------------------------------------------------------- + * Put a finder pattern. + * @param frame + * @param width + * @param ox,oy upper-left coordinate of the pattern + */ + public static function putFinderPattern(&$frame, $ox, $oy) + { + $finder = array( + "\xc1\xc1\xc1\xc1\xc1\xc1\xc1", + "\xc1\xc0\xc0\xc0\xc0\xc0\xc1", + "\xc1\xc0\xc1\xc1\xc1\xc0\xc1", + "\xc1\xc0\xc1\xc1\xc1\xc0\xc1", + "\xc1\xc0\xc1\xc1\xc1\xc0\xc1", + "\xc1\xc0\xc0\xc0\xc0\xc0\xc1", + "\xc1\xc1\xc1\xc1\xc1\xc1\xc1" + ); + + for($y=0; $y<7; $y++) { + QRstr::set($frame, $ox, $oy+$y, $finder[$y]); + } + } + + //---------------------------------------------------------------------- + public static function createFrame($version) + { + $width = self::$capacity[$version][Constants::QRCAP_WIDTH]; + $frameLine = str_repeat ("\0", $width); + $frame = array_fill(0, $width, $frameLine); + + // Finder pattern + self::putFinderPattern($frame, 0, 0); + self::putFinderPattern($frame, $width - 7, 0); + self::putFinderPattern($frame, 0, $width - 7); + + // Separator + $yOffset = $width - 7; + + for($y=0; $y<7; $y++) { + $frame[$y][7] = "\xc0"; + $frame[$y][$width - 8] = "\xc0"; + $frame[$yOffset][7] = "\xc0"; + $yOffset++; + } + + $setPattern = str_repeat("\xc0", 8); + + QRstr::set($frame, 0, 7, $setPattern); + QRstr::set($frame, $width-8, 7, $setPattern); + QRstr::set($frame, 0, $width - 8, $setPattern); + + // Format info + $setPattern = str_repeat("\x84", 9); + QRstr::set($frame, 0, 8, $setPattern); + QRstr::set($frame, $width - 8, 8, $setPattern, 8); + + $yOffset = $width - 8; + + for($y=0; $y<8; $y++,$yOffset++) { + $frame[$y][8] = "\x84"; + $frame[$yOffset][8] = "\x84"; + } + + // Timing pattern + + for($i=1; $i<$width-15; $i++) { + $frame[6][7+$i] = chr(0x90 | ($i & 1)); + $frame[7+$i][6] = chr(0x90 | ($i & 1)); + } + + // Alignment pattern + self::putAlignmentPattern($version, $frame, $width); + + // Version information + if($version >= 7) { + $vinf = self::getVersionPattern($version); + + $v = $vinf; + + for($x=0; $x<6; $x++) { + for($y=0; $y<3; $y++) { + $frame[($width - 11)+$y][$x] = chr(0x88 | ($v & 1)); + $v = $v >> 1; + } + } + + $v = $vinf; + for($y=0; $y<6; $y++) { + for($x=0; $x<3; $x++) { + $frame[$y][$x+($width - 11)] = chr(0x88 | ($v & 1)); + $v = $v >> 1; + } + } + } + + // and a little bit... + $frame[$width - 8][8] = "\x81"; + + return $frame; + } + + //---------------------------------------------------------------------- + public static function debug($frame, $binary_mode = false) + { + if ($binary_mode) { + + foreach ($frame as &$frameLine) { + $frameLine = join('  ', explode('0', $frameLine)); + $frameLine = join('██', explode('1', $frameLine)); + } + + ?> + +


        '; + echo join("
        ", $frame); + echo '






'; + + } else { + + foreach ($frame as &$frameLine) { + $frameLine = join(' ', explode("\xc0", $frameLine)); + $frameLine = join('', explode("\xc1", $frameLine)); + $frameLine = join(' ', explode("\xa0", $frameLine)); + $frameLine = join('', explode("\xa1", $frameLine)); + $frameLine = join('', explode("\x84", $frameLine)); //format 0 + $frameLine = join('', explode("\x85", $frameLine)); //format 1 + $frameLine = join('', explode("\x81", $frameLine)); //special bit + $frameLine = join(' ', explode("\x90", $frameLine)); //clock 0 + $frameLine = join('', explode("\x91", $frameLine)); //clock 1 + $frameLine = join(' ', explode("\x88", $frameLine)); //version + $frameLine = join('', explode("\x89", $frameLine)); //version + $frameLine = join('♦', explode("\x01", $frameLine)); + $frameLine = join('⋅', explode("\0", $frameLine)); + } + + ?> + + "; + echo join("
", $frame); + echo "
"; + + } + } + + //---------------------------------------------------------------------- + public static function serial($frame) + { + return gzcompress(join("\n", $frame), 9); + } + + //---------------------------------------------------------------------- + public static function unserial($code) + { + return explode("\n", gzuncompress($code)); + } + + //---------------------------------------------------------------------- + public static function newFrame($version) + { + if($version < 1 || $version > Constants::QRSPEC_VERSION_MAX) + return null; + + if(!isset(self::$frames[$version])) { + + $fileName = Constants::QR_CACHE_DIR.'frame_'.$version.'.dat'; + + if (Constants::QR_CACHEABLE) { + if (file_exists($fileName)) { + self::$frames[$version] = self::unserial(file_get_contents($fileName)); + } else { + self::$frames[$version] = self::createFrame($version); + file_put_contents($fileName, self::serial(self::$frames[$version])); + } + } else { + self::$frames[$version] = self::createFrame($version); + } + } + + if(is_null(self::$frames[$version])) + return null; + + return self::$frames[$version]; + } + + //---------------------------------------------------------------------- + public static function rsBlockNum($spec) { return $spec[0] + $spec[3]; } + public static function rsBlockNum1($spec) { return $spec[0]; } + public static function rsDataCodes1($spec) { return $spec[1]; } + public static function rsEccCodes1($spec) { return $spec[2]; } + public static function rsBlockNum2($spec) { return $spec[3]; } + public static function rsDataCodes2($spec) { return $spec[4]; } + public static function rsEccCodes2($spec) { return $spec[2]; } + public static function rsDataLength($spec) { return ($spec[0] * $spec[1]) + ($spec[3] * $spec[4]); } + public static function rsEccLength($spec) { return ($spec[0] + $spec[3]) * $spec[2]; } + +} \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRsplit.php b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRsplit.php new file mode 100755 index 0000000000..805140a97c --- /dev/null +++ b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRsplit.php @@ -0,0 +1,316 @@ + + * + * PHP QR Code is distributed under LGPL 3 + * Copyright (C) 2010 Dominik Dzienia + * + * The following data / specifications are taken from + * "Two dimensional symbol -- QR-code -- Basic Specification" (JIS X0510:2004) + * or + * "Automatic identification and data capture techniques -- + * QR Code 2005 bar code symbology specification" (ISO/IEC 18004:2006) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +namespace PHPQRCode; + +use Exception; + +class QRsplit { + + public $dataStr = ''; + public $input; + public $modeHint; + + //---------------------------------------------------------------------- + public function __construct($dataStr, $input, $modeHint) + { + $this->dataStr = $dataStr; + $this->input = $input; + $this->modeHint = $modeHint; + } + + //---------------------------------------------------------------------- + public static function isdigitat($str, $pos) + { + if ($pos >= strlen($str)) + return false; + + return ((ord($str[$pos]) >= ord('0'))&&(ord($str[$pos]) <= ord('9'))); + } + + //---------------------------------------------------------------------- + public static function isalnumat($str, $pos) + { + if ($pos >= strlen($str)) + return false; + + return (QRinput::lookAnTable(ord($str[$pos])) >= 0); + } + + //---------------------------------------------------------------------- + public function identifyMode($pos) + { + if ($pos >= strlen($this->dataStr)) + return Constants::QR_MODE_NUL; + + $c = $this->dataStr[$pos]; + + if(self::isdigitat($this->dataStr, $pos)) { + return Constants::QR_MODE_NUM; + } else if(self::isalnumat($this->dataStr, $pos)) { + return Constants::QR_MODE_AN; + } else if($this->modeHint == Constants::QR_MODE_KANJI) { + + if ($pos+1 < strlen($this->dataStr)) + { + $d = $this->dataStr[$pos+1]; + $word = (ord($c) << 8) | ord($d); + if(($word >= 0x8140 && $word <= 0x9ffc) || ($word >= 0xe040 && $word <= 0xebbf)) { + return Constants::QR_MODE_KANJI; + } + } + } + + return Constants::QR_MODE_8; + } + + //---------------------------------------------------------------------- + public function eatNum() + { + $ln = QRspec::lengthIndicator(Constants::QR_MODE_NUM, $this->input->getVersion()); + + $p = 0; + while(self::isdigitat($this->dataStr, $p)) { + $p++; + } + + $run = $p; + $mode = $this->identifyMode($p); + + if($mode == Constants::QR_MODE_8) { + $dif = QRinput::estimateBitsModeNum($run) + 4 + $ln + + QRinput::estimateBitsMode8(1) // + 4 + l8 + - QRinput::estimateBitsMode8($run + 1); // - 4 - l8 + if($dif > 0) { + return $this->eat8(); + } + } + if($mode == Constants::QR_MODE_AN) { + $dif = QRinput::estimateBitsModeNum($run) + 4 + $ln + + QRinput::estimateBitsModeAn(1) // + 4 + la + - QRinput::estimateBitsModeAn($run + 1);// - 4 - la + if($dif > 0) { + return $this->eatAn(); + } + } + + $ret = $this->input->append(Constants::QR_MODE_NUM, $run, str_split($this->dataStr)); + if($ret < 0) + return -1; + + return $run; + } + + //---------------------------------------------------------------------- + public function eatAn() + { + $la = QRspec::lengthIndicator(Constants::QR_MODE_AN, $this->input->getVersion()); + $ln = QRspec::lengthIndicator(Constants::QR_MODE_NUM, $this->input->getVersion()); + + $p = 0; + + while(self::isalnumat($this->dataStr, $p)) { + if(self::isdigitat($this->dataStr, $p)) { + $q = $p; + while(self::isdigitat($this->dataStr, $q)) { + $q++; + } + + $dif = QRinput::estimateBitsModeAn($p) // + 4 + la + + QRinput::estimateBitsModeNum($q - $p) + 4 + $ln + - QRinput::estimateBitsModeAn($q); // - 4 - la + + if($dif < 0) { + break; + } else { + $p = $q; + } + } else { + $p++; + } + } + + $run = $p; + + if(!self::isalnumat($this->dataStr, $p)) { + $dif = QRinput::estimateBitsModeAn($run) + 4 + $la + + QRinput::estimateBitsMode8(1) // + 4 + l8 + - QRinput::estimateBitsMode8($run + 1); // - 4 - l8 + if($dif > 0) { + return $this->eat8(); + } + } + + $ret = $this->input->append(Constants::QR_MODE_AN, $run, str_split($this->dataStr)); + if($ret < 0) + return -1; + + return $run; + } + + //---------------------------------------------------------------------- + public function eatKanji() + { + $p = 0; + + while($this->identifyMode($p) == Constants::QR_MODE_KANJI) { + $p += 2; + } + + $ret = $this->input->append(Constants::QR_MODE_KANJI, $p, str_split($this->dataStr)); + if($ret < 0) + return -1; + + return $ret; + } + + //---------------------------------------------------------------------- + public function eat8() + { + $la = QRspec::lengthIndicator(Constants::QR_MODE_AN, $this->input->getVersion()); + $ln = QRspec::lengthIndicator(Constants::QR_MODE_NUM, $this->input->getVersion()); + + $p = 1; + $dataStrLen = strlen($this->dataStr); + + while($p < $dataStrLen) { + + $mode = $this->identifyMode($p); + if($mode == Constants::QR_MODE_KANJI) { + break; + } + if($mode == Constants::QR_MODE_NUM) { + $q = $p; + while(self::isdigitat($this->dataStr, $q)) { + $q++; + } + $dif = QRinput::estimateBitsMode8($p) // + 4 + l8 + + QRinput::estimateBitsModeNum($q - $p) + 4 + $ln + - QRinput::estimateBitsMode8($q); // - 4 - l8 + if($dif < 0) { + break; + } else { + $p = $q; + } + } else if($mode == Constants::QR_MODE_AN) { + $q = $p; + while(self::isalnumat($this->dataStr, $q)) { + $q++; + } + $dif = QRinput::estimateBitsMode8($p) // + 4 + l8 + + QRinput::estimateBitsModeAn($q - $p) + 4 + $la + - QRinput::estimateBitsMode8($q); // - 4 - l8 + if($dif < 0) { + break; + } else { + $p = $q; + } + } else { + $p++; + } + } + + $run = $p; + $ret = $this->input->append(Constants::QR_MODE_8, $run, str_split($this->dataStr)); + + if($ret < 0) + return -1; + + return $run; + } + + //---------------------------------------------------------------------- + public function splitString() + { + while (strlen($this->dataStr) > 0) + { + if($this->dataStr == '') + return 0; + + $mode = $this->identifyMode(0); + + switch ($mode) { + case Constants::QR_MODE_NUM: $length = $this->eatNum(); break; + case Constants::QR_MODE_AN: $length = $this->eatAn(); break; + case Constants::QR_MODE_KANJI: + if ($hint == Constants::QR_MODE_KANJI) + $length = $this->eatKanji(); + else $length = $this->eat8(); + break; + default: $length = $this->eat8(); break; + + } + + if($length == 0) return 0; + if($length < 0) return -1; + + $this->dataStr = substr($this->dataStr, $length); + } + } + + //---------------------------------------------------------------------- + public function toUpper() + { + $stringLen = strlen($this->dataStr); + $p = 0; + + while ($p<$stringLen) { + $mode = self::identifyMode(substr($this->dataStr, $p), $this->modeHint); + if($mode == Constants::QR_MODE_KANJI) { + $p += 2; + } else { + if (ord($this->dataStr[$p]) >= ord('a') && ord($this->dataStr[$p]) <= ord('z')) { + $this->dataStr[$p] = chr(ord($this->dataStr[$p]) - 32); + } + $p++; + } + } + + return $this->dataStr; + } + + //---------------------------------------------------------------------- + public static function splitStringToQRinput($string, QRinput $input, $modeHint, $casesensitive = true) + { + if(is_null($string) || $string == '\0' || $string == '') { + throw new Exception('empty string!!!'); + } + + $split = new QRsplit($string, $input, $modeHint); + + if(!$casesensitive) + $split->toUpper(); + + return $split->splitString(); + } +} diff --git a/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRstr.php b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRstr.php new file mode 100644 index 0000000000..64c4bd5c68 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRstr.php @@ -0,0 +1,35 @@ + + * + * PHP QR Code is distributed under LGPL 3 + * Copyright (C) 2010 Dominik Dzienia + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +namespace PHPQRCode; + +class QRstr { + public static function set(&$srctab, $x, $y, $repl, $replLen = false) { + $srctab[$y] = substr_replace($srctab[$y], ($replLen !== false)?substr($repl,0,$replLen):$repl, $x, ($replLen !== false)?$replLen:strlen($repl)); + } +} \ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRtools.php b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRtools.php new file mode 100755 index 0000000000..7c75a6e2a6 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRtools.php @@ -0,0 +1,171 @@ + + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +namespace PHPQRCode; + +class QRtools { + + //---------------------------------------------------------------------- + public static function binarize($frame) + { + $len = count($frame); + foreach ($frame as &$frameLine) { + + for($i=0; $i<$len; $i++) { + $frameLine[$i] = (ord($frameLine[$i])&1)?'1':'0'; + } + } + + return $frame; + } + + //---------------------------------------------------------------------- + public static function tcpdfBarcodeArray($code, $mode = 'QR,L', $tcPdfVersion = '4.5.037') + { + $barcode_array = array(); + + if (!is_array($mode)) + $mode = explode(',', $mode); + + $eccLevel = 'L'; + + if (count($mode) > 1) { + $eccLevel = $mode[1]; + } + + $qrTab = QRcode::text($code, false, $eccLevel); + $size = count($qrTab); + + $barcode_array['num_rows'] = $size; + $barcode_array['num_cols'] = $size; + $barcode_array['bcode'] = array(); + + foreach ($qrTab as $line) { + $arrAdd = array(); + foreach(str_split($line) as $char) + $arrAdd[] = ($char=='1')?1:0; + $barcode_array['bcode'][] = $arrAdd; + } + + return $barcode_array; + } + + //---------------------------------------------------------------------- + public static function clearCache() + { + self::$frames = array(); + } + + //---------------------------------------------------------------------- + public static function buildCache() + { + QRtools::markTime('before_build_cache'); + + $mask = new QRmask(); + for ($a=1; $a <= Constants::QRSPEC_VERSION_MAX; $a++) { + $frame = QRspec::newFrame($a); + if (Constants::QR_IMAGE) { + $fileName = Constants::QR_CACHE_DIR.'frame_'.$a.'.png'; + QRimage::png(self::binarize($frame), $fileName, 1, 0); + } + + $width = count($frame); + $bitMask = array_fill(0, $width, array_fill(0, $width, 0)); + for ($maskNo=0; $maskNo<8; $maskNo++) + $mask->makeMaskNo($maskNo, $width, $frame, $bitMask, true); + } + + QRtools::markTime('after_build_cache'); + } + + //---------------------------------------------------------------------- + public static function log($outfile, $err) + { + if (Constants::QR_LOG_DIR !== false) { + if ($err != '') { + if ($outfile !== false) { + file_put_contents(Constants::QR_LOG_DIR.basename($outfile).'-errors.txt', date('Y-m-d H:i:s').': '.$err, FILE_APPEND); + } else { + file_put_contents(Constants::QR_LOG_DIR.'errors.txt', date('Y-m-d H:i:s').': '.$err, FILE_APPEND); + } + } + } + } + + //---------------------------------------------------------------------- + public static function dumpMask($frame) + { + $width = count($frame); + for($y=0;$y<$width;$y++) { + for($x=0;$x<$width;$x++) { + echo ord($frame[$y][$x]).','; + } + } + } + + //---------------------------------------------------------------------- + public static function markTime($markerId) + { + list($usec, $sec) = explode(" ", microtime()); + $time = ((float)$usec + (float)$sec); + + if (!isset($GLOBALS['qr_time_bench'])) + $GLOBALS['qr_time_bench'] = array(); + + $GLOBALS['qr_time_bench'][$markerId] = $time; + } + + //---------------------------------------------------------------------- + public static function timeBenchmark() + { + self::markTime('finish'); + + $lastTime = 0; + $startTime = 0; + $p = 0; + + echo ' + + '; + + foreach($GLOBALS['qr_time_bench'] as $markerId=>$thisTime) { + if ($p > 0) { + echo ''; + } else { + $startTime = $thisTime; + } + + $p++; + $lastTime = $thisTime; + } + + echo ' + + +
BENCHMARK
till '.$markerId.': '.number_format($thisTime-$lastTime, 6).'s
TOTAL: '.number_format($lastTime-$startTime, 6).'s
'; + } + +} + +QRtools::markTime('start'); diff --git a/vendor/aferrandini/phpqrcode/readme.md b/vendor/aferrandini/phpqrcode/readme.md new file mode 100755 index 0000000000..e8f2f5ab3f --- /dev/null +++ b/vendor/aferrandini/phpqrcode/readme.md @@ -0,0 +1,37 @@ +# PHP QRCode Library + +To install this library please follow the next steps: + +## Install the library using `composer`: + +Add the required module to your `composer.json` file: + + { + "require": { + ... + "aferrandini/phpqrcode": "1.0.1" + ... + } + } + +Then run the command `composer update`. + + +## Usage + +Sample code: + + \PHPQRCode\QRcode::png("Test", "/tmp/qrcode.png", 'L', 4, 2); + +This code will generate a PNG file on '/tmp/qrcode.png' with a QRCode that contains the word 'Test'. + +## Acknowledgements + +This library is an import of PHP QR Code by Dominik Dzienia that you can find at http://phpqrcode.sourceforge.net + +Based on C libqrencode library (ver. 3.1.1), Copyright (C) 2006-2010 by Kentaro Fukuchi +http://megaui.net/fukuchi/works/qrencode/index.en.html + +QR Code is registered trademarks of DENSO WAVE INCORPORATED in JAPAN and other countries. + +Reed-Solomon code encoder is written by Phil Karn, KA9Q. Copyright (C) 2002, 2003, 2004, 2006 Phil Karn, KA9Q diff --git a/vendor/autoload.php b/vendor/autoload.php new file mode 100644 index 0000000000..099ee6b978 --- /dev/null +++ b/vendor/autoload.php @@ -0,0 +1,7 @@ + http://christianriesen.com + +Acknowledgements +---------------- + +Base32 is mostly based on the work of https://github.com/NTICompass/PHP-Base32 + diff --git a/vendor/christian-riesen/base32/build.xml b/vendor/christian-riesen/base32/build.xml new file mode 100644 index 0000000000..c06a49f97d --- /dev/null +++ b/vendor/christian-riesen/base32/build.xml @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/christian-riesen/base32/composer.json b/vendor/christian-riesen/base32/composer.json new file mode 100644 index 0000000000..0c61fbf7ac --- /dev/null +++ b/vendor/christian-riesen/base32/composer.json @@ -0,0 +1,33 @@ +{ + "name": "christian-riesen/base32", + "type": "library", + "description": "Base32 encoder/decoder according to RFC 4648", + "keywords": ["base32","encode","decode","rfc4648"], + "homepage": "https://github.com/ChristianRiesen/base32", + "license": "MIT", + "authors": [ + { + "name": "Christian Riesen", + "email": "chris.riesen@gmail.com", + "homepage": "http://christianriesen.com", + "role": "Developer" + } + ], + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*", + "satooshi/php-coveralls": "0.*" + }, + "autoload": { + "psr-4": { + "Base32\\": "src/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + } +} diff --git a/vendor/christian-riesen/base32/phpunit.xml.dist b/vendor/christian-riesen/base32/phpunit.xml.dist new file mode 100644 index 0000000000..3e2def6f7c --- /dev/null +++ b/vendor/christian-riesen/base32/phpunit.xml.dist @@ -0,0 +1,32 @@ + + + + + tests/ + + + + + + src/ + + + + + + + + + diff --git a/vendor/christian-riesen/base32/src/Base32.php b/vendor/christian-riesen/base32/src/Base32.php new file mode 100644 index 0000000000..bf79018267 --- /dev/null +++ b/vendor/christian-riesen/base32/src/Base32.php @@ -0,0 +1,146 @@ + + * @link http://christianriesen.com + * @license MIT License see LICENSE file + */ +class Base32 +{ + /** + * Alphabet for encoding and decoding base32 + * + * @var array + */ + private static $alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567='; + + /** + * Creates an array from a binary string into a given chunk size + * + * @param string $binaryString String to chunk + * @param integer $bits Number of bits per chunk + * @return array + */ + private static function chunk($binaryString, $bits) + { + $binaryString = chunk_split($binaryString, $bits, ' '); + + if (substr($binaryString, (strlen($binaryString)) - 1) == ' ') { + $binaryString = substr($binaryString, 0, strlen($binaryString)-1); + } + + return explode(' ', $binaryString); + } + + /** + * Encodes into base32 + * + * @param string $string Clear text string + * @return string Base32 encoded string + */ + public static function encode($string) + { + if (strlen($string) == 0) { + // Gives an empty string + + return ''; + } + + // Convert string to binary + $binaryString = ''; + + foreach (str_split($string) as $s) { + // Return each character as an 8-bit binary string + $binaryString .= sprintf('%08b', ord($s)); + } + + // Break into 5-bit chunks, then break that into an array + $binaryArray = self::chunk($binaryString, 5); + + // Pad array to be divisible by 8 + while (count($binaryArray) % 8 !== 0) { + $binaryArray[] = null; + } + + $base32String = ''; + + // Encode in base32 + foreach ($binaryArray as $bin) { + $char = 32; + + if (!is_null($bin)) { + // Pad the binary strings + $bin = str_pad($bin, 5, 0, STR_PAD_RIGHT); + $char = bindec($bin); + } + + // Base32 character + $base32String .= self::$alphabet[$char]; + } + + return $base32String; + } + + /** + * Decodes base32 + * + * @param string $base32String Base32 encoded string + * @return string Clear text string + */ + public static function decode($base32String) + { + // Only work in upper cases + $base32String = strtoupper($base32String); + + // Remove anything that is not base32 alphabet + $pattern = '/[^A-Z2-7]/'; + + $base32String = preg_replace($pattern, '', $base32String); + + if (strlen($base32String) == 0) { + // Gives an empty string + return ''; + } + + $base32Array = str_split($base32String); + + $string = ''; + + foreach ($base32Array as $str) { + $char = strpos(self::$alphabet, $str); + + // Ignore the padding character + if ($char !== 32) { + $string .= sprintf('%05b', $char); + } + } + + while (strlen($string) %8 !== 0) { + $string = substr($string, 0, strlen($string)-1); + } + + $binaryArray = self::chunk($string, 8); + + $realString = ''; + + foreach ($binaryArray as $bin) { + // Pad each value to 8 bits + $bin = str_pad($bin, 8, 0, STR_PAD_RIGHT); + // Convert binary strings to ASCII + $realString .= chr(bindec($bin)); + } + + return $realString; + } +} diff --git a/vendor/christian-riesen/base32/tests/Base32Test.php b/vendor/christian-riesen/base32/tests/Base32Test.php new file mode 100644 index 0000000000..3e5924ce9a --- /dev/null +++ b/vendor/christian-riesen/base32/tests/Base32Test.php @@ -0,0 +1,51 @@ +decode() + * + * Testing test vectors according to RFC 4648 + * http://www.ietf.org/rfc/rfc4648.txt + */ + public function testDecode() + { + // RFC test vectors say that empty string returns empty string + $this->assertEquals('', Base32::decode('')); + + // these strings are taken from the RFC + $this->assertEquals('f', Base32::decode('MY======')); + $this->assertEquals('fo', Base32::decode('MZXQ====')); + $this->assertEquals('foo', Base32::decode('MZXW6===')); + $this->assertEquals('foob', Base32::decode('MZXW6YQ=')); + $this->assertEquals('fooba', Base32::decode('MZXW6YTB')); + $this->assertEquals('foobar', Base32::decode('MZXW6YTBOI======')); + + // Decoding a string made up entirely of invalid characters + $this->assertEquals('', Base32::decode('8908908908908908')); + } + + /** + * Encoder tests, reverse of the decodes + */ + public function testEncode() + { + // RFC test vectors say that empty string returns empty string + $this->assertEquals('', Base32::encode('')); + + // these strings are taken from the RFC + $this->assertEquals('MY======', Base32::encode('f')); + $this->assertEquals('MZXQ====', Base32::encode('fo')); + $this->assertEquals('MZXW6===', Base32::encode('foo')); + $this->assertEquals('MZXW6YQ=', Base32::encode('foob')); + $this->assertEquals('MZXW6YTB', Base32::encode('fooba')); + $this->assertEquals('MZXW6YTBOI======', Base32::encode('foobar')); + } +} diff --git a/vendor/christian-riesen/base32/tests/bootstrap.php b/vendor/christian-riesen/base32/tests/bootstrap.php new file mode 100644 index 0000000000..12bea5b14c --- /dev/null +++ b/vendor/christian-riesen/base32/tests/bootstrap.php @@ -0,0 +1,5 @@ +add("Base32", __DIR__); +$loader->register(); diff --git a/vendor/christian-riesen/otp/.gitignore b/vendor/christian-riesen/otp/.gitignore new file mode 100644 index 0000000000..bab62331de --- /dev/null +++ b/vendor/christian-riesen/otp/.gitignore @@ -0,0 +1,5 @@ +.svn +/.buildpath +/.project +/.settings + diff --git a/vendor/christian-riesen/otp/.travis.yml b/vendor/christian-riesen/otp/.travis.yml new file mode 100644 index 0000000000..b863299eb0 --- /dev/null +++ b/vendor/christian-riesen/otp/.travis.yml @@ -0,0 +1,11 @@ +language: php + +php: + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - 7.0 + +script: phpunit + diff --git a/vendor/christian-riesen/otp/LICENSE b/vendor/christian-riesen/otp/LICENSE new file mode 100644 index 0000000000..7089474646 --- /dev/null +++ b/vendor/christian-riesen/otp/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) Christian Riesen http://christianriesen.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/vendor/christian-riesen/otp/README.md b/vendor/christian-riesen/otp/README.md new file mode 100644 index 0000000000..c979dbbc30 --- /dev/null +++ b/vendor/christian-riesen/otp/README.md @@ -0,0 +1,105 @@ +One Time Passwords +================== + +[![Build Status](https://secure.travis-ci.org/ChristianRiesen/otp.png)](http://travis-ci.org/ChristianRiesen/otp) + +Did you like this? Flattr it: + +[![Flattr otp](http://api.flattr.com/button/flattr-badge-large.png)](https://flattr.com/thing/719284/ChristianRiesenotp-on-GitHub) + +Installation +------------ + +Use [composer](http://getcomposer.org/) and require the library in your `composer.json` + + { + "require": { + "christian-riesen/otp": "1.*", + } + } + +Usage +----- + +```php +checkTotp(Base32::decode($secret), $key)) { + // Correct key + // IMPORTANT! Note this key as being used + // so nobody could launch a replay attack. + // Cache that for the next minutes and you + // should be good. +} else { + // Wrong key +} + +// Just to create a key for display (testing) +$key = $otp->totp($secret); + +``` + +Sample script in `example` folder. Requires sessions to work (for secret storage). + +Class Otp +--------- + +Implements hotp according to [RFC4226](https://tools.ietf.org/html/rfc4226) and totp according to [RFC6238](https://tools.ietf.org/html/rfc6238) (only sha1 algorithm). Once you have a secret, you can use it directly in this class to create the passwords themselves (mainly for debugging use) or use the check functions to safely check the validity of the keys. The `checkTotp` function also includes a helper to battle timedrift. + +Class GoogleAuthenticator +------------------------- + +Static function class to generate a correct url for the QR code, so you can easy scan it with your device. Google Authenticator is avaiaible as application for iPhone and Android. This removes the burden to create such an app from the developers of websites by using this set of classes. + +There are also older open source versions of the Google Authenticator app for both [iPhone](https://github.com/google/google-authenticator) and [Android](https://github.com/google/google-authenticator-android) + +This helper class uses the random_int function from PHP7, or the polyfill method from [paragonie/random_compat](https://packagist.org/packages/paragonie/random_compat) if present and falls back on other (less "secure") random generators. + +About +===== + +Requirements +------------ + +PHP 5.3.x+ + +Uses [Base32 class](https://github.com/ChristianRiesen/base32). + +If you want to run the tests, PHPUnit 3.6 or up is required. + +Author +------ + +Christian Riesen http://christianriesen.com + +Acknowledgements +---------------- + +The classes have been inspired by many different places that were talking about otp and Google Authenticator. Thank you all for your help. + +Project setup ideas blantently taken from https://github.com/Seldaek/monolog + diff --git a/vendor/christian-riesen/otp/composer.json b/vendor/christian-riesen/otp/composer.json new file mode 100644 index 0000000000..94f16c67a0 --- /dev/null +++ b/vendor/christian-riesen/otp/composer.json @@ -0,0 +1,28 @@ +{ + "name": "christian-riesen/otp", + "type": "library", + "description": "One Time Passwords, hotp and totp according to RFC4226 and RFC6238", + "keywords": ["otp","hotp","totp","googleauthenticator","rfc4226","rfc6238"], + "homepage": "https://github.com/ChristianRiesen/otp", + "license": "MIT", + "authors": [ + { + "name": "Christian Riesen", + "email": "chris.riesen@gmail.com", + "homepage": "http://christianriesen.com", + "role": "Developer" + } + ], + "require": { + "php": ">=5.3.0", + "christian-riesen/base32": ">=1.0" + }, + "suggest": { + "paragonie/random_compat": "Optional polyfill for a more secure random generator for pre PHP7 versions" + }, + "autoload": { + "psr-0": { + "Otp": "src" + } + } +} diff --git a/vendor/christian-riesen/otp/example/index.php b/vendor/christian-riesen/otp/example/index.php new file mode 100644 index 0000000000..4da362c5f5 --- /dev/null +++ b/vendor/christian-riesen/otp/example/index.php @@ -0,0 +1,100 @@ +totp(Base32::decode($secret)); + +$qrCode = GoogleAuthenticator::getQrCodeUrl('totp', 'otpsample@cr', $secret); +$keyUri = GoogleAuthenticator::getKeyUri('totp', 'otpsample@cr', $secret); + +?> + +One Time Passwords Example + + + +

One Time Passwords Example

+ +Secret is . This is saved with the users credentials. +
+
+
+ +QR Code for totp:
+ +
+This QR Code contains the Key URI: +
+
+ +Current totp would be
+
+
+ +Because of timedrift, you could technically enter a code before or after it +would actually be used. This form uses the checkTotp function. To test this, +open this page, wait until the key changes once or twice (not more) on your +Google Authenticator, then hit submit. Even though the key is "wrong" because of +small time differences, you can still use it. +
+
+ +
+ +
+Output:
+
+ + +checkTotp(Base32::decode($secret), $key)) { + echo 'Key correct!'; + // Add here something that makes note of this key and will not allow + // the use of it, for this user for the next 2 minutes. This way you + // prevent a replay attack. Otherwise your OTP is missing one of the + // key features it can bring in security to your application! + } else { + echo 'Wrong key!'; + } + + } else { + echo 'Key not the correct size'; + } +} + +?> + + + diff --git a/vendor/christian-riesen/otp/phpunit.xml.dist b/vendor/christian-riesen/otp/phpunit.xml.dist new file mode 100644 index 0000000000..7ffc6c632f --- /dev/null +++ b/vendor/christian-riesen/otp/phpunit.xml.dist @@ -0,0 +1,15 @@ + + + + + + tests/Otp/ + + + + + + src/Otp/ + + + diff --git a/vendor/christian-riesen/otp/src/Otp/GoogleAuthenticator.php b/vendor/christian-riesen/otp/src/Otp/GoogleAuthenticator.php new file mode 100644 index 0000000000..23e67ff051 --- /dev/null +++ b/vendor/christian-riesen/otp/src/Otp/GoogleAuthenticator.php @@ -0,0 +1,189 @@ + + * @link http://christianriesen.com + * @license MIT License see LICENSE file + */ + +class GoogleAuthenticator +{ + protected static $allowedTypes = array('hotp', 'totp'); + + protected static $height = 200; + protected static $width = 200; + + /** + * Returns the Key URI + * + * Format of encoded url is here: + * https://code.google.com/p/google-authenticator/wiki/KeyUriFormat + * Should be done in a better fashion + * + * @param string $type totp or hotp + * @param string $label Label to display this as to the user + * @param string $secret Base32 encoded secret + * @param integer $counter Required by hotp, otherwise ignored + * @param array $options Optional fields that will be set if present + * + * @return string Key URI + */ + public static function getKeyUri($type, $label, $secret, $counter = null, $options = array()) + { + // two types only.. + if (!in_array($type, self::$allowedTypes)) { + throw new \InvalidArgumentException('Type has to be of allowed types list'); + } + + // Label can't be empty + $label = trim($label); + + if (strlen($label) < 1) { + throw new \InvalidArgumentException('Label has to be one or more printable characters'); + } + + if (substr_count($label, ':') > 2) { + throw new \InvalidArgumentException('Account name contains illegal colon characters'); + } + + // Secret needs to be here + if (strlen($secret) < 1) { + throw new \InvalidArgumentException('No secret present'); + } + + // check for counter on hotp + if ($type == 'hotp' && is_null($counter)) { + throw new \InvalidArgumentException('Counter required for hotp'); + } + + // This is the base, these are at least required + $otpauth = 'otpauth://' . $type . '/' . str_replace(array(':', ' '), array('%3A', '%20'), $label) . '?secret=' . rawurlencode($secret); + + if ($type == 'hotp' && !is_null($counter)) { + $otpauth .= '&counter=' . intval($counter); + } + + // Now check the options array + + // algorithm (currently ignored by Authenticator) + // Defaults to SHA1 + if (array_key_exists('algorithm', $options)) { + $otpauth .= '&algorithm=' . rawurlencode($options['algorithm']); + } + + // digits (currently ignored by Authenticator) + // Defaults to 6 + if (array_key_exists('digits', $options) && intval($options['digits']) !== 6 && intval($options['digits']) !== 8) { + throw new \InvalidArgumentException('Digits can only have the values 6 or 8, ' . $options['digits'] . ' given'); + } elseif (array_key_exists('digits', $options)) { + $otpauth .= '&digits=' . intval($options['digits']); + } + + // period, only for totp (currently ignored by Authenticator) + // Defaults to 30 + if ($type == 'totp' && array_key_exists('period', $options)) { + $otpauth .= '&period=' . rawurlencode($options['period']); + } + + // issuer + // Defaults to none + if (array_key_exists('issuer', $options)) { + $otpauth .= '&issuer=' . rawurlencode($options['issuer']); + } + + return $otpauth; + } + + + /** + * Returns the QR code url + * + * Format of encoded url is here: + * https://code.google.com/p/google-authenticator/wiki/KeyUriFormat + * Should be done in a better fashion + * + * @param string $type totp or hotp + * @param string $label Label to display this as to the user + * @param string $secret Base32 encoded secret + * @param integer $counter Required by hotp, otherwise ignored + * @param array $options Optional fields that will be set if present + * + * @return string URL to the QR code + */ + public static function getQrCodeUrl($type, $label, $secret, $counter = null, $options = array()) + { + // Width and height can be overwritten + $width = self::$width; + + if (array_key_exists('width', $options) && is_numeric($options['width'])) { + $width = $options['width']; + } + + $height = self::$height; + + if (array_key_exists('height', $options) && is_numeric($options['height'])) { + $height = $options['height']; + } + + $otpauth = self::getKeyUri($type, $label, $secret, $counter, $options); + + $url = 'https://chart.googleapis.com/chart?chs=' . $width . 'x' + . $height . '&cht=qr&chld=M|0&chl=' . urlencode($otpauth); + + return $url; + } + + /** + * Creates a pseudo random Base32 string + * + * This could decode into anything. It's located here as a small helper + * where code that might need base32 usually also needs something like this. + * + * @param integer $length Exact length of output string + * @return string Base32 encoded random + */ + public static function generateRandom($length = 16) + { + $keys = array_merge(range('A','Z'), range(2,7)); // No padding char + + $string = ''; + + for ($i = 0; $i < $length; $i++) { + $string .= $keys[self::getRand()]; + } + + return $string; + } + + /** + * Get random number + * + * @return int Random number between 0 and 31 (including) + */ + private static function getRand() + { + if (function_exists('random_int')) { + // Uses either the PHP7 internal function or the polyfill if present + return random_int(0, 31); + } elseif (function_exists('openssl_random_pseudo_bytes')) { + $bytes = openssl_random_pseudo_bytes(2); + $number = hexdec(bin2hex($bytes)); + + if ($number > 31) { + $number = $number % 32; + } + + return $number; + } else { + return mt_rand(0, 31); + } + } +} diff --git a/vendor/christian-riesen/otp/src/Otp/Otp.php b/vendor/christian-riesen/otp/src/Otp/Otp.php new file mode 100644 index 0000000000..7d954870df --- /dev/null +++ b/vendor/christian-riesen/otp/src/Otp/Otp.php @@ -0,0 +1,310 @@ + + * @link http://christianriesen.com + * @license MIT License see LICENSE file + */ + +class Otp implements OtpInterface +{ + /** + * The digits the code can have + * + * Either 6 or 8. + * Authenticator does only support 6. + * + * @var integer + */ + protected $digits = 6; + + /** + * Time in seconds one counter period is long + * + * @var integer + */ + protected $period = 30; + + /** + * Possible algorithms + * + * @var array + */ + protected $allowedAlgorithms = array('sha1', 'sha256', 'sha512'); + + /** + * Currently used algorithm + * + * @var string + */ + protected $algorithm = 'sha1'; + + /* (non-PHPdoc) + * @see Otp.OtpInterface::hotp() + */ + public function hotp($secret, $counter) + { + if (!is_numeric($counter)) { + throw new \InvalidArgumentException('Counter must be integer'); + } + + $hash = hash_hmac( + $this->algorithm, + $this->getBinaryCounter($counter), + $secret, + true + ); + + return str_pad($this->truncate($hash), $this->digits, '0', STR_PAD_LEFT); + } + + /* (non-PHPdoc) + * @see Otp.OtpInterface::totp() + */ + public function totp($secret, $timecounter = null) + { + if (is_null($timecounter)) { + $timecounter = $this->getTimecounter(); + } + + return $this->hotp($secret, $timecounter); + } + + /* (non-PHPdoc) + * @see Otp.OtpInterface::checkHotp() + */ + public function checkHotp($secret, $counter, $key) + { + return $this->safeCompare($this->hotp($secret, $counter), $key); + } + + /* (non-PHPdoc) + * @see Otp.OtpInterface::checkTotp() + */ + public function checkTotp($secret, $key, $timedrift = 1) + { + if (!is_numeric($timedrift) || $timedrift < 0) { + throw new \InvalidArgumentException('Invalid timedrift supplied'); + } + // Counter comes from time now + // Also we check the current timestamp as well as previous and future ones + // according to $timerange + $timecounter = $this->getTimecounter(); + + $start = $timecounter - ($timedrift); + $end = $timecounter + ($timedrift); + + // We first try the current, as it is the most likely to work + if ($this->safeCompare($this->totp($secret, $timecounter), $key)) { + return true; + } elseif ($timedrift == 0) { + // When timedrift is 0, this is the end of the checks + return false; + } + + // Well, that didn't work, so try the others + for ($t = $start; $t <= $end; $t = $t + 1) { + if ($t == $timecounter) { + // Already tried that one + continue; + } + + if ($this->safeCompare($this->totp($secret, $t), $key)) { + return true; + } + } + + // if none worked, then return false + return false; + } + + /** + * Changing the used algorithm for hashing + * + * Can only be one of the algorithms in the allowedAlgorithms property. + * + * @param string $algorithm + * @throws \InvalidArgumentException + * @return \Otp\Otp + */ + + /* + * This has been disabled since it does not bring the expected results + * according to the RFC test vectors for sha256 or sha512. + * Until that is fixed, the algorithm simply stays at sha1. + * Google Authenticator does not support sha256 and sha512 at the moment. + * + + public function setAlgorithm($algorithm) + { + if (!in_array($algorithm, $this->allowedAlgorithms)) { + throw new \InvalidArgumentException('Not an allowed algorithm: ' . $algorithm); + } + + $this->algorithm = $algorithm; + + return $this; + } + // */ + + /** + * Get the algorithms name (lowercase) + * + * @return string + */ + public function getAlgorithm() + { + return $this->algorithm; + } + + /** + * Setting period lenght for totp + * + * @param integer $period + * @throws \InvalidArgumentException + * @return \Otp\Otp + */ + public function setPeriod($period) + { + if (!is_int($period)) { + throw new \InvalidArgumentException('Period must be an integer'); + } + + $this->period = $period; + + return $this; + } + + /** + * Returns the set period value + * + * @return integer + */ + public function getPeriod() + { + return $this->period; + } + + /** + * Setting number of otp digits + * + * @param integer $digits Number of digits for the otp (6 or 8) + * @throws \InvalidArgumentException + * @return \Otp\Otp + */ + public function setDigits($digits) + { + if (!in_array($digits, array(6, 8))) { + throw new \InvalidArgumentException('Digits must be 6 or 8'); + } + + $this->digits = $digits; + + return $this; + } + + /** + * Returns number of digits in the otp + * + * @return integer + */ + public function getDigits() + { + return $this->digits; + } + + /** + * Generates a binary counter for hashing + * + * Warning: Not 2038 safe. Maybe until then pack supports 64bit. + * + * @param integer $counter Counter in integer form + * @return string Binary string + */ + protected function getBinaryCounter($counter) + { + return pack('N*', 0) . pack('N*', $counter); + } + + /** + * Generating time counter + * + * This is the time divided by 30 by default. + * + * @return integer Time counter + */ + protected function getTimecounter() + { + return floor(time() / $this->period); + } + + /** + * Creates the basic number for otp from hash + * + * This number is left padded with zeros to the required length by the + * calling function. + * + * @param string $hash hmac hash + * @return number + */ + protected function truncate($hash) + { + $offset = ord($hash[19]) & 0xf; + + return ( + ((ord($hash[$offset+0]) & 0x7f) << 24 ) | + ((ord($hash[$offset+1]) & 0xff) << 16 ) | + ((ord($hash[$offset+2]) & 0xff) << 8 ) | + (ord($hash[$offset+3]) & 0xff) + ) % pow(10, $this->digits); + } + + /** + * Safely compares two inputs + * + * Assumed inputs are numbers and strings. + * Compares them in a time linear manner. No matter how much you guess + * correct of the partial content, it does not change the time it takes to + * run the entire comparison. + * + * @param mixed $a + * @param mixed $b + * @return boolean + */ + protected function safeCompare($a, $b) + { + $sha1a = sha1($a); + $sha1b = sha1($b); + + // Now the compare is always the same length. Even considering minute + // time differences in sha1 creation, all you know is that a longer + // input takes longer to hash, not how long the actual compared value is + $result = 0; + + for ($i = 0; $i < 40; $i++) { + $result |= ord($sha1a[$i]) ^ ord($sha1b[$i]); + } + + return $result == 0; + } +} + diff --git a/vendor/christian-riesen/otp/src/Otp/OtpInterface.php b/vendor/christian-riesen/otp/src/Otp/OtpInterface.php new file mode 100644 index 0000000000..7ff34f2f4a --- /dev/null +++ b/vendor/christian-riesen/otp/src/Otp/OtpInterface.php @@ -0,0 +1,65 @@ + + * @link http://christianriesen.com + * @license MIT License see LICENSE file + */ + +interface OtpInterface +{ + /** + * Returns OTP using the HOTP algorithm + * + * @param string $secret + * @param integer $counter + * @return string One Time Password + */ + function hotp($secret, $counter); + + /** + * Returns OTP using the TOTP algorithm + * + * @param string $secret + * @param integer $timecounter Optional: Uses current time if null + * @return string One Time Password + */ + function totp($secret, $timecounter = null); + + /** + * Checks Hotp against a key + * + * This is a helper function, but is here to ensure the Totp can be checked + * in the same manner. + * + * @param string $secret + * @param integer $counter + * @param string $key + * + * @return boolean If key is correct + */ + function checkHotp($secret, $counter, $key); + + /** + * Checks Totp agains a key + * + * + * @param string $secret + * @param integer $key + * @param integer $timedrift + * + * @return boolean If key is correct + */ + function checkTotp($secret, $key, $timedrift = 1); +} diff --git a/vendor/christian-riesen/otp/tests/Otp/GoogleAuthenticatorTest.php b/vendor/christian-riesen/otp/tests/Otp/GoogleAuthenticatorTest.php new file mode 100644 index 0000000000..219bdaa82c --- /dev/null +++ b/vendor/christian-riesen/otp/tests/Otp/GoogleAuthenticatorTest.php @@ -0,0 +1,88 @@ +assertEquals( + 'https://chart.googleapis.com/chart?chs=200x200&cht=qr&chld=M|0&chl=otpauth%3A%2F%2Ftotp%2Fuser%40host.com%3Fsecret%3DMEP3EYVA6XNFNVNM', + GoogleAuthenticator::getQrCodeUrl('totp', 'user@host.com', $secret) + ); + + // hotp (include a counter) + $this->assertEquals( + 'https://chart.googleapis.com/chart?chs=200x200&cht=qr&chld=M|0&chl=otpauth%3A%2F%2Fhotp%2Fuser%40host.com%3Fsecret%3DMEP3EYVA6XNFNVNM%26counter%3D1234', + GoogleAuthenticator::getQrCodeUrl('hotp', 'user@host.com', $secret, 1234) + ); + + // totp, this time with a parameter for chaning the size of the QR + $this->assertEquals( + 'https://chart.googleapis.com/chart?chs=300x300&cht=qr&chld=M|0&chl=otpauth%3A%2F%2Ftotp%2Fuser%40host.com%3Fsecret%3DMEP3EYVA6XNFNVNM', + GoogleAuthenticator::getQrCodeUrl('totp', 'user@host.com', $secret, null, array('height' => 300, 'width' => 300)) + ); + + } + + /** + * Tests getKeyUri + */ + public function testGetKeyUri() + { + $secret = 'MEP3EYVA6XNFNVNM'; // testing secret + + // Standard totp case + $this->assertEquals( + 'otpauth://totp/user@host.com?secret=MEP3EYVA6XNFNVNM', + GoogleAuthenticator::getKeyUri('totp', 'user@host.com', $secret) + ); + + // hotp (include a counter) + $this->assertEquals( + 'otpauth://hotp/user@host.com?secret=MEP3EYVA6XNFNVNM&counter=1234', + GoogleAuthenticator::getKeyUri('hotp', 'user@host.com', $secret, 1234) + ); + + // totp/hotp with an issuer in the label + $this->assertEquals( + 'otpauth://hotp/issuer%3Auser@host.com?secret=MEP3EYVA6XNFNVNM&counter=1234', + GoogleAuthenticator::getKeyUri('hotp', 'issuer:user@host.com', $secret, 1234) + ); + + // totp/hotp with an issuer and spaces in the label + $this->assertEquals( + 'otpauth://hotp/an%20issuer%3A%20user@host.com?secret=MEP3EYVA6XNFNVNM&counter=1234', + GoogleAuthenticator::getKeyUri('hotp', 'an issuer: user@host.com', $secret, 1234) + ); + + // totp/hotp with an issuer as option + $this->assertEquals( + 'otpauth://hotp/an%20issuer%3Auser@host.com?secret=MEP3EYVA6XNFNVNM&counter=1234&issuer=an%20issuer', + GoogleAuthenticator::getKeyUri('hotp', 'an issuer:user@host.com', $secret, 1234, array('issuer' => 'an issuer')) + ); + } + + /** + * Tests generateRandom + */ + public function testGenerateRandom() + { + // contains numbers 2-7 and letters A-Z in large letters, 16 chars long + $this->assertRegExp('/[2-7A-Z]{16}/', GoogleAuthenticator::generateRandom()); + + // Can be told to make a longer secret + $this->assertRegExp('/[2-7A-Z]{18}/', GoogleAuthenticator::generateRandom(18)); + } +} diff --git a/vendor/christian-riesen/otp/tests/Otp/OtpTest.php b/vendor/christian-riesen/otp/tests/Otp/OtpTest.php new file mode 100644 index 0000000000..ccfdddabb9 --- /dev/null +++ b/vendor/christian-riesen/otp/tests/Otp/OtpTest.php @@ -0,0 +1,124 @@ +Otp = new Otp(); + + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->Otp = null; + + parent::tearDown(); + } + + /** + * Tests Otp->hotp() + * + * Using test vectors from RFC + * https://tools.ietf.org/html/rfc4226 + */ + public function testHotpRfc() + { + $secret = $this->secret; + + $this->assertEquals('755224', $this->Otp->hotp($secret, 0)); + $this->assertEquals('287082', $this->Otp->hotp($secret, 1)); + $this->assertEquals('359152', $this->Otp->hotp($secret, 2)); + $this->assertEquals('969429', $this->Otp->hotp($secret, 3)); + $this->assertEquals('338314', $this->Otp->hotp($secret, 4)); + $this->assertEquals('254676', $this->Otp->hotp($secret, 5)); + $this->assertEquals('287922', $this->Otp->hotp($secret, 6)); + $this->assertEquals('162583', $this->Otp->hotp($secret, 7)); + $this->assertEquals('399871', $this->Otp->hotp($secret, 8)); + $this->assertEquals('520489', $this->Otp->hotp($secret, 9)); + } + + /** + * Tests TOTP general construction + * + * Still uses the hotp function, but since totp is a bit more special, has + * its own tests + * Using test vectors from RFC + * https://tools.ietf.org/html/rfc6238 + */ + public function testTotpRfc() + { + $secret = $this->secret; + + // Test vectors are in 8 digits + $this->Otp->setDigits(8); + + // The time presented in the test vector has to be first divided through 30 + // to count as the key + + // SHA 1 grouping + $this->assertEquals('94287082', $this->Otp->hotp($secret, floor(59/30)), 'sha1 with time 59'); + $this->assertEquals('07081804', $this->Otp->hotp($secret, floor(1111111109/30)), 'sha1 with time 1111111109'); + $this->assertEquals('14050471', $this->Otp->hotp($secret, floor(1111111111/30)), 'sha1 with time 1111111111'); + $this->assertEquals('89005924', $this->Otp->hotp($secret, floor(1234567890/30)), 'sha1 with time 1234567890'); + $this->assertEquals('69279037', $this->Otp->hotp($secret, floor(2000000000/30)), 'sha1 with time 2000000000'); + $this->assertEquals('65353130', $this->Otp->hotp($secret, floor(20000000000/30)), 'sha1 with time 20000000000'); + + /* + The following tests do NOT pass. + Once the otp class can deal with these correctly, they can be used again. + They are here for completeness test vectors from the RFC. + + // SHA 256 grouping + $this->Otp->setAlgorithm('sha256'); + $this->assertEquals('46119246', $this->Otp->hotp($secret, floor(59/30)), 'sha256 with time 59'); + $this->assertEquals('07081804', $this->Otp->hotp($secret, floor(1111111109/30)), 'sha256 with time 1111111109'); + $this->assertEquals('14050471', $this->Otp->hotp($secret, floor(1111111111/30)), 'sha256 with time 1111111111'); + $this->assertEquals('89005924', $this->Otp->hotp($secret, floor(1234567890/30)), 'sha256 with time 1234567890'); + $this->assertEquals('69279037', $this->Otp->hotp($secret, floor(2000000000/30)), 'sha256 with time 2000000000'); + $this->assertEquals('65353130', $this->Otp->hotp($secret, floor(20000000000/30)), 'sha256 with time 20000000000'); + + // SHA 512 grouping + $this->Otp->setAlgorithm('sha512'); + $this->assertEquals('90693936', $this->Otp->hotp($secret, floor(59/30)), 'sha512 with time 59'); + $this->assertEquals('25091201', $this->Otp->hotp($secret, floor(1111111109/30)), 'sha512 with time 1111111109'); + $this->assertEquals('99943326', $this->Otp->hotp($secret, floor(1111111111/30)), 'sha512 with time 1111111111'); + $this->assertEquals('93441116', $this->Otp->hotp($secret, floor(1234567890/30)), 'sha512 with time 1234567890'); + $this->assertEquals('38618901', $this->Otp->hotp($secret, floor(2000000000/30)), 'sha512 with time 2000000000'); + $this->assertEquals('47863826', $this->Otp->hotp($secret, floor(20000000000/30)), 'sha512 with time 20000000000'); + */ + } + + /** + * @expectedException InvalidArgumentException + * @expectedExceptionMessage Counter must be integer + */ + public function testHotpInvalidCounter() + { + $this->Otp->hotp($this->secret, 'a'); + } + +} diff --git a/vendor/composer/ClassLoader.php b/vendor/composer/ClassLoader.php new file mode 100644 index 0000000000..2c72175e77 --- /dev/null +++ b/vendor/composer/ClassLoader.php @@ -0,0 +1,445 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Autoload; + +/** + * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. + * + * $loader = new \Composer\Autoload\ClassLoader(); + * + * // register classes with namespaces + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * // activate the autoloader + * $loader->register(); + * + * // to enable searching the include path (eg. for PEAR packages) + * $loader->setUseIncludePath(true); + * + * In this example, if you try to use a class in the Symfony\Component + * namespace or one of its children (Symfony\Component\Console for instance), + * the autoloader will first look for the class under the component/ + * directory, and it will then fallback to the framework/ directory if not + * found before giving up. + * + * This class is loosely based on the Symfony UniversalClassLoader. + * + * @author Fabien Potencier + * @author Jordi Boggiano + * @see http://www.php-fig.org/psr/psr-0/ + * @see http://www.php-fig.org/psr/psr-4/ + */ +class ClassLoader +{ + // PSR-4 + private $prefixLengthsPsr4 = array(); + private $prefixDirsPsr4 = array(); + private $fallbackDirsPsr4 = array(); + + // PSR-0 + private $prefixesPsr0 = array(); + private $fallbackDirsPsr0 = array(); + + private $useIncludePath = false; + private $classMap = array(); + private $classMapAuthoritative = false; + private $missingClasses = array(); + private $apcuPrefix; + + public function getPrefixes() + { + if (!empty($this->prefixesPsr0)) { + return call_user_func_array('array_merge', $this->prefixesPsr0); + } + + return array(); + } + + public function getPrefixesPsr4() + { + return $this->prefixDirsPsr4; + } + + public function getFallbackDirs() + { + return $this->fallbackDirsPsr0; + } + + public function getFallbackDirsPsr4() + { + return $this->fallbackDirsPsr4; + } + + public function getClassMap() + { + return $this->classMap; + } + + /** + * @param array $classMap Class to filename map + */ + public function addClassMap(array $classMap) + { + if ($this->classMap) { + $this->classMap = array_merge($this->classMap, $classMap); + } else { + $this->classMap = $classMap; + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, either + * appending or prepending to the ones previously set for this prefix. + * + * @param string $prefix The prefix + * @param array|string $paths The PSR-0 root directories + * @param bool $prepend Whether to prepend the directories + */ + public function add($prefix, $paths, $prepend = false) + { + if (!$prefix) { + if ($prepend) { + $this->fallbackDirsPsr0 = array_merge( + (array) $paths, + $this->fallbackDirsPsr0 + ); + } else { + $this->fallbackDirsPsr0 = array_merge( + $this->fallbackDirsPsr0, + (array) $paths + ); + } + + return; + } + + $first = $prefix[0]; + if (!isset($this->prefixesPsr0[$first][$prefix])) { + $this->prefixesPsr0[$first][$prefix] = (array) $paths; + + return; + } + if ($prepend) { + $this->prefixesPsr0[$first][$prefix] = array_merge( + (array) $paths, + $this->prefixesPsr0[$first][$prefix] + ); + } else { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $this->prefixesPsr0[$first][$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, either + * appending or prepending to the ones previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param array|string $paths The PSR-4 base directories + * @param bool $prepend Whether to prepend the directories + * + * @throws \InvalidArgumentException + */ + public function addPsr4($prefix, $paths, $prepend = false) + { + if (!$prefix) { + // Register directories for the root namespace. + if ($prepend) { + $this->fallbackDirsPsr4 = array_merge( + (array) $paths, + $this->fallbackDirsPsr4 + ); + } else { + $this->fallbackDirsPsr4 = array_merge( + $this->fallbackDirsPsr4, + (array) $paths + ); + } + } elseif (!isset($this->prefixDirsPsr4[$prefix])) { + // Register directories for a new namespace. + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } elseif ($prepend) { + // Prepend directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + (array) $paths, + $this->prefixDirsPsr4[$prefix] + ); + } else { + // Append directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $this->prefixDirsPsr4[$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, + * replacing any others previously set for this prefix. + * + * @param string $prefix The prefix + * @param array|string $paths The PSR-0 base directories + */ + public function set($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr0 = (array) $paths; + } else { + $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, + * replacing any others previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param array|string $paths The PSR-4 base directories + * + * @throws \InvalidArgumentException + */ + public function setPsr4($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr4 = (array) $paths; + } else { + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } + } + + /** + * Turns on searching the include path for class files. + * + * @param bool $useIncludePath + */ + public function setUseIncludePath($useIncludePath) + { + $this->useIncludePath = $useIncludePath; + } + + /** + * Can be used to check if the autoloader uses the include path to check + * for classes. + * + * @return bool + */ + public function getUseIncludePath() + { + return $this->useIncludePath; + } + + /** + * Turns off searching the prefix and fallback directories for classes + * that have not been registered with the class map. + * + * @param bool $classMapAuthoritative + */ + public function setClassMapAuthoritative($classMapAuthoritative) + { + $this->classMapAuthoritative = $classMapAuthoritative; + } + + /** + * Should class lookup fail if not found in the current class map? + * + * @return bool + */ + public function isClassMapAuthoritative() + { + return $this->classMapAuthoritative; + } + + /** + * APCu prefix to use to cache found/not-found classes, if the extension is enabled. + * + * @param string|null $apcuPrefix + */ + public function setApcuPrefix($apcuPrefix) + { + $this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null; + } + + /** + * The APCu prefix in use, or null if APCu caching is not enabled. + * + * @return string|null + */ + public function getApcuPrefix() + { + return $this->apcuPrefix; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + } + + /** + * Unregisters this instance as an autoloader. + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * @return bool|null True if loaded, null otherwise + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + includeFile($file); + + return true; + } + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|false The path if found, false otherwise + */ + public function findFile($class) + { + // class map lookup + if (isset($this->classMap[$class])) { + return $this->classMap[$class]; + } + if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { + return false; + } + if (null !== $this->apcuPrefix) { + $file = apcu_fetch($this->apcuPrefix.$class, $hit); + if ($hit) { + return $file; + } + } + + $file = $this->findFileWithExtension($class, '.php'); + + // Search for Hack files if we are running on HHVM + if (false === $file && defined('HHVM_VERSION')) { + $file = $this->findFileWithExtension($class, '.hh'); + } + + if (null !== $this->apcuPrefix) { + apcu_add($this->apcuPrefix.$class, $file); + } + + if (false === $file) { + // Remember that this class does not exist. + $this->missingClasses[$class] = true; + } + + return $file; + } + + private function findFileWithExtension($class, $ext) + { + // PSR-4 lookup + $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; + + $first = $class[0]; + if (isset($this->prefixLengthsPsr4[$first])) { + $subPath = $class; + while (false !== $lastPos = strrpos($subPath, '\\')) { + $subPath = substr($subPath, 0, $lastPos); + $search = $subPath.'\\'; + if (isset($this->prefixDirsPsr4[$search])) { + foreach ($this->prefixDirsPsr4[$search] as $dir) { + $length = $this->prefixLengthsPsr4[$first][$search]; + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) { + return $file; + } + } + } + } + } + + // PSR-4 fallback dirs + foreach ($this->fallbackDirsPsr4 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { + return $file; + } + } + + // PSR-0 lookup + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) + . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); + } else { + // PEAR-like class name + $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; + } + + if (isset($this->prefixesPsr0[$first])) { + foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { + if (0 === strpos($class, $prefix)) { + foreach ($dirs as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + } + } + } + + // PSR-0 fallback dirs + foreach ($this->fallbackDirsPsr0 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + + // PSR-0 include paths. + if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { + return $file; + } + + return false; + } +} + +/** + * Scope isolated include. + * + * Prevents access to $this/self from included files. + */ +function includeFile($file) +{ + include $file; +} diff --git a/vendor/composer/LICENSE b/vendor/composer/LICENSE new file mode 100644 index 0000000000..f27399a042 --- /dev/null +++ b/vendor/composer/LICENSE @@ -0,0 +1,21 @@ + +Copyright (c) Nils Adermann, Jordi Boggiano + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php new file mode 100644 index 0000000000..75207dbe59 --- /dev/null +++ b/vendor/composer/autoload_classmap.php @@ -0,0 +1,901 @@ + $vendorDir . '/christian-riesen/base32/src/Base32.php', + 'Eluceo\\iCal\\Component' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/Component.php', + 'Eluceo\\iCal\\Component\\Alarm' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/Component/Alarm.php', + 'Eluceo\\iCal\\Component\\Calendar' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/Component/Calendar.php', + 'Eluceo\\iCal\\Component\\Event' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/Component/Event.php', + 'Eluceo\\iCal\\Component\\Timezone' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/Component/Timezone.php', + 'Eluceo\\iCal\\Component\\TimezoneRule' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/Component/TimezoneRule.php', + 'Eluceo\\iCal\\ParameterBag' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/ParameterBag.php', + 'Eluceo\\iCal\\Property' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/Property.php', + 'Eluceo\\iCal\\PropertyBag' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/PropertyBag.php', + 'Eluceo\\iCal\\Property\\ArrayValue' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/Property/ArrayValue.php', + 'Eluceo\\iCal\\Property\\DateTimeProperty' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/Property/DateTimeProperty.php', + 'Eluceo\\iCal\\Property\\DateTimesProperty' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/Property/DateTimesProperty.php', + 'Eluceo\\iCal\\Property\\Event\\Attendees' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/Property/Event/Attendees.php', + 'Eluceo\\iCal\\Property\\Event\\Description' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/Property/Event/Description.php', + 'Eluceo\\iCal\\Property\\Event\\Organizer' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/Property/Event/Organizer.php', + 'Eluceo\\iCal\\Property\\Event\\RecurrenceId' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/Property/Event/RecurrenceId.php', + 'Eluceo\\iCal\\Property\\Event\\RecurrenceRule' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/Property/Event/RecurrenceRule.php', + 'Eluceo\\iCal\\Property\\StringValue' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/Property/StringValue.php', + 'Eluceo\\iCal\\Property\\ValueInterface' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/Property/ValueInterface.php', + 'Eluceo\\iCal\\Util\\ComponentUtil' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/Util/ComponentUtil.php', + 'Eluceo\\iCal\\Util\\DateUtil' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/Util/DateUtil.php', + 'Eluceo\\iCal\\Util\\PropertyValueUtil' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/Util/PropertyValueUtil.php', + 'Gregwar\\Captcha\\CaptchaBuilder' => $vendorDir . '/gregwar/captcha/CaptchaBuilder.php', + 'Gregwar\\Captcha\\CaptchaBuilderInterface' => $vendorDir . '/gregwar/captcha/CaptchaBuilderInterface.php', + 'Gregwar\\Captcha\\ImageFileHandler' => $vendorDir . '/gregwar/captcha/ImageFileHandler.php', + 'Gregwar\\Captcha\\PhraseBuilder' => $vendorDir . '/gregwar/captcha/PhraseBuilder.php', + 'Gregwar\\Captcha\\PhraseBuilderInterface' => $vendorDir . '/gregwar/captcha/PhraseBuilderInterface.php', + 'JsonRPC\\Client' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Client.php', + 'JsonRPC\\Exception\\AccessDeniedException' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Exception/AccessDeniedException.php', + 'JsonRPC\\Exception\\AuthenticationFailureException' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Exception/AuthenticationFailureException.php', + 'JsonRPC\\Exception\\ConnectionFailureException' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Exception/ConnectionFailureException.php', + 'JsonRPC\\Exception\\InvalidJsonFormatException' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Exception/InvalidJsonFormatException.php', + 'JsonRPC\\Exception\\InvalidJsonRpcFormatException' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Exception/InvalidJsonRpcFormatException.php', + 'JsonRPC\\Exception\\ResponseEncodingFailureException' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Exception/ResponseEncodingFailureException.php', + 'JsonRPC\\Exception\\ResponseException' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Exception/ResponseException.php', + 'JsonRPC\\Exception\\ServerErrorException' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Exception/ServerErrorException.php', + 'JsonRPC\\HttpClient' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/HttpClient.php', + 'JsonRPC\\MiddlewareHandler' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/MiddlewareHandler.php', + 'JsonRPC\\MiddlewareInterface' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/MiddlewareInterface.php', + 'JsonRPC\\ProcedureHandler' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/ProcedureHandler.php', + 'JsonRPC\\Request\\BatchRequestParser' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Request/BatchRequestParser.php', + 'JsonRPC\\Request\\RequestBuilder' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Request/RequestBuilder.php', + 'JsonRPC\\Request\\RequestParser' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Request/RequestParser.php', + 'JsonRPC\\Response\\ResponseBuilder' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Response/ResponseBuilder.php', + 'JsonRPC\\Response\\ResponseParser' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Response/ResponseParser.php', + 'JsonRPC\\Server' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Server.php', + 'JsonRPC\\Validator\\HostValidator' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Validator/HostValidator.php', + 'JsonRPC\\Validator\\JsonEncodingValidator' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Validator/JsonEncodingValidator.php', + 'JsonRPC\\Validator\\JsonFormatValidator' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Validator/JsonFormatValidator.php', + 'JsonRPC\\Validator\\RpcFormatValidator' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Validator/RpcFormatValidator.php', + 'JsonRPC\\Validator\\UserValidator' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Validator/UserValidator.php', + 'Kanboard\\Action\\Base' => $baseDir . '/app/Action/Base.php', + 'Kanboard\\Action\\CommentCreation' => $baseDir . '/app/Action/CommentCreation.php', + 'Kanboard\\Action\\CommentCreationMoveTaskColumn' => $baseDir . '/app/Action/CommentCreationMoveTaskColumn.php', + 'Kanboard\\Action\\TaskAssignCategoryColor' => $baseDir . '/app/Action/TaskAssignCategoryColor.php', + 'Kanboard\\Action\\TaskAssignCategoryLabel' => $baseDir . '/app/Action/TaskAssignCategoryLabel.php', + 'Kanboard\\Action\\TaskAssignCategoryLink' => $baseDir . '/app/Action/TaskAssignCategoryLink.php', + 'Kanboard\\Action\\TaskAssignColorCategory' => $baseDir . '/app/Action/TaskAssignColorCategory.php', + 'Kanboard\\Action\\TaskAssignColorColumn' => $baseDir . '/app/Action/TaskAssignColorColumn.php', + 'Kanboard\\Action\\TaskAssignColorLink' => $baseDir . '/app/Action/TaskAssignColorLink.php', + 'Kanboard\\Action\\TaskAssignColorOnDueDate' => $baseDir . '/app/Action/TaskAssignColorOnDueDate.php', + 'Kanboard\\Action\\TaskAssignColorPriority' => $baseDir . '/app/Action/TaskAssignColorPriority.php', + 'Kanboard\\Action\\TaskAssignColorSwimlane' => $baseDir . '/app/Action/TaskAssignColorSwimlane.php', + 'Kanboard\\Action\\TaskAssignColorUser' => $baseDir . '/app/Action/TaskAssignColorUser.php', + 'Kanboard\\Action\\TaskAssignCreator' => $baseDir . '/app/Action/TaskAssignCreator.php', + 'Kanboard\\Action\\TaskAssignCurrentUser' => $baseDir . '/app/Action/TaskAssignCurrentUser.php', + 'Kanboard\\Action\\TaskAssignCurrentUserColumn' => $baseDir . '/app/Action/TaskAssignCurrentUserColumn.php', + 'Kanboard\\Action\\TaskAssignDueDateOnCreation' => $baseDir . '/app/Action/TaskAssignDueDateOnCreation.php', + 'Kanboard\\Action\\TaskAssignPrioritySwimlane' => $baseDir . '/app/Action/TaskAssignPrioritySwimlane.php', + 'Kanboard\\Action\\TaskAssignSpecificUser' => $baseDir . '/app/Action/TaskAssignSpecificUser.php', + 'Kanboard\\Action\\TaskAssignUser' => $baseDir . '/app/Action/TaskAssignUser.php', + 'Kanboard\\Action\\TaskClose' => $baseDir . '/app/Action/TaskClose.php', + 'Kanboard\\Action\\TaskCloseColumn' => $baseDir . '/app/Action/TaskCloseColumn.php', + 'Kanboard\\Action\\TaskCloseNoActivity' => $baseDir . '/app/Action/TaskCloseNoActivity.php', + 'Kanboard\\Action\\TaskCloseNoActivityColumn' => $baseDir . '/app/Action/TaskCloseNoActivityColumn.php', + 'Kanboard\\Action\\TaskCloseNotMovedColumn' => $baseDir . '/app/Action/TaskCloseNotMovedColumn.php', + 'Kanboard\\Action\\TaskCreation' => $baseDir . '/app/Action/TaskCreation.php', + 'Kanboard\\Action\\TaskDuplicateAnotherProject' => $baseDir . '/app/Action/TaskDuplicateAnotherProject.php', + 'Kanboard\\Action\\TaskEmail' => $baseDir . '/app/Action/TaskEmail.php', + 'Kanboard\\Action\\TaskEmailNoActivity' => $baseDir . '/app/Action/TaskEmailNoActivity.php', + 'Kanboard\\Action\\TaskMoveAnotherProject' => $baseDir . '/app/Action/TaskMoveAnotherProject.php', + 'Kanboard\\Action\\TaskMoveColumnAssigned' => $baseDir . '/app/Action/TaskMoveColumnAssigned.php', + 'Kanboard\\Action\\TaskMoveColumnCategoryChange' => $baseDir . '/app/Action/TaskMoveColumnCategoryChange.php', + 'Kanboard\\Action\\TaskMoveColumnClosed' => $baseDir . '/app/Action/TaskMoveColumnClosed.php', + 'Kanboard\\Action\\TaskMoveColumnNotMovedPeriod' => $baseDir . '/app/Action/TaskMoveColumnNotMovedPeriod.php', + 'Kanboard\\Action\\TaskMoveColumnUnAssigned' => $baseDir . '/app/Action/TaskMoveColumnUnAssigned.php', + 'Kanboard\\Action\\TaskOpen' => $baseDir . '/app/Action/TaskOpen.php', + 'Kanboard\\Action\\TaskUpdateStartDate' => $baseDir . '/app/Action/TaskUpdateStartDate.php', + 'Kanboard\\Analytic\\AverageLeadCycleTimeAnalytic' => $baseDir . '/app/Analytic/AverageLeadCycleTimeAnalytic.php', + 'Kanboard\\Analytic\\AverageTimeSpentColumnAnalytic' => $baseDir . '/app/Analytic/AverageTimeSpentColumnAnalytic.php', + 'Kanboard\\Analytic\\EstimatedTimeComparisonAnalytic' => $baseDir . '/app/Analytic/EstimatedTimeComparisonAnalytic.php', + 'Kanboard\\Analytic\\TaskDistributionAnalytic' => $baseDir . '/app/Analytic/TaskDistributionAnalytic.php', + 'Kanboard\\Analytic\\UserDistributionAnalytic' => $baseDir . '/app/Analytic/UserDistributionAnalytic.php', + 'Kanboard\\Api\\Authorization\\ActionAuthorization' => $baseDir . '/app/Api/Authorization/ActionAuthorization.php', + 'Kanboard\\Api\\Authorization\\CategoryAuthorization' => $baseDir . '/app/Api/Authorization/CategoryAuthorization.php', + 'Kanboard\\Api\\Authorization\\ColumnAuthorization' => $baseDir . '/app/Api/Authorization/ColumnAuthorization.php', + 'Kanboard\\Api\\Authorization\\CommentAuthorization' => $baseDir . '/app/Api/Authorization/CommentAuthorization.php', + 'Kanboard\\Api\\Authorization\\ProcedureAuthorization' => $baseDir . '/app/Api/Authorization/ProcedureAuthorization.php', + 'Kanboard\\Api\\Authorization\\ProjectAuthorization' => $baseDir . '/app/Api/Authorization/ProjectAuthorization.php', + 'Kanboard\\Api\\Authorization\\SubtaskAuthorization' => $baseDir . '/app/Api/Authorization/SubtaskAuthorization.php', + 'Kanboard\\Api\\Authorization\\TagAuthorization' => $baseDir . '/app/Api/Authorization/TagAuthorization.php', + 'Kanboard\\Api\\Authorization\\TaskAuthorization' => $baseDir . '/app/Api/Authorization/TaskAuthorization.php', + 'Kanboard\\Api\\Authorization\\TaskFileAuthorization' => $baseDir . '/app/Api/Authorization/TaskFileAuthorization.php', + 'Kanboard\\Api\\Authorization\\TaskLinkAuthorization' => $baseDir . '/app/Api/Authorization/TaskLinkAuthorization.php', + 'Kanboard\\Api\\Authorization\\UserAuthorization' => $baseDir . '/app/Api/Authorization/UserAuthorization.php', + 'Kanboard\\Api\\Middleware\\AuthenticationMiddleware' => $baseDir . '/app/Api/Middleware/AuthenticationMiddleware.php', + 'Kanboard\\Api\\Procedure\\ActionProcedure' => $baseDir . '/app/Api/Procedure/ActionProcedure.php', + 'Kanboard\\Api\\Procedure\\AppProcedure' => $baseDir . '/app/Api/Procedure/AppProcedure.php', + 'Kanboard\\Api\\Procedure\\BaseProcedure' => $baseDir . '/app/Api/Procedure/BaseProcedure.php', + 'Kanboard\\Api\\Procedure\\BoardProcedure' => $baseDir . '/app/Api/Procedure/BoardProcedure.php', + 'Kanboard\\Api\\Procedure\\CategoryProcedure' => $baseDir . '/app/Api/Procedure/CategoryProcedure.php', + 'Kanboard\\Api\\Procedure\\ColumnProcedure' => $baseDir . '/app/Api/Procedure/ColumnProcedure.php', + 'Kanboard\\Api\\Procedure\\CommentProcedure' => $baseDir . '/app/Api/Procedure/CommentProcedure.php', + 'Kanboard\\Api\\Procedure\\GroupMemberProcedure' => $baseDir . '/app/Api/Procedure/GroupMemberProcedure.php', + 'Kanboard\\Api\\Procedure\\GroupProcedure' => $baseDir . '/app/Api/Procedure/GroupProcedure.php', + 'Kanboard\\Api\\Procedure\\LinkProcedure' => $baseDir . '/app/Api/Procedure/LinkProcedure.php', + 'Kanboard\\Api\\Procedure\\MeProcedure' => $baseDir . '/app/Api/Procedure/MeProcedure.php', + 'Kanboard\\Api\\Procedure\\ProjectFileProcedure' => $baseDir . '/app/Api/Procedure/ProjectFileProcedure.php', + 'Kanboard\\Api\\Procedure\\ProjectPermissionProcedure' => $baseDir . '/app/Api/Procedure/ProjectPermissionProcedure.php', + 'Kanboard\\Api\\Procedure\\ProjectProcedure' => $baseDir . '/app/Api/Procedure/ProjectProcedure.php', + 'Kanboard\\Api\\Procedure\\SubtaskProcedure' => $baseDir . '/app/Api/Procedure/SubtaskProcedure.php', + 'Kanboard\\Api\\Procedure\\SubtaskTimeTrackingProcedure' => $baseDir . '/app/Api/Procedure/SubtaskTimeTrackingProcedure.php', + 'Kanboard\\Api\\Procedure\\SwimlaneProcedure' => $baseDir . '/app/Api/Procedure/SwimlaneProcedure.php', + 'Kanboard\\Api\\Procedure\\TagProcedure' => $baseDir . '/app/Api/Procedure/TagProcedure.php', + 'Kanboard\\Api\\Procedure\\TaskExternalLinkProcedure' => $baseDir . '/app/Api/Procedure/TaskExternalLinkProcedure.php', + 'Kanboard\\Api\\Procedure\\TaskFileProcedure' => $baseDir . '/app/Api/Procedure/TaskFileProcedure.php', + 'Kanboard\\Api\\Procedure\\TaskLinkProcedure' => $baseDir . '/app/Api/Procedure/TaskLinkProcedure.php', + 'Kanboard\\Api\\Procedure\\TaskMetadataProcedure' => $baseDir . '/app/Api/Procedure/TaskMetadataProcedure.php', + 'Kanboard\\Api\\Procedure\\TaskProcedure' => $baseDir . '/app/Api/Procedure/TaskProcedure.php', + 'Kanboard\\Api\\Procedure\\TaskTagProcedure' => $baseDir . '/app/Api/Procedure/TaskTagProcedure.php', + 'Kanboard\\Api\\Procedure\\UserProcedure' => $baseDir . '/app/Api/Procedure/UserProcedure.php', + 'Kanboard\\Auth\\ApiAccessTokenAuth' => $baseDir . '/app/Auth/ApiAccessTokenAuth.php', + 'Kanboard\\Auth\\DatabaseAuth' => $baseDir . '/app/Auth/DatabaseAuth.php', + 'Kanboard\\Auth\\LdapAuth' => $baseDir . '/app/Auth/LdapAuth.php', + 'Kanboard\\Auth\\RememberMeAuth' => $baseDir . '/app/Auth/RememberMeAuth.php', + 'Kanboard\\Auth\\ReverseProxyAuth' => $baseDir . '/app/Auth/ReverseProxyAuth.php', + 'Kanboard\\Auth\\TotpAuth' => $baseDir . '/app/Auth/TotpAuth.php', + 'Kanboard\\Console\\BaseCommand' => $baseDir . '/app/Console/BaseCommand.php', + 'Kanboard\\Console\\CronjobCommand' => $baseDir . '/app/Console/CronjobCommand.php', + 'Kanboard\\Console\\DatabaseMigrationCommand' => $baseDir . '/app/Console/DatabaseMigrationCommand.php', + 'Kanboard\\Console\\DatabaseVersionCommand' => $baseDir . '/app/Console/DatabaseVersionCommand.php', + 'Kanboard\\Console\\JobCommand' => $baseDir . '/app/Console/JobCommand.php', + 'Kanboard\\Console\\LocaleComparatorCommand' => $baseDir . '/app/Console/LocaleComparatorCommand.php', + 'Kanboard\\Console\\LocaleSyncCommand' => $baseDir . '/app/Console/LocaleSyncCommand.php', + 'Kanboard\\Console\\PluginInstallCommand' => $baseDir . '/app/Console/PluginInstallCommand.php', + 'Kanboard\\Console\\PluginUninstallCommand' => $baseDir . '/app/Console/PluginUninstallCommand.php', + 'Kanboard\\Console\\PluginUpgradeCommand' => $baseDir . '/app/Console/PluginUpgradeCommand.php', + 'Kanboard\\Console\\ProjectDailyColumnStatsExportCommand' => $baseDir . '/app/Console/ProjectDailyColumnStatsExportCommand.php', + 'Kanboard\\Console\\ProjectDailyStatsCalculationCommand' => $baseDir . '/app/Console/ProjectDailyStatsCalculationCommand.php', + 'Kanboard\\Console\\ResetPasswordCommand' => $baseDir . '/app/Console/ResetPasswordCommand.php', + 'Kanboard\\Console\\ResetTwoFactorCommand' => $baseDir . '/app/Console/ResetTwoFactorCommand.php', + 'Kanboard\\Console\\SubtaskExportCommand' => $baseDir . '/app/Console/SubtaskExportCommand.php', + 'Kanboard\\Console\\TaskExportCommand' => $baseDir . '/app/Console/TaskExportCommand.php', + 'Kanboard\\Console\\TaskOverdueNotificationCommand' => $baseDir . '/app/Console/TaskOverdueNotificationCommand.php', + 'Kanboard\\Console\\TaskTriggerCommand' => $baseDir . '/app/Console/TaskTriggerCommand.php', + 'Kanboard\\Console\\TransitionExportCommand' => $baseDir . '/app/Console/TransitionExportCommand.php', + 'Kanboard\\Console\\WorkerCommand' => $baseDir . '/app/Console/WorkerCommand.php', + 'Kanboard\\Controller\\ActionController' => $baseDir . '/app/Controller/ActionController.php', + 'Kanboard\\Controller\\ActionCreationController' => $baseDir . '/app/Controller/ActionCreationController.php', + 'Kanboard\\Controller\\ActivityController' => $baseDir . '/app/Controller/ActivityController.php', + 'Kanboard\\Controller\\AnalyticController' => $baseDir . '/app/Controller/AnalyticController.php', + 'Kanboard\\Controller\\AppController' => $baseDir . '/app/Controller/AppController.php', + 'Kanboard\\Controller\\AuthController' => $baseDir . '/app/Controller/AuthController.php', + 'Kanboard\\Controller\\AvatarFileController' => $baseDir . '/app/Controller/AvatarFileController.php', + 'Kanboard\\Controller\\BaseController' => $baseDir . '/app/Controller/BaseController.php', + 'Kanboard\\Controller\\BoardAjaxController' => $baseDir . '/app/Controller/BoardAjaxController.php', + 'Kanboard\\Controller\\BoardPopoverController' => $baseDir . '/app/Controller/BoardPopoverController.php', + 'Kanboard\\Controller\\BoardTooltipController' => $baseDir . '/app/Controller/BoardTooltipController.php', + 'Kanboard\\Controller\\BoardViewController' => $baseDir . '/app/Controller/BoardViewController.php', + 'Kanboard\\Controller\\CaptchaController' => $baseDir . '/app/Controller/CaptchaController.php', + 'Kanboard\\Controller\\CategoryController' => $baseDir . '/app/Controller/CategoryController.php', + 'Kanboard\\Controller\\ColumnController' => $baseDir . '/app/Controller/ColumnController.php', + 'Kanboard\\Controller\\ColumnMoveRestrictionController' => $baseDir . '/app/Controller/ColumnMoveRestrictionController.php', + 'Kanboard\\Controller\\ColumnRestrictionController' => $baseDir . '/app/Controller/ColumnRestrictionController.php', + 'Kanboard\\Controller\\CommentController' => $baseDir . '/app/Controller/CommentController.php', + 'Kanboard\\Controller\\CommentListController' => $baseDir . '/app/Controller/CommentListController.php', + 'Kanboard\\Controller\\CommentMailController' => $baseDir . '/app/Controller/CommentMailController.php', + 'Kanboard\\Controller\\ConfigController' => $baseDir . '/app/Controller/ConfigController.php', + 'Kanboard\\Controller\\CurrencyController' => $baseDir . '/app/Controller/CurrencyController.php', + 'Kanboard\\Controller\\CustomFilterController' => $baseDir . '/app/Controller/CustomFilterController.php', + 'Kanboard\\Controller\\DashboardController' => $baseDir . '/app/Controller/DashboardController.php', + 'Kanboard\\Controller\\DocumentationController' => $baseDir . '/app/Controller/DocumentationController.php', + 'Kanboard\\Controller\\ExportController' => $baseDir . '/app/Controller/ExportController.php', + 'Kanboard\\Controller\\ExternalTaskCreationController' => $baseDir . '/app/Controller/ExternalTaskCreationController.php', + 'Kanboard\\Controller\\ExternalTaskViewController' => $baseDir . '/app/Controller/ExternalTaskViewController.php', + 'Kanboard\\Controller\\FeedController' => $baseDir . '/app/Controller/FeedController.php', + 'Kanboard\\Controller\\FileViewerController' => $baseDir . '/app/Controller/FileViewerController.php', + 'Kanboard\\Controller\\GroupAjaxController' => $baseDir . '/app/Controller/GroupAjaxController.php', + 'Kanboard\\Controller\\GroupCreationController' => $baseDir . '/app/Controller/GroupCreationController.php', + 'Kanboard\\Controller\\GroupListController' => $baseDir . '/app/Controller/GroupListController.php', + 'Kanboard\\Controller\\GroupModificationController' => $baseDir . '/app/Controller/GroupModificationController.php', + 'Kanboard\\Controller\\ICalendarController' => $baseDir . '/app/Controller/ICalendarController.php', + 'Kanboard\\Controller\\LinkController' => $baseDir . '/app/Controller/LinkController.php', + 'Kanboard\\Controller\\OAuthController' => $baseDir . '/app/Controller/OAuthController.php', + 'Kanboard\\Controller\\PasswordResetController' => $baseDir . '/app/Controller/PasswordResetController.php', + 'Kanboard\\Controller\\PluginController' => $baseDir . '/app/Controller/PluginController.php', + 'Kanboard\\Controller\\ProjectActionDuplicationController' => $baseDir . '/app/Controller/ProjectActionDuplicationController.php', + 'Kanboard\\Controller\\ProjectCreationController' => $baseDir . '/app/Controller/ProjectCreationController.php', + 'Kanboard\\Controller\\ProjectEditController' => $baseDir . '/app/Controller/ProjectEditController.php', + 'Kanboard\\Controller\\ProjectFileController' => $baseDir . '/app/Controller/ProjectFileController.php', + 'Kanboard\\Controller\\ProjectListController' => $baseDir . '/app/Controller/ProjectListController.php', + 'Kanboard\\Controller\\ProjectOverviewController' => $baseDir . '/app/Controller/ProjectOverviewController.php', + 'Kanboard\\Controller\\ProjectPermissionController' => $baseDir . '/app/Controller/ProjectPermissionController.php', + 'Kanboard\\Controller\\ProjectRoleController' => $baseDir . '/app/Controller/ProjectRoleController.php', + 'Kanboard\\Controller\\ProjectRoleRestrictionController' => $baseDir . '/app/Controller/ProjectRoleRestrictionController.php', + 'Kanboard\\Controller\\ProjectStatusController' => $baseDir . '/app/Controller/ProjectStatusController.php', + 'Kanboard\\Controller\\ProjectTagController' => $baseDir . '/app/Controller/ProjectTagController.php', + 'Kanboard\\Controller\\ProjectUserOverviewController' => $baseDir . '/app/Controller/ProjectUserOverviewController.php', + 'Kanboard\\Controller\\ProjectViewController' => $baseDir . '/app/Controller/ProjectViewController.php', + 'Kanboard\\Controller\\SearchController' => $baseDir . '/app/Controller/SearchController.php', + 'Kanboard\\Controller\\SubtaskController' => $baseDir . '/app/Controller/SubtaskController.php', + 'Kanboard\\Controller\\SubtaskConverterController' => $baseDir . '/app/Controller/SubtaskConverterController.php', + 'Kanboard\\Controller\\SubtaskRestrictionController' => $baseDir . '/app/Controller/SubtaskRestrictionController.php', + 'Kanboard\\Controller\\SubtaskStatusController' => $baseDir . '/app/Controller/SubtaskStatusController.php', + 'Kanboard\\Controller\\SwimlaneController' => $baseDir . '/app/Controller/SwimlaneController.php', + 'Kanboard\\Controller\\TagController' => $baseDir . '/app/Controller/TagController.php', + 'Kanboard\\Controller\\TaskAjaxController' => $baseDir . '/app/Controller/TaskAjaxController.php', + 'Kanboard\\Controller\\TaskBulkController' => $baseDir . '/app/Controller/TaskBulkController.php', + 'Kanboard\\Controller\\TaskCreationController' => $baseDir . '/app/Controller/TaskCreationController.php', + 'Kanboard\\Controller\\TaskDuplicationController' => $baseDir . '/app/Controller/TaskDuplicationController.php', + 'Kanboard\\Controller\\TaskExternalLinkController' => $baseDir . '/app/Controller/TaskExternalLinkController.php', + 'Kanboard\\Controller\\TaskFileController' => $baseDir . '/app/Controller/TaskFileController.php', + 'Kanboard\\Controller\\TaskImportController' => $baseDir . '/app/Controller/TaskImportController.php', + 'Kanboard\\Controller\\TaskInternalLinkController' => $baseDir . '/app/Controller/TaskInternalLinkController.php', + 'Kanboard\\Controller\\TaskListController' => $baseDir . '/app/Controller/TaskListController.php', + 'Kanboard\\Controller\\TaskMailController' => $baseDir . '/app/Controller/TaskMailController.php', + 'Kanboard\\Controller\\TaskModificationController' => $baseDir . '/app/Controller/TaskModificationController.php', + 'Kanboard\\Controller\\TaskMovePositionController' => $baseDir . '/app/Controller/TaskMovePositionController.php', + 'Kanboard\\Controller\\TaskPopoverController' => $baseDir . '/app/Controller/TaskPopoverController.php', + 'Kanboard\\Controller\\TaskRecurrenceController' => $baseDir . '/app/Controller/TaskRecurrenceController.php', + 'Kanboard\\Controller\\TaskStatusController' => $baseDir . '/app/Controller/TaskStatusController.php', + 'Kanboard\\Controller\\TaskSuppressionController' => $baseDir . '/app/Controller/TaskSuppressionController.php', + 'Kanboard\\Controller\\TaskViewController' => $baseDir . '/app/Controller/TaskViewController.php', + 'Kanboard\\Controller\\TwoFactorController' => $baseDir . '/app/Controller/TwoFactorController.php', + 'Kanboard\\Controller\\UserAjaxController' => $baseDir . '/app/Controller/UserAjaxController.php', + 'Kanboard\\Controller\\UserApiAccessController' => $baseDir . '/app/Controller/UserApiAccessController.php', + 'Kanboard\\Controller\\UserCreationController' => $baseDir . '/app/Controller/UserCreationController.php', + 'Kanboard\\Controller\\UserCredentialController' => $baseDir . '/app/Controller/UserCredentialController.php', + 'Kanboard\\Controller\\UserImportController' => $baseDir . '/app/Controller/UserImportController.php', + 'Kanboard\\Controller\\UserInviteController' => $baseDir . '/app/Controller/UserInviteController.php', + 'Kanboard\\Controller\\UserListController' => $baseDir . '/app/Controller/UserListController.php', + 'Kanboard\\Controller\\UserModificationController' => $baseDir . '/app/Controller/UserModificationController.php', + 'Kanboard\\Controller\\UserStatusController' => $baseDir . '/app/Controller/UserStatusController.php', + 'Kanboard\\Controller\\UserViewController' => $baseDir . '/app/Controller/UserViewController.php', + 'Kanboard\\Controller\\WebNotificationController' => $baseDir . '/app/Controller/WebNotificationController.php', + 'Kanboard\\Core\\Action\\ActionManager' => $baseDir . '/app/Core/Action/ActionManager.php', + 'Kanboard\\Core\\Base' => $baseDir . '/app/Core/Base.php', + 'Kanboard\\Core\\Cache\\BaseCache' => $baseDir . '/app/Core/Cache/BaseCache.php', + 'Kanboard\\Core\\Cache\\CacheInterface' => $baseDir . '/app/Core/Cache/CacheInterface.php', + 'Kanboard\\Core\\Cache\\FileCache' => $baseDir . '/app/Core/Cache/FileCache.php', + 'Kanboard\\Core\\Cache\\MemoryCache' => $baseDir . '/app/Core/Cache/MemoryCache.php', + 'Kanboard\\Core\\Controller\\AccessForbiddenException' => $baseDir . '/app/Core/Controller/AccessForbiddenException.php', + 'Kanboard\\Core\\Controller\\BaseException' => $baseDir . '/app/Core/Controller/BaseException.php', + 'Kanboard\\Core\\Controller\\BaseMiddleware' => $baseDir . '/app/Core/Controller/BaseMiddleware.php', + 'Kanboard\\Core\\Controller\\PageNotFoundException' => $baseDir . '/app/Core/Controller/PageNotFoundException.php', + 'Kanboard\\Core\\Controller\\Runner' => $baseDir . '/app/Core/Controller/Runner.php', + 'Kanboard\\Core\\Csv' => $baseDir . '/app/Core/Csv.php', + 'Kanboard\\Core\\DateParser' => $baseDir . '/app/Core/DateParser.php', + 'Kanboard\\Core\\Event\\EventManager' => $baseDir . '/app/Core/Event/EventManager.php', + 'Kanboard\\Core\\ExternalLink\\ExternalLinkInterface' => $baseDir . '/app/Core/ExternalLink/ExternalLinkInterface.php', + 'Kanboard\\Core\\ExternalLink\\ExternalLinkManager' => $baseDir . '/app/Core/ExternalLink/ExternalLinkManager.php', + 'Kanboard\\Core\\ExternalLink\\ExternalLinkProviderInterface' => $baseDir . '/app/Core/ExternalLink/ExternalLinkProviderInterface.php', + 'Kanboard\\Core\\ExternalLink\\ExternalLinkProviderNotFound' => $baseDir . '/app/Core/ExternalLink/ExternalLinkProviderNotFound.php', + 'Kanboard\\Core\\ExternalTask\\AccessForbiddenException' => $baseDir . '/app/Core/ExternalTask/AccessForbiddenException.php', + 'Kanboard\\Core\\ExternalTask\\ExternalTaskException' => $baseDir . '/app/Core/ExternalTask/ExternalTaskException.php', + 'Kanboard\\Core\\ExternalTask\\ExternalTaskInterface' => $baseDir . '/app/Core/ExternalTask/ExternalTaskInterface.php', + 'Kanboard\\Core\\ExternalTask\\ExternalTaskManager' => $baseDir . '/app/Core/ExternalTask/ExternalTaskManager.php', + 'Kanboard\\Core\\ExternalTask\\ExternalTaskProviderInterface' => $baseDir . '/app/Core/ExternalTask/ExternalTaskProviderInterface.php', + 'Kanboard\\Core\\ExternalTask\\NotFoundException' => $baseDir . '/app/Core/ExternalTask/NotFoundException.php', + 'Kanboard\\Core\\ExternalTask\\ProviderNotFoundException' => $baseDir . '/app/Core/ExternalTask/ProviderNotFoundException.php', + 'Kanboard\\Core\\Filter\\CriteriaInterface' => $baseDir . '/app/Core/Filter/CriteriaInterface.php', + 'Kanboard\\Core\\Filter\\FilterInterface' => $baseDir . '/app/Core/Filter/FilterInterface.php', + 'Kanboard\\Core\\Filter\\FormatterInterface' => $baseDir . '/app/Core/Filter/FormatterInterface.php', + 'Kanboard\\Core\\Filter\\Lexer' => $baseDir . '/app/Core/Filter/Lexer.php', + 'Kanboard\\Core\\Filter\\LexerBuilder' => $baseDir . '/app/Core/Filter/LexerBuilder.php', + 'Kanboard\\Core\\Filter\\OrCriteria' => $baseDir . '/app/Core/Filter/OrCriteria.php', + 'Kanboard\\Core\\Filter\\QueryBuilder' => $baseDir . '/app/Core/Filter/QueryBuilder.php', + 'Kanboard\\Core\\Group\\GroupBackendProviderInterface' => $baseDir . '/app/Core/Group/GroupBackendProviderInterface.php', + 'Kanboard\\Core\\Group\\GroupManager' => $baseDir . '/app/Core/Group/GroupManager.php', + 'Kanboard\\Core\\Group\\GroupProviderInterface' => $baseDir . '/app/Core/Group/GroupProviderInterface.php', + 'Kanboard\\Core\\Helper' => $baseDir . '/app/Core/Helper.php', + 'Kanboard\\Core\\Http\\Client' => $baseDir . '/app/Core/Http/Client.php', + 'Kanboard\\Core\\Http\\OAuth2' => $baseDir . '/app/Core/Http/OAuth2.php', + 'Kanboard\\Core\\Http\\RememberMeCookie' => $baseDir . '/app/Core/Http/RememberMeCookie.php', + 'Kanboard\\Core\\Http\\Request' => $baseDir . '/app/Core/Http/Request.php', + 'Kanboard\\Core\\Http\\Response' => $baseDir . '/app/Core/Http/Response.php', + 'Kanboard\\Core\\Http\\Route' => $baseDir . '/app/Core/Http/Route.php', + 'Kanboard\\Core\\Http\\Router' => $baseDir . '/app/Core/Http/Router.php', + 'Kanboard\\Core\\Ldap\\Client' => $baseDir . '/app/Core/Ldap/Client.php', + 'Kanboard\\Core\\Ldap\\ClientException' => $baseDir . '/app/Core/Ldap/ClientException.php', + 'Kanboard\\Core\\Ldap\\ConnectionException' => $baseDir . '/app/Core/Ldap/ConnectionException.php', + 'Kanboard\\Core\\Ldap\\Entries' => $baseDir . '/app/Core/Ldap/Entries.php', + 'Kanboard\\Core\\Ldap\\Entry' => $baseDir . '/app/Core/Ldap/Entry.php', + 'Kanboard\\Core\\Ldap\\Group' => $baseDir . '/app/Core/Ldap/Group.php', + 'Kanboard\\Core\\Ldap\\Query' => $baseDir . '/app/Core/Ldap/Query.php', + 'Kanboard\\Core\\Ldap\\User' => $baseDir . '/app/Core/Ldap/User.php', + 'Kanboard\\Core\\Mail\\Client' => $baseDir . '/app/Core/Mail/Client.php', + 'Kanboard\\Core\\Mail\\ClientInterface' => $baseDir . '/app/Core/Mail/ClientInterface.php', + 'Kanboard\\Core\\Mail\\Transport\\Mail' => $baseDir . '/app/Core/Mail/Transport/Mail.php', + 'Kanboard\\Core\\Mail\\Transport\\Sendmail' => $baseDir . '/app/Core/Mail/Transport/Sendmail.php', + 'Kanboard\\Core\\Mail\\Transport\\Smtp' => $baseDir . '/app/Core/Mail/Transport/Smtp.php', + 'Kanboard\\Core\\Markdown' => $baseDir . '/app/Core/Markdown.php', + 'Kanboard\\Core\\Notification\\NotificationInterface' => $baseDir . '/app/Core/Notification/NotificationInterface.php', + 'Kanboard\\Core\\ObjectStorage\\FileStorage' => $baseDir . '/app/Core/ObjectStorage/FileStorage.php', + 'Kanboard\\Core\\ObjectStorage\\ObjectStorageException' => $baseDir . '/app/Core/ObjectStorage/ObjectStorageException.php', + 'Kanboard\\Core\\ObjectStorage\\ObjectStorageInterface' => $baseDir . '/app/Core/ObjectStorage/ObjectStorageInterface.php', + 'Kanboard\\Core\\Paginator' => $baseDir . '/app/Core/Paginator.php', + 'Kanboard\\Core\\Plugin\\Base' => $baseDir . '/app/Core/Plugin/Base.php', + 'Kanboard\\Core\\Plugin\\Directory' => $baseDir . '/app/Core/Plugin/Directory.php', + 'Kanboard\\Core\\Plugin\\Hook' => $baseDir . '/app/Core/Plugin/Hook.php', + 'Kanboard\\Core\\Plugin\\Installer' => $baseDir . '/app/Core/Plugin/Installer.php', + 'Kanboard\\Core\\Plugin\\Loader' => $baseDir . '/app/Core/Plugin/Loader.php', + 'Kanboard\\Core\\Plugin\\PluginException' => $baseDir . '/app/Core/Plugin/PluginException.php', + 'Kanboard\\Core\\Plugin\\PluginInstallerException' => $baseDir . '/app/Core/Plugin/PluginInstallerException.php', + 'Kanboard\\Core\\Plugin\\SchemaHandler' => $baseDir . '/app/Core/Plugin/SchemaHandler.php', + 'Kanboard\\Core\\Plugin\\Version' => $baseDir . '/app/Core/Plugin/Version.php', + 'Kanboard\\Core\\Queue\\JobHandler' => $baseDir . '/app/Core/Queue/JobHandler.php', + 'Kanboard\\Core\\Queue\\QueueManager' => $baseDir . '/app/Core/Queue/QueueManager.php', + 'Kanboard\\Core\\Security\\AccessMap' => $baseDir . '/app/Core/Security/AccessMap.php', + 'Kanboard\\Core\\Security\\AuthenticationManager' => $baseDir . '/app/Core/Security/AuthenticationManager.php', + 'Kanboard\\Core\\Security\\AuthenticationProviderInterface' => $baseDir . '/app/Core/Security/AuthenticationProviderInterface.php', + 'Kanboard\\Core\\Security\\Authorization' => $baseDir . '/app/Core/Security/Authorization.php', + 'Kanboard\\Core\\Security\\OAuthAuthenticationProviderInterface' => $baseDir . '/app/Core/Security/OAuthAuthenticationProviderInterface.php', + 'Kanboard\\Core\\Security\\PasswordAuthenticationProviderInterface' => $baseDir . '/app/Core/Security/PasswordAuthenticationProviderInterface.php', + 'Kanboard\\Core\\Security\\PostAuthenticationProviderInterface' => $baseDir . '/app/Core/Security/PostAuthenticationProviderInterface.php', + 'Kanboard\\Core\\Security\\PreAuthenticationProviderInterface' => $baseDir . '/app/Core/Security/PreAuthenticationProviderInterface.php', + 'Kanboard\\Core\\Security\\Role' => $baseDir . '/app/Core/Security/Role.php', + 'Kanboard\\Core\\Security\\SessionCheckProviderInterface' => $baseDir . '/app/Core/Security/SessionCheckProviderInterface.php', + 'Kanboard\\Core\\Security\\Token' => $baseDir . '/app/Core/Security/Token.php', + 'Kanboard\\Core\\Session\\FlashMessage' => $baseDir . '/app/Core/Session/FlashMessage.php', + 'Kanboard\\Core\\Session\\SessionManager' => $baseDir . '/app/Core/Session/SessionManager.php', + 'Kanboard\\Core\\Session\\SessionStorage' => $baseDir . '/app/Core/Session/SessionStorage.php', + 'Kanboard\\Core\\Template' => $baseDir . '/app/Core/Template.php', + 'Kanboard\\Core\\Thumbnail' => $baseDir . '/app/Core/Thumbnail.php', + 'Kanboard\\Core\\Tool' => $baseDir . '/app/Core/Tool.php', + 'Kanboard\\Core\\Translator' => $baseDir . '/app/Core/Translator.php', + 'Kanboard\\Core\\User\\Avatar\\AvatarManager' => $baseDir . '/app/Core/User/Avatar/AvatarManager.php', + 'Kanboard\\Core\\User\\Avatar\\AvatarProviderInterface' => $baseDir . '/app/Core/User/Avatar/AvatarProviderInterface.php', + 'Kanboard\\Core\\User\\GroupSync' => $baseDir . '/app/Core/User/GroupSync.php', + 'Kanboard\\Core\\User\\UserProfile' => $baseDir . '/app/Core/User/UserProfile.php', + 'Kanboard\\Core\\User\\UserProperty' => $baseDir . '/app/Core/User/UserProperty.php', + 'Kanboard\\Core\\User\\UserProviderInterface' => $baseDir . '/app/Core/User/UserProviderInterface.php', + 'Kanboard\\Core\\User\\UserSession' => $baseDir . '/app/Core/User/UserSession.php', + 'Kanboard\\Core\\User\\UserSync' => $baseDir . '/app/Core/User/UserSync.php', + 'Kanboard\\Decorator\\ColumnMoveRestrictionCacheDecorator' => $baseDir . '/app/Decorator/ColumnMoveRestrictionCacheDecorator.php', + 'Kanboard\\Decorator\\ColumnRestrictionCacheDecorator' => $baseDir . '/app/Decorator/ColumnRestrictionCacheDecorator.php', + 'Kanboard\\Decorator\\MetadataCacheDecorator' => $baseDir . '/app/Decorator/MetadataCacheDecorator.php', + 'Kanboard\\Decorator\\ProjectRoleRestrictionCacheDecorator' => $baseDir . '/app/Decorator/ProjectRoleRestrictionCacheDecorator.php', + 'Kanboard\\Decorator\\UserCacheDecorator' => $baseDir . '/app/Decorator/UserCacheDecorator.php', + 'Kanboard\\EventBuilder\\BaseEventBuilder' => $baseDir . '/app/EventBuilder/BaseEventBuilder.php', + 'Kanboard\\EventBuilder\\CommentEventBuilder' => $baseDir . '/app/EventBuilder/CommentEventBuilder.php', + 'Kanboard\\EventBuilder\\EventIteratorBuilder' => $baseDir . '/app/EventBuilder/EventIteratorBuilder.php', + 'Kanboard\\EventBuilder\\ProjectFileEventBuilder' => $baseDir . '/app/EventBuilder/ProjectFileEventBuilder.php', + 'Kanboard\\EventBuilder\\SubtaskEventBuilder' => $baseDir . '/app/EventBuilder/SubtaskEventBuilder.php', + 'Kanboard\\EventBuilder\\TaskEventBuilder' => $baseDir . '/app/EventBuilder/TaskEventBuilder.php', + 'Kanboard\\EventBuilder\\TaskFileEventBuilder' => $baseDir . '/app/EventBuilder/TaskFileEventBuilder.php', + 'Kanboard\\EventBuilder\\TaskLinkEventBuilder' => $baseDir . '/app/EventBuilder/TaskLinkEventBuilder.php', + 'Kanboard\\Event\\AuthFailureEvent' => $baseDir . '/app/Event/AuthFailureEvent.php', + 'Kanboard\\Event\\AuthSuccessEvent' => $baseDir . '/app/Event/AuthSuccessEvent.php', + 'Kanboard\\Event\\CommentEvent' => $baseDir . '/app/Event/CommentEvent.php', + 'Kanboard\\Event\\GenericEvent' => $baseDir . '/app/Event/GenericEvent.php', + 'Kanboard\\Event\\ProjectFileEvent' => $baseDir . '/app/Event/ProjectFileEvent.php', + 'Kanboard\\Event\\SubtaskEvent' => $baseDir . '/app/Event/SubtaskEvent.php', + 'Kanboard\\Event\\TaskEvent' => $baseDir . '/app/Event/TaskEvent.php', + 'Kanboard\\Event\\TaskFileEvent' => $baseDir . '/app/Event/TaskFileEvent.php', + 'Kanboard\\Event\\TaskLinkEvent' => $baseDir . '/app/Event/TaskLinkEvent.php', + 'Kanboard\\Event\\TaskListEvent' => $baseDir . '/app/Event/TaskListEvent.php', + 'Kanboard\\Event\\UserProfileSyncEvent' => $baseDir . '/app/Event/UserProfileSyncEvent.php', + 'Kanboard\\Export\\SubtaskExport' => $baseDir . '/app/Export/SubtaskExport.php', + 'Kanboard\\Export\\TaskExport' => $baseDir . '/app/Export/TaskExport.php', + 'Kanboard\\Export\\TransitionExport' => $baseDir . '/app/Export/TransitionExport.php', + 'Kanboard\\ExternalLink\\AttachmentLink' => $baseDir . '/app/ExternalLink/AttachmentLink.php', + 'Kanboard\\ExternalLink\\AttachmentLinkProvider' => $baseDir . '/app/ExternalLink/AttachmentLinkProvider.php', + 'Kanboard\\ExternalLink\\BaseLink' => $baseDir . '/app/ExternalLink/BaseLink.php', + 'Kanboard\\ExternalLink\\BaseLinkProvider' => $baseDir . '/app/ExternalLink/BaseLinkProvider.php', + 'Kanboard\\ExternalLink\\FileLink' => $baseDir . '/app/ExternalLink/FileLink.php', + 'Kanboard\\ExternalLink\\FileLinkProvider' => $baseDir . '/app/ExternalLink/FileLinkProvider.php', + 'Kanboard\\ExternalLink\\WebLink' => $baseDir . '/app/ExternalLink/WebLink.php', + 'Kanboard\\ExternalLink\\WebLinkProvider' => $baseDir . '/app/ExternalLink/WebLinkProvider.php', + 'Kanboard\\Filter\\BaseComparisonFilter' => $baseDir . '/app/Filter/BaseComparisonFilter.php', + 'Kanboard\\Filter\\BaseDateFilter' => $baseDir . '/app/Filter/BaseDateFilter.php', + 'Kanboard\\Filter\\BaseFilter' => $baseDir . '/app/Filter/BaseFilter.php', + 'Kanboard\\Filter\\ProjectActivityCreationDateFilter' => $baseDir . '/app/Filter/ProjectActivityCreationDateFilter.php', + 'Kanboard\\Filter\\ProjectActivityCreatorFilter' => $baseDir . '/app/Filter/ProjectActivityCreatorFilter.php', + 'Kanboard\\Filter\\ProjectActivityProjectIdFilter' => $baseDir . '/app/Filter/ProjectActivityProjectIdFilter.php', + 'Kanboard\\Filter\\ProjectActivityProjectIdsFilter' => $baseDir . '/app/Filter/ProjectActivityProjectIdsFilter.php', + 'Kanboard\\Filter\\ProjectActivityProjectNameFilter' => $baseDir . '/app/Filter/ProjectActivityProjectNameFilter.php', + 'Kanboard\\Filter\\ProjectActivityTaskIdFilter' => $baseDir . '/app/Filter/ProjectActivityTaskIdFilter.php', + 'Kanboard\\Filter\\ProjectActivityTaskStatusFilter' => $baseDir . '/app/Filter/ProjectActivityTaskStatusFilter.php', + 'Kanboard\\Filter\\ProjectActivityTaskTitleFilter' => $baseDir . '/app/Filter/ProjectActivityTaskTitleFilter.php', + 'Kanboard\\Filter\\ProjectGroupRoleProjectFilter' => $baseDir . '/app/Filter/ProjectGroupRoleProjectFilter.php', + 'Kanboard\\Filter\\ProjectGroupRoleUsernameFilter' => $baseDir . '/app/Filter/ProjectGroupRoleUsernameFilter.php', + 'Kanboard\\Filter\\ProjectIdsFilter' => $baseDir . '/app/Filter/ProjectIdsFilter.php', + 'Kanboard\\Filter\\ProjectStatusFilter' => $baseDir . '/app/Filter/ProjectStatusFilter.php', + 'Kanboard\\Filter\\ProjectTypeFilter' => $baseDir . '/app/Filter/ProjectTypeFilter.php', + 'Kanboard\\Filter\\ProjectUserRoleProjectFilter' => $baseDir . '/app/Filter/ProjectUserRoleProjectFilter.php', + 'Kanboard\\Filter\\ProjectUserRoleUsernameFilter' => $baseDir . '/app/Filter/ProjectUserRoleUsernameFilter.php', + 'Kanboard\\Filter\\TaskAssigneeFilter' => $baseDir . '/app/Filter/TaskAssigneeFilter.php', + 'Kanboard\\Filter\\TaskCategoryFilter' => $baseDir . '/app/Filter/TaskCategoryFilter.php', + 'Kanboard\\Filter\\TaskColorFilter' => $baseDir . '/app/Filter/TaskColorFilter.php', + 'Kanboard\\Filter\\TaskColumnFilter' => $baseDir . '/app/Filter/TaskColumnFilter.php', + 'Kanboard\\Filter\\TaskCommentFilter' => $baseDir . '/app/Filter/TaskCommentFilter.php', + 'Kanboard\\Filter\\TaskCompletionDateFilter' => $baseDir . '/app/Filter/TaskCompletionDateFilter.php', + 'Kanboard\\Filter\\TaskCreationDateFilter' => $baseDir . '/app/Filter/TaskCreationDateFilter.php', + 'Kanboard\\Filter\\TaskCreatorFilter' => $baseDir . '/app/Filter/TaskCreatorFilter.php', + 'Kanboard\\Filter\\TaskDescriptionFilter' => $baseDir . '/app/Filter/TaskDescriptionFilter.php', + 'Kanboard\\Filter\\TaskDueDateFilter' => $baseDir . '/app/Filter/TaskDueDateFilter.php', + 'Kanboard\\Filter\\TaskDueDateRangeFilter' => $baseDir . '/app/Filter/TaskDueDateRangeFilter.php', + 'Kanboard\\Filter\\TaskIdExclusionFilter' => $baseDir . '/app/Filter/TaskIdExclusionFilter.php', + 'Kanboard\\Filter\\TaskIdFilter' => $baseDir . '/app/Filter/TaskIdFilter.php', + 'Kanboard\\Filter\\TaskLinkFilter' => $baseDir . '/app/Filter/TaskLinkFilter.php', + 'Kanboard\\Filter\\TaskModificationDateFilter' => $baseDir . '/app/Filter/TaskModificationDateFilter.php', + 'Kanboard\\Filter\\TaskMovedDateFilter' => $baseDir . '/app/Filter/TaskMovedDateFilter.php', + 'Kanboard\\Filter\\TaskPriorityFilter' => $baseDir . '/app/Filter/TaskPriorityFilter.php', + 'Kanboard\\Filter\\TaskProjectFilter' => $baseDir . '/app/Filter/TaskProjectFilter.php', + 'Kanboard\\Filter\\TaskProjectsFilter' => $baseDir . '/app/Filter/TaskProjectsFilter.php', + 'Kanboard\\Filter\\TaskReferenceFilter' => $baseDir . '/app/Filter/TaskReferenceFilter.php', + 'Kanboard\\Filter\\TaskScoreFilter' => $baseDir . '/app/Filter/TaskScoreFilter.php', + 'Kanboard\\Filter\\TaskStartDateFilter' => $baseDir . '/app/Filter/TaskStartDateFilter.php', + 'Kanboard\\Filter\\TaskStartsWithIdFilter' => $baseDir . '/app/Filter/TaskStartsWithIdFilter.php', + 'Kanboard\\Filter\\TaskStatusFilter' => $baseDir . '/app/Filter/TaskStatusFilter.php', + 'Kanboard\\Filter\\TaskSubtaskAssigneeFilter' => $baseDir . '/app/Filter/TaskSubtaskAssigneeFilter.php', + 'Kanboard\\Filter\\TaskSwimlaneFilter' => $baseDir . '/app/Filter/TaskSwimlaneFilter.php', + 'Kanboard\\Filter\\TaskTagFilter' => $baseDir . '/app/Filter/TaskTagFilter.php', + 'Kanboard\\Filter\\TaskTitleFilter' => $baseDir . '/app/Filter/TaskTitleFilter.php', + 'Kanboard\\Filter\\UserNameFilter' => $baseDir . '/app/Filter/UserNameFilter.php', + 'Kanboard\\Formatter\\BaseFormatter' => $baseDir . '/app/Formatter/BaseFormatter.php', + 'Kanboard\\Formatter\\BaseTaskCalendarFormatter' => $baseDir . '/app/Formatter/BaseTaskCalendarFormatter.php', + 'Kanboard\\Formatter\\BoardColumnFormatter' => $baseDir . '/app/Formatter/BoardColumnFormatter.php', + 'Kanboard\\Formatter\\BoardFormatter' => $baseDir . '/app/Formatter/BoardFormatter.php', + 'Kanboard\\Formatter\\BoardSwimlaneFormatter' => $baseDir . '/app/Formatter/BoardSwimlaneFormatter.php', + 'Kanboard\\Formatter\\BoardTaskFormatter' => $baseDir . '/app/Formatter/BoardTaskFormatter.php', + 'Kanboard\\Formatter\\GroupAutoCompleteFormatter' => $baseDir . '/app/Formatter/GroupAutoCompleteFormatter.php', + 'Kanboard\\Formatter\\ProjectActivityEventFormatter' => $baseDir . '/app/Formatter/ProjectActivityEventFormatter.php', + 'Kanboard\\Formatter\\ProjectApiFormatter' => $baseDir . '/app/Formatter/ProjectApiApiFormatter.php', + 'Kanboard\\Formatter\\ProjectsApiFormatter' => $baseDir . '/app/Formatter/ProjectsApiFormatter.php', + 'Kanboard\\Formatter\\SubtaskListFormatter' => $baseDir . '/app/Formatter/SubtaskListFormatter.php', + 'Kanboard\\Formatter\\SubtaskTimeTrackingCalendarFormatter' => $baseDir . '/app/Formatter/SubtaskTimeTrackingCalendarFormatter.php', + 'Kanboard\\Formatter\\TaskApiFormatter' => $baseDir . '/app/Formatter/TaskApiFormatter.php', + 'Kanboard\\Formatter\\TaskAutoCompleteFormatter' => $baseDir . '/app/Formatter/TaskAutoCompleteFormatter.php', + 'Kanboard\\Formatter\\TaskICalFormatter' => $baseDir . '/app/Formatter/TaskICalFormatter.php', + 'Kanboard\\Formatter\\TaskListFormatter' => $baseDir . '/app/Formatter/TaskListFormatter.php', + 'Kanboard\\Formatter\\TaskListSubtaskAssigneeFormatter' => $baseDir . '/app/Formatter/TaskListSubtaskAssigneeFormatter.php', + 'Kanboard\\Formatter\\TaskListSubtaskFormatter' => $baseDir . '/app/Formatter/TaskListSubtaskFormatter.php', + 'Kanboard\\Formatter\\TaskSuggestMenuFormatter' => $baseDir . '/app/Formatter/TaskSuggestMenuFormatter.php', + 'Kanboard\\Formatter\\TasksApiFormatter' => $baseDir . '/app/Formatter/TasksApiFormatter.php', + 'Kanboard\\Formatter\\UserAutoCompleteFormatter' => $baseDir . '/app/Formatter/UserAutoCompleteFormatter.php', + 'Kanboard\\Formatter\\UserMentionFormatter' => $baseDir . '/app/Formatter/UserMentionFormatter.php', + 'Kanboard\\Group\\DatabaseBackendGroupProvider' => $baseDir . '/app/Group/DatabaseBackendGroupProvider.php', + 'Kanboard\\Group\\DatabaseGroupProvider' => $baseDir . '/app/Group/DatabaseGroupProvider.php', + 'Kanboard\\Group\\LdapBackendGroupProvider' => $baseDir . '/app/Group/LdapBackendGroupProvider.php', + 'Kanboard\\Group\\LdapGroupProvider' => $baseDir . '/app/Group/LdapGroupProvider.php', + 'Kanboard\\Helper\\AppHelper' => $baseDir . '/app/Helper/AppHelper.php', + 'Kanboard\\Helper\\AssetHelper' => $baseDir . '/app/Helper/AssetHelper.php', + 'Kanboard\\Helper\\AvatarHelper' => $baseDir . '/app/Helper/AvatarHelper.php', + 'Kanboard\\Helper\\BoardHelper' => $baseDir . '/app/Helper/BoardHelper.php', + 'Kanboard\\Helper\\CommentHelper' => $baseDir . '/app/Helper/CommentHelper.php', + 'Kanboard\\Helper\\DateHelper' => $baseDir . '/app/Helper/DateHelper.php', + 'Kanboard\\Helper\\FileHelper' => $baseDir . '/app/Helper/FileHelper.php', + 'Kanboard\\Helper\\FormHelper' => $baseDir . '/app/Helper/FormHelper.php', + 'Kanboard\\Helper\\HookHelper' => $baseDir . '/app/Helper/HookHelper.php', + 'Kanboard\\Helper\\ICalHelper' => $baseDir . '/app/Helper/ICalHelper.php', + 'Kanboard\\Helper\\LayoutHelper' => $baseDir . '/app/Helper/LayoutHelper.php', + 'Kanboard\\Helper\\MailHelper' => $baseDir . '/app/Helper/MailHelper.php', + 'Kanboard\\Helper\\ModalHelper' => $baseDir . '/app/Helper/ModalHelper.php', + 'Kanboard\\Helper\\ModelHelper' => $baseDir . '/app/Helper/ModelHelper.php', + 'Kanboard\\Helper\\ProjectActivityHelper' => $baseDir . '/app/Helper/ProjectActivityHelper.php', + 'Kanboard\\Helper\\ProjectHeaderHelper' => $baseDir . '/app/Helper/ProjectHeaderHelper.php', + 'Kanboard\\Helper\\ProjectRoleHelper' => $baseDir . '/app/Helper/ProjectRoleHelper.php', + 'Kanboard\\Helper\\SubtaskHelper' => $baseDir . '/app/Helper/SubtaskHelper.php', + 'Kanboard\\Helper\\TaskHelper' => $baseDir . '/app/Helper/TaskHelper.php', + 'Kanboard\\Helper\\TextHelper' => $baseDir . '/app/Helper/TextHelper.php', + 'Kanboard\\Helper\\UrlHelper' => $baseDir . '/app/Helper/UrlHelper.php', + 'Kanboard\\Helper\\UserHelper' => $baseDir . '/app/Helper/UserHelper.php', + 'Kanboard\\Import\\TaskImport' => $baseDir . '/app/Import/TaskImport.php', + 'Kanboard\\Import\\UserImport' => $baseDir . '/app/Import/UserImport.php', + 'Kanboard\\Job\\BaseJob' => $baseDir . '/app/Job/BaseJob.php', + 'Kanboard\\Job\\CommentEventJob' => $baseDir . '/app/Job/CommentEventJob.php', + 'Kanboard\\Job\\EmailJob' => $baseDir . '/app/Job/EmailJob.php', + 'Kanboard\\Job\\HttpAsyncJob' => $baseDir . '/app/Job/HttpAsyncJob.php', + 'Kanboard\\Job\\NotificationJob' => $baseDir . '/app/Job/NotificationJob.php', + 'Kanboard\\Job\\ProjectFileEventJob' => $baseDir . '/app/Job/ProjectFileEventJob.php', + 'Kanboard\\Job\\ProjectMetricJob' => $baseDir . '/app/Job/ProjectMetricJob.php', + 'Kanboard\\Job\\SubtaskEventJob' => $baseDir . '/app/Job/SubtaskEventJob.php', + 'Kanboard\\Job\\TaskEventJob' => $baseDir . '/app/Job/TaskEventJob.php', + 'Kanboard\\Job\\TaskFileEventJob' => $baseDir . '/app/Job/TaskFileEventJob.php', + 'Kanboard\\Job\\TaskLinkEventJob' => $baseDir . '/app/Job/TaskLinkEventJob.php', + 'Kanboard\\Job\\UserMentionJob' => $baseDir . '/app/Job/UserMentionJob.php', + 'Kanboard\\Middleware\\ApplicationAuthorizationMiddleware' => $baseDir . '/app/Middleware/ApplicationAuthorizationMiddleware.php', + 'Kanboard\\Middleware\\AuthenticationMiddleware' => $baseDir . '/app/Middleware/AuthenticationMiddleware.php', + 'Kanboard\\Middleware\\BootstrapMiddleware' => $baseDir . '/app/Middleware/BootstrapMiddleware.php', + 'Kanboard\\Middleware\\PostAuthenticationMiddleware' => $baseDir . '/app/Middleware/PostAuthenticationMiddleware.php', + 'Kanboard\\Middleware\\ProjectAuthorizationMiddleware' => $baseDir . '/app/Middleware/ProjectAuthorizationMiddleware.php', + 'Kanboard\\Model\\ActionModel' => $baseDir . '/app/Model/ActionModel.php', + 'Kanboard\\Model\\ActionParameterModel' => $baseDir . '/app/Model/ActionParameterModel.php', + 'Kanboard\\Model\\AvatarFileModel' => $baseDir . '/app/Model/AvatarFileModel.php', + 'Kanboard\\Model\\BoardModel' => $baseDir . '/app/Model/BoardModel.php', + 'Kanboard\\Model\\CategoryModel' => $baseDir . '/app/Model/CategoryModel.php', + 'Kanboard\\Model\\ColorModel' => $baseDir . '/app/Model/ColorModel.php', + 'Kanboard\\Model\\ColumnModel' => $baseDir . '/app/Model/ColumnModel.php', + 'Kanboard\\Model\\ColumnMoveRestrictionModel' => $baseDir . '/app/Model/ColumnMoveRestrictionModel.php', + 'Kanboard\\Model\\ColumnRestrictionModel' => $baseDir . '/app/Model/ColumnRestrictionModel.php', + 'Kanboard\\Model\\CommentModel' => $baseDir . '/app/Model/CommentModel.php', + 'Kanboard\\Model\\ConfigModel' => $baseDir . '/app/Model/ConfigModel.php', + 'Kanboard\\Model\\CurrencyModel' => $baseDir . '/app/Model/CurrencyModel.php', + 'Kanboard\\Model\\CustomFilterModel' => $baseDir . '/app/Model/CustomFilterModel.php', + 'Kanboard\\Model\\FileModel' => $baseDir . '/app/Model/FileModel.php', + 'Kanboard\\Model\\GroupMemberModel' => $baseDir . '/app/Model/GroupMemberModel.php', + 'Kanboard\\Model\\GroupModel' => $baseDir . '/app/Model/GroupModel.php', + 'Kanboard\\Model\\InviteModel' => $baseDir . '/app/Model/InviteModel.php', + 'Kanboard\\Model\\LanguageModel' => $baseDir . '/app/Model/LanguageModel.php', + 'Kanboard\\Model\\LastLoginModel' => $baseDir . '/app/Model/LastLoginModel.php', + 'Kanboard\\Model\\LinkModel' => $baseDir . '/app/Model/LinkModel.php', + 'Kanboard\\Model\\MetadataModel' => $baseDir . '/app/Model/MetadataModel.php', + 'Kanboard\\Model\\NotificationModel' => $baseDir . '/app/Model/NotificationModel.php', + 'Kanboard\\Model\\NotificationTypeModel' => $baseDir . '/app/Model/NotificationTypeModel.php', + 'Kanboard\\Model\\PasswordResetModel' => $baseDir . '/app/Model/PasswordResetModel.php', + 'Kanboard\\Model\\ProjectActivityModel' => $baseDir . '/app/Model/ProjectActivityModel.php', + 'Kanboard\\Model\\ProjectDailyColumnStatsModel' => $baseDir . '/app/Model/ProjectDailyColumnStatsModel.php', + 'Kanboard\\Model\\ProjectDailyStatsModel' => $baseDir . '/app/Model/ProjectDailyStatsModel.php', + 'Kanboard\\Model\\ProjectDuplicationModel' => $baseDir . '/app/Model/ProjectDuplicationModel.php', + 'Kanboard\\Model\\ProjectFileModel' => $baseDir . '/app/Model/ProjectFileModel.php', + 'Kanboard\\Model\\ProjectGroupRoleModel' => $baseDir . '/app/Model/ProjectGroupRoleModel.php', + 'Kanboard\\Model\\ProjectMetadataModel' => $baseDir . '/app/Model/ProjectMetadataModel.php', + 'Kanboard\\Model\\ProjectModel' => $baseDir . '/app/Model/ProjectModel.php', + 'Kanboard\\Model\\ProjectNotificationModel' => $baseDir . '/app/Model/ProjectNotificationModel.php', + 'Kanboard\\Model\\ProjectNotificationTypeModel' => $baseDir . '/app/Model/ProjectNotificationTypeModel.php', + 'Kanboard\\Model\\ProjectPermissionModel' => $baseDir . '/app/Model/ProjectPermissionModel.php', + 'Kanboard\\Model\\ProjectRoleModel' => $baseDir . '/app/Model/ProjectRoleModel.php', + 'Kanboard\\Model\\ProjectRoleRestrictionModel' => $baseDir . '/app/Model/ProjectRoleRestrictionModel.php', + 'Kanboard\\Model\\ProjectTaskDuplicationModel' => $baseDir . '/app/Model/ProjectTaskDuplicationModel.php', + 'Kanboard\\Model\\ProjectTaskPriorityModel' => $baseDir . '/app/Model/ProjectTaskPriorityModel.php', + 'Kanboard\\Model\\ProjectUserRoleModel' => $baseDir . '/app/Model/ProjectUserRoleModel.php', + 'Kanboard\\Model\\RememberMeSessionModel' => $baseDir . '/app/Model/RememberMeSessionModel.php', + 'Kanboard\\Model\\SettingModel' => $baseDir . '/app/Model/SettingModel.php', + 'Kanboard\\Model\\SubtaskModel' => $baseDir . '/app/Model/SubtaskModel.php', + 'Kanboard\\Model\\SubtaskPositionModel' => $baseDir . '/app/Model/SubtaskPositionModel.php', + 'Kanboard\\Model\\SubtaskStatusModel' => $baseDir . '/app/Model/SubtaskStatusModel.php', + 'Kanboard\\Model\\SubtaskTaskConversionModel' => $baseDir . '/app/Model/SubtaskTaskConversionModel.php', + 'Kanboard\\Model\\SubtaskTimeTrackingModel' => $baseDir . '/app/Model/SubtaskTimeTrackingModel.php', + 'Kanboard\\Model\\SwimlaneModel' => $baseDir . '/app/Model/SwimlaneModel.php', + 'Kanboard\\Model\\TagDuplicationModel' => $baseDir . '/app/Model/TagDuplicationModel.php', + 'Kanboard\\Model\\TagModel' => $baseDir . '/app/Model/TagModel.php', + 'Kanboard\\Model\\TaskAnalyticModel' => $baseDir . '/app/Model/TaskAnalyticModel.php', + 'Kanboard\\Model\\TaskCreationModel' => $baseDir . '/app/Model/TaskCreationModel.php', + 'Kanboard\\Model\\TaskDuplicationModel' => $baseDir . '/app/Model/TaskDuplicationModel.php', + 'Kanboard\\Model\\TaskExternalLinkModel' => $baseDir . '/app/Model/TaskExternalLinkModel.php', + 'Kanboard\\Model\\TaskFileModel' => $baseDir . '/app/Model/TaskFileModel.php', + 'Kanboard\\Model\\TaskFinderModel' => $baseDir . '/app/Model/TaskFinderModel.php', + 'Kanboard\\Model\\TaskLinkModel' => $baseDir . '/app/Model/TaskLinkModel.php', + 'Kanboard\\Model\\TaskMetadataModel' => $baseDir . '/app/Model/TaskMetadataModel.php', + 'Kanboard\\Model\\TaskModel' => $baseDir . '/app/Model/TaskModel.php', + 'Kanboard\\Model\\TaskModificationModel' => $baseDir . '/app/Model/TaskModificationModel.php', + 'Kanboard\\Model\\TaskPositionModel' => $baseDir . '/app/Model/TaskPositionModel.php', + 'Kanboard\\Model\\TaskProjectDuplicationModel' => $baseDir . '/app/Model/TaskProjectDuplicationModel.php', + 'Kanboard\\Model\\TaskProjectMoveModel' => $baseDir . '/app/Model/TaskProjectMoveModel.php', + 'Kanboard\\Model\\TaskRecurrenceModel' => $baseDir . '/app/Model/TaskRecurrenceModel.php', + 'Kanboard\\Model\\TaskStatusModel' => $baseDir . '/app/Model/TaskStatusModel.php', + 'Kanboard\\Model\\TaskTagModel' => $baseDir . '/app/Model/TaskTagModel.php', + 'Kanboard\\Model\\TimezoneModel' => $baseDir . '/app/Model/TimezoneModel.php', + 'Kanboard\\Model\\TransitionModel' => $baseDir . '/app/Model/TransitionModel.php', + 'Kanboard\\Model\\UserLockingModel' => $baseDir . '/app/Model/UserLockingModel.php', + 'Kanboard\\Model\\UserMetadataModel' => $baseDir . '/app/Model/UserMetadataModel.php', + 'Kanboard\\Model\\UserModel' => $baseDir . '/app/Model/UserModel.php', + 'Kanboard\\Model\\UserNotificationFilterModel' => $baseDir . '/app/Model/UserNotificationFilterModel.php', + 'Kanboard\\Model\\UserNotificationModel' => $baseDir . '/app/Model/UserNotificationModel.php', + 'Kanboard\\Model\\UserNotificationTypeModel' => $baseDir . '/app/Model/UserNotificationTypeModel.php', + 'Kanboard\\Model\\UserUnreadNotificationModel' => $baseDir . '/app/Model/UserUnreadNotificationModel.php', + 'Kanboard\\Notification\\ActivityStreamNotification' => $baseDir . '/app/Notification/ActivityStreamNotification.php', + 'Kanboard\\Notification\\MailNotification' => $baseDir . '/app/Notification/MailNotification.php', + 'Kanboard\\Notification\\WebNotification' => $baseDir . '/app/Notification/WebNotification.php', + 'Kanboard\\Notification\\WebhookNotification' => $baseDir . '/app/Notification/WebhookNotification.php', + 'Kanboard\\Pagination\\DashboardPagination' => $baseDir . '/app/Pagination/DashboardPagination.php', + 'Kanboard\\Pagination\\ProjectPagination' => $baseDir . '/app/Pagination/ProjectPagination.php', + 'Kanboard\\Pagination\\SubtaskPagination' => $baseDir . '/app/Pagination/SubtaskPagination.php', + 'Kanboard\\Pagination\\TaskPagination' => $baseDir . '/app/Pagination/TaskPagination.php', + 'Kanboard\\Pagination\\UserPagination' => $baseDir . '/app/Pagination/UserPagination.php', + 'Kanboard\\ServiceProvider\\ActionProvider' => $baseDir . '/app/ServiceProvider/ActionProvider.php', + 'Kanboard\\ServiceProvider\\ApiProvider' => $baseDir . '/app/ServiceProvider/ApiProvider.php', + 'Kanboard\\ServiceProvider\\AuthenticationProvider' => $baseDir . '/app/ServiceProvider/AuthenticationProvider.php', + 'Kanboard\\ServiceProvider\\AvatarProvider' => $baseDir . '/app/ServiceProvider/AvatarProvider.php', + 'Kanboard\\ServiceProvider\\CacheProvider' => $baseDir . '/app/ServiceProvider/CacheProvider.php', + 'Kanboard\\ServiceProvider\\ClassProvider' => $baseDir . '/app/ServiceProvider/ClassProvider.php', + 'Kanboard\\ServiceProvider\\CommandProvider' => $baseDir . '/app/ServiceProvider/CommandProvider.php', + 'Kanboard\\ServiceProvider\\DatabaseProvider' => $baseDir . '/app/ServiceProvider/DatabaseProvider.php', + 'Kanboard\\ServiceProvider\\EventDispatcherProvider' => $baseDir . '/app/ServiceProvider/EventDispatcherProvider.php', + 'Kanboard\\ServiceProvider\\ExternalLinkProvider' => $baseDir . '/app/ServiceProvider/ExternalLinkProvider.php', + 'Kanboard\\ServiceProvider\\ExternalTaskProvider' => $baseDir . '/app/ServiceProvider/ExternalTaskProvider.php', + 'Kanboard\\ServiceProvider\\FilterProvider' => $baseDir . '/app/ServiceProvider/FilterProvider.php', + 'Kanboard\\ServiceProvider\\FormatterProvider' => $baseDir . '/app/ServiceProvider/FormatterProvider.php', + 'Kanboard\\ServiceProvider\\GroupProvider' => $baseDir . '/app/ServiceProvider/GroupProvider.php', + 'Kanboard\\ServiceProvider\\HelperProvider' => $baseDir . '/app/ServiceProvider/HelperProvider.php', + 'Kanboard\\ServiceProvider\\JobProvider' => $baseDir . '/app/ServiceProvider/JobProvider.php', + 'Kanboard\\ServiceProvider\\LoggingProvider' => $baseDir . '/app/ServiceProvider/LoggingProvider.php', + 'Kanboard\\ServiceProvider\\MailProvider' => $baseDir . '/app/ServiceProvider/MailProvider.php', + 'Kanboard\\ServiceProvider\\NotificationProvider' => $baseDir . '/app/ServiceProvider/NotificationProvider.php', + 'Kanboard\\ServiceProvider\\ObjectStorageProvider' => $baseDir . '/app/ServiceProvider/ObjectStorageProvider.php', + 'Kanboard\\ServiceProvider\\PluginProvider' => $baseDir . '/app/ServiceProvider/PluginProvider.php', + 'Kanboard\\ServiceProvider\\QueueProvider' => $baseDir . '/app/ServiceProvider/QueueProvider.php', + 'Kanboard\\ServiceProvider\\RouteProvider' => $baseDir . '/app/ServiceProvider/RouteProvider.php', + 'Kanboard\\ServiceProvider\\SessionProvider' => $baseDir . '/app/ServiceProvider/SessionProvider.php', + 'Kanboard\\Subscriber\\AuthSubscriber' => $baseDir . '/app/Subscriber/AuthSubscriber.php', + 'Kanboard\\Subscriber\\BaseSubscriber' => $baseDir . '/app/Subscriber/BaseSubscriber.php', + 'Kanboard\\Subscriber\\BootstrapSubscriber' => $baseDir . '/app/Subscriber/BootstrapSubscriber.php', + 'Kanboard\\Subscriber\\LdapUserPhotoSubscriber' => $baseDir . '/app/Subscriber/LdapUserPhotoSubscriber.php', + 'Kanboard\\Subscriber\\NotificationSubscriber' => $baseDir . '/app/Subscriber/NotificationSubscriber.php', + 'Kanboard\\Subscriber\\ProjectDailySummarySubscriber' => $baseDir . '/app/Subscriber/ProjectDailySummarySubscriber.php', + 'Kanboard\\Subscriber\\ProjectModificationDateSubscriber' => $baseDir . '/app/Subscriber/ProjectModificationDateSubscriber.php', + 'Kanboard\\Subscriber\\RecurringTaskSubscriber' => $baseDir . '/app/Subscriber/RecurringTaskSubscriber.php', + 'Kanboard\\Subscriber\\TransitionSubscriber' => $baseDir . '/app/Subscriber/TransitionSubscriber.php', + 'Kanboard\\User\\Avatar\\AvatarFileProvider' => $baseDir . '/app/User/Avatar/AvatarFileProvider.php', + 'Kanboard\\User\\Avatar\\LetterAvatarProvider' => $baseDir . '/app/User/Avatar/LetterAvatarProvider.php', + 'Kanboard\\User\\DatabaseUserProvider' => $baseDir . '/app/User/DatabaseUserProvider.php', + 'Kanboard\\User\\LdapUserProvider' => $baseDir . '/app/User/LdapUserProvider.php', + 'Kanboard\\User\\OAuthUserProvider' => $baseDir . '/app/User/OAuthUserProvider.php', + 'Kanboard\\User\\ReverseProxyUserProvider' => $baseDir . '/app/User/ReverseProxyUserProvider.php', + 'Kanboard\\Validator\\ActionValidator' => $baseDir . '/app/Validator/ActionValidator.php', + 'Kanboard\\Validator\\AuthValidator' => $baseDir . '/app/Validator/AuthValidator.php', + 'Kanboard\\Validator\\BaseValidator' => $baseDir . '/app/Validator/BaseValidator.php', + 'Kanboard\\Validator\\CategoryValidator' => $baseDir . '/app/Validator/CategoryValidator.php', + 'Kanboard\\Validator\\ColumnMoveRestrictionValidator' => $baseDir . '/app/Validator/ColumnMoveRestrictionValidator.php', + 'Kanboard\\Validator\\ColumnRestrictionValidator' => $baseDir . '/app/Validator/ColumnRestrictionValidator.php', + 'Kanboard\\Validator\\ColumnValidator' => $baseDir . '/app/Validator/ColumnValidator.php', + 'Kanboard\\Validator\\CommentValidator' => $baseDir . '/app/Validator/CommentValidator.php', + 'Kanboard\\Validator\\CurrencyValidator' => $baseDir . '/app/Validator/CurrencyValidator.php', + 'Kanboard\\Validator\\CustomFilterValidator' => $baseDir . '/app/Validator/CustomFilterValidator.php', + 'Kanboard\\Validator\\ExternalLinkValidator' => $baseDir . '/app/Validator/ExternalLinkValidator.php', + 'Kanboard\\Validator\\GroupValidator' => $baseDir . '/app/Validator/GroupValidator.php', + 'Kanboard\\Validator\\LinkValidator' => $baseDir . '/app/Validator/LinkValidator.php', + 'Kanboard\\Validator\\PasswordResetValidator' => $baseDir . '/app/Validator/PasswordResetValidator.php', + 'Kanboard\\Validator\\ProjectRoleValidator' => $baseDir . '/app/Validator/ProjectRoleValidator.php', + 'Kanboard\\Validator\\ProjectValidator' => $baseDir . '/app/Validator/ProjectValidator.php', + 'Kanboard\\Validator\\SubtaskValidator' => $baseDir . '/app/Validator/SubtaskValidator.php', + 'Kanboard\\Validator\\SwimlaneValidator' => $baseDir . '/app/Validator/SwimlaneValidator.php', + 'Kanboard\\Validator\\TagValidator' => $baseDir . '/app/Validator/TagValidator.php', + 'Kanboard\\Validator\\TaskLinkValidator' => $baseDir . '/app/Validator/TaskLinkValidator.php', + 'Kanboard\\Validator\\TaskValidator' => $baseDir . '/app/Validator/TaskValidator.php', + 'Kanboard\\Validator\\UserValidator' => $baseDir . '/app/Validator/UserValidator.php', + 'Otp\\GoogleAuthenticator' => $vendorDir . '/christian-riesen/otp/src/Otp/GoogleAuthenticator.php', + 'Otp\\Otp' => $vendorDir . '/christian-riesen/otp/src/Otp/Otp.php', + 'Otp\\OtpInterface' => $vendorDir . '/christian-riesen/otp/src/Otp/OtpInterface.php', + 'PHPQRCode' => $vendorDir . '/aferrandini/phpqrcode/lib/PHPQRCode.php', + 'PHPQRCode\\Autoloader' => $vendorDir . '/aferrandini/phpqrcode/lib/PHPQRCode/Autoloader.php', + 'PHPQRCode\\Constants' => $vendorDir . '/aferrandini/phpqrcode/lib/PHPQRCode/Constants.php', + 'PHPQRCode\\FrameFiller' => $vendorDir . '/aferrandini/phpqrcode/lib/PHPQRCode/FrameFiller.php', + 'PHPQRCode\\QRbitstream' => $vendorDir . '/aferrandini/phpqrcode/lib/PHPQRCode/QRbitstream.php', + 'PHPQRCode\\QRcode' => $vendorDir . '/aferrandini/phpqrcode/lib/PHPQRCode/QRcode.php', + 'PHPQRCode\\QRencode' => $vendorDir . '/aferrandini/phpqrcode/lib/PHPQRCode/QRencode.php', + 'PHPQRCode\\QRimage' => $vendorDir . '/aferrandini/phpqrcode/lib/PHPQRCode/QRimage.php', + 'PHPQRCode\\QRinput' => $vendorDir . '/aferrandini/phpqrcode/lib/PHPQRCode/QRinput.php', + 'PHPQRCode\\QRinputItem' => $vendorDir . '/aferrandini/phpqrcode/lib/PHPQRCode/QRinputItem.php', + 'PHPQRCode\\QRmask' => $vendorDir . '/aferrandini/phpqrcode/lib/PHPQRCode/QRmask.php', + 'PHPQRCode\\QRrawcode' => $vendorDir . '/aferrandini/phpqrcode/lib/PHPQRCode/QRrawcode.php', + 'PHPQRCode\\QRrs' => $vendorDir . '/aferrandini/phpqrcode/lib/PHPQRCode/QRrs.php', + 'PHPQRCode\\QRrsItem' => $vendorDir . '/aferrandini/phpqrcode/lib/PHPQRCode/QRrsItem.php', + 'PHPQRCode\\QRrsblock' => $vendorDir . '/aferrandini/phpqrcode/lib/PHPQRCode/QRrsblock.php', + 'PHPQRCode\\QRspec' => $vendorDir . '/aferrandini/phpqrcode/lib/PHPQRCode/QRspec.php', + 'PHPQRCode\\QRsplit' => $vendorDir . '/aferrandini/phpqrcode/lib/PHPQRCode/QRsplit.php', + 'PHPQRCode\\QRstr' => $vendorDir . '/aferrandini/phpqrcode/lib/PHPQRCode/QRstr.php', + 'PHPQRCode\\QRtools' => $vendorDir . '/aferrandini/phpqrcode/lib/PHPQRCode/QRtools.php', + 'Parsedown' => $vendorDir . '/erusev/parsedown/Parsedown.php', + 'ParsedownTest' => $vendorDir . '/erusev/parsedown/test/ParsedownTest.php', + 'PicoDb\\Builder\\BaseBuilder' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Builder/BaseBuilder.php', + 'PicoDb\\Builder\\ConditionBuilder' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Builder/ConditionBuilder.php', + 'PicoDb\\Builder\\InsertBuilder' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Builder/InsertBuilder.php', + 'PicoDb\\Builder\\OrConditionBuilder' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Builder/OrConditionBuilder.php', + 'PicoDb\\Builder\\UpdateBuilder' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Builder/UpdateBuilder.php', + 'PicoDb\\Database' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Database.php', + 'PicoDb\\DriverFactory' => $vendorDir . '/fguillot/picodb/lib/PicoDb/DriverFactory.php', + 'PicoDb\\Driver\\Base' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Driver/Base.php', + 'PicoDb\\Driver\\Mssql' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Driver/Mssql.php', + 'PicoDb\\Driver\\Mysql' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Driver/Mysql.php', + 'PicoDb\\Driver\\Postgres' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Driver/Postgres.php', + 'PicoDb\\Driver\\Sqlite' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Driver/Sqlite.php', + 'PicoDb\\Hashtable' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Hashtable.php', + 'PicoDb\\LargeObject' => $vendorDir . '/fguillot/picodb/lib/PicoDb/LargeObject.php', + 'PicoDb\\SQLException' => $vendorDir . '/fguillot/picodb/lib/PicoDb/SQLException.php', + 'PicoDb\\Schema' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Schema.php', + 'PicoDb\\StatementHandler' => $vendorDir . '/fguillot/picodb/lib/PicoDb/StatementHandler.php', + 'PicoDb\\Table' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Table.php', + 'PicoDb\\UrlParser' => $vendorDir . '/fguillot/picodb/lib/PicoDb/UrlParser.php', + 'PicoFeed\\Base' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Base.php', + 'PicoFeed\\Client\\Client' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Client/Client.php', + 'PicoFeed\\Client\\ClientException' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Client/ClientException.php', + 'PicoFeed\\Client\\Curl' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Client/Curl.php', + 'PicoFeed\\Client\\ForbiddenException' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Client/ForbiddenException.php', + 'PicoFeed\\Client\\HttpHeaders' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Client/HttpHeaders.php', + 'PicoFeed\\Client\\InvalidCertificateException' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Client/InvalidCertificateException.php', + 'PicoFeed\\Client\\InvalidUrlException' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Client/InvalidUrlException.php', + 'PicoFeed\\Client\\MaxRedirectException' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Client/MaxRedirectException.php', + 'PicoFeed\\Client\\MaxSizeException' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Client/MaxSizeException.php', + 'PicoFeed\\Client\\Stream' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Client/Stream.php', + 'PicoFeed\\Client\\TimeoutException' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Client/TimeoutException.php', + 'PicoFeed\\Client\\UnauthorizedException' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Client/UnauthorizedException.php', + 'PicoFeed\\Client\\Url' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Client/Url.php', + 'PicoFeed\\Config\\Config' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Config/Config.php', + 'PicoFeed\\Encoding\\Encoding' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Encoding/Encoding.php', + 'PicoFeed\\Filter\\Attribute' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Filter/Attribute.php', + 'PicoFeed\\Filter\\Filter' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Filter/Filter.php', + 'PicoFeed\\Filter\\Html' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Filter/Html.php', + 'PicoFeed\\Filter\\Tag' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Filter/Tag.php', + 'PicoFeed\\Generator\\ContentGeneratorInterface' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Generator/ContentGeneratorInterface.php', + 'PicoFeed\\Generator\\FileContentGenerator' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Generator/FileContentGenerator.php', + 'PicoFeed\\Generator\\YoutubeContentGenerator' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Generator/YoutubeContentGenerator.php', + 'PicoFeed\\Logging\\Logger' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Logging/Logger.php', + 'PicoFeed\\Parser\\Atom' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Parser/Atom.php', + 'PicoFeed\\Parser\\DateParser' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Parser/DateParser.php', + 'PicoFeed\\Parser\\Feed' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Parser/Feed.php', + 'PicoFeed\\Parser\\Item' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Parser/Item.php', + 'PicoFeed\\Parser\\MalformedXmlException' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Parser/MalformedXmlException.php', + 'PicoFeed\\Parser\\Parser' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Parser/Parser.php', + 'PicoFeed\\Parser\\ParserException' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Parser/ParserException.php', + 'PicoFeed\\Parser\\ParserInterface' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Parser/ParserInterface.php', + 'PicoFeed\\Parser\\Rss10' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Parser/Rss10.php', + 'PicoFeed\\Parser\\Rss20' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Parser/Rss20.php', + 'PicoFeed\\Parser\\Rss91' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Parser/Rss91.php', + 'PicoFeed\\Parser\\Rss92' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Parser/Rss92.php', + 'PicoFeed\\Parser\\XmlEntityException' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Parser/XmlEntityException.php', + 'PicoFeed\\Parser\\XmlParser' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Parser/XmlParser.php', + 'PicoFeed\\PicoFeedException' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/PicoFeedException.php', + 'PicoFeed\\Processor\\ContentFilterProcessor' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Processor/ContentFilterProcessor.php', + 'PicoFeed\\Processor\\ContentGeneratorProcessor' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Processor/ContentGeneratorProcessor.php', + 'PicoFeed\\Processor\\ItemPostProcessor' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Processor/ItemPostProcessor.php', + 'PicoFeed\\Processor\\ItemProcessorInterface' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Processor/ItemProcessorInterface.php', + 'PicoFeed\\Processor\\ScraperProcessor' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Processor/ScraperProcessor.php', + 'PicoFeed\\Reader\\Favicon' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Reader/Favicon.php', + 'PicoFeed\\Reader\\Reader' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Reader/Reader.php', + 'PicoFeed\\Reader\\ReaderException' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Reader/ReaderException.php', + 'PicoFeed\\Reader\\SubscriptionNotFoundException' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Reader/SubscriptionNotFoundException.php', + 'PicoFeed\\Reader\\UnsupportedFeedFormatException' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Reader/UnsupportedFeedFormatException.php', + 'PicoFeed\\Scraper\\CandidateParser' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Scraper/CandidateParser.php', + 'PicoFeed\\Scraper\\ParserInterface' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Scraper/ParserInterface.php', + 'PicoFeed\\Scraper\\RuleLoader' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Scraper/RuleLoader.php', + 'PicoFeed\\Scraper\\RuleParser' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Scraper/RuleParser.php', + 'PicoFeed\\Scraper\\Scraper' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Scraper/Scraper.php', + 'PicoFeed\\Serialization\\Subscription' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Serialization/Subscription.php', + 'PicoFeed\\Serialization\\SubscriptionList' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionList.php', + 'PicoFeed\\Serialization\\SubscriptionListBuilder' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionListBuilder.php', + 'PicoFeed\\Serialization\\SubscriptionListParser' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionListParser.php', + 'PicoFeed\\Serialization\\SubscriptionParser' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionParser.php', + 'PicoFeed\\Syndication\\AtomFeedBuilder' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Syndication/AtomFeedBuilder.php', + 'PicoFeed\\Syndication\\AtomHelper' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Syndication/AtomHelper.php', + 'PicoFeed\\Syndication\\AtomItemBuilder' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Syndication/AtomItemBuilder.php', + 'PicoFeed\\Syndication\\FeedBuilder' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Syndication/FeedBuilder.php', + 'PicoFeed\\Syndication\\ItemBuilder' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Syndication/ItemBuilder.php', + 'PicoFeed\\Syndication\\Rss20FeedBuilder' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20FeedBuilder.php', + 'PicoFeed\\Syndication\\Rss20Helper' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20Helper.php', + 'PicoFeed\\Syndication\\Rss20ItemBuilder' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20ItemBuilder.php', + 'Pimple\\Container' => $vendorDir . '/pimple/pimple/src/Pimple/Container.php', + 'Pimple\\ServiceProviderInterface' => $vendorDir . '/pimple/pimple/src/Pimple/ServiceProviderInterface.php', + 'Pimple\\Tests\\Fixtures\\Invokable' => $vendorDir . '/pimple/pimple/src/Pimple/Tests/Fixtures/Invokable.php', + 'Pimple\\Tests\\Fixtures\\NonInvokable' => $vendorDir . '/pimple/pimple/src/Pimple/Tests/Fixtures/NonInvokable.php', + 'Pimple\\Tests\\Fixtures\\PimpleServiceProvider' => $vendorDir . '/pimple/pimple/src/Pimple/Tests/Fixtures/PimpleServiceProvider.php', + 'Pimple\\Tests\\Fixtures\\Service' => $vendorDir . '/pimple/pimple/src/Pimple/Tests/Fixtures/Service.php', + 'Pimple\\Tests\\PimpleServiceProviderInterfaceTest' => $vendorDir . '/pimple/pimple/src/Pimple/Tests/PimpleServiceProviderInterfaceTest.php', + 'Pimple\\Tests\\PimpleTest' => $vendorDir . '/pimple/pimple/src/Pimple/Tests/PimpleTest.php', + 'Psr\\Log\\AbstractLogger' => $vendorDir . '/psr/log/Psr/Log/AbstractLogger.php', + 'Psr\\Log\\InvalidArgumentException' => $vendorDir . '/psr/log/Psr/Log/InvalidArgumentException.php', + 'Psr\\Log\\LogLevel' => $vendorDir . '/psr/log/Psr/Log/LogLevel.php', + 'Psr\\Log\\LoggerAwareInterface' => $vendorDir . '/psr/log/Psr/Log/LoggerAwareInterface.php', + 'Psr\\Log\\LoggerAwareTrait' => $vendorDir . '/psr/log/Psr/Log/LoggerAwareTrait.php', + 'Psr\\Log\\LoggerInterface' => $vendorDir . '/psr/log/Psr/Log/LoggerInterface.php', + 'Psr\\Log\\LoggerTrait' => $vendorDir . '/psr/log/Psr/Log/LoggerTrait.php', + 'Psr\\Log\\NullLogger' => $vendorDir . '/psr/log/Psr/Log/NullLogger.php', + 'Psr\\Log\\Test\\DummyTest' => $vendorDir . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php', + 'Psr\\Log\\Test\\LoggerInterfaceTest' => $vendorDir . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php', + 'SimpleLogger\\Base' => $vendorDir . '/fguillot/simpleLogger/src/SimpleLogger/Base.php', + 'SimpleLogger\\File' => $vendorDir . '/fguillot/simpleLogger/src/SimpleLogger/File.php', + 'SimpleLogger\\Logger' => $vendorDir . '/fguillot/simpleLogger/src/SimpleLogger/Logger.php', + 'SimpleLogger\\Stderr' => $vendorDir . '/fguillot/simpleLogger/src/SimpleLogger/Stderr.php', + 'SimpleLogger\\Stdout' => $vendorDir . '/fguillot/simpleLogger/src/SimpleLogger/Stdout.php', + 'SimpleLogger\\Syslog' => $vendorDir . '/fguillot/simpleLogger/src/SimpleLogger/Syslog.php', + 'SimpleQueue\\Adapter\\AmqpQueueAdapter' => $vendorDir . '/fguillot/simple-queue/src/Adapter/AmqpQueueAdapter.php', + 'SimpleQueue\\Adapter\\AwsSqsQueueAdapter' => $vendorDir . '/fguillot/simple-queue/src/Adapter/AwsSqsQueueAdapter.php', + 'SimpleQueue\\Adapter\\BeanstalkQueueAdapter' => $vendorDir . '/fguillot/simple-queue/src/Adapter/BeanstalkQueueAdapter.php', + 'SimpleQueue\\Adapter\\DisqueQueueAdapter' => $vendorDir . '/fguillot/simple-queue/src/Adapter/DisqueQueueAdapter.php', + 'SimpleQueue\\Adapter\\MemoryQueueAdapter' => $vendorDir . '/fguillot/simple-queue/src/Adapter/MemoryQueueAdapter.php', + 'SimpleQueue\\Exception\\NotSupportedException' => $vendorDir . '/fguillot/simple-queue/src/Exception/NotSupportedException.php', + 'SimpleQueue\\Job' => $vendorDir . '/fguillot/simple-queue/src/Job.php', + 'SimpleQueue\\Queue' => $vendorDir . '/fguillot/simple-queue/src/Queue.php', + 'SimpleQueue\\QueueAdapterInterface' => $vendorDir . '/fguillot/simple-queue/src/QueueAdapterInterface.php', + 'SimpleValidator\\Validator' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validator.php', + 'SimpleValidator\\Validators\\Alpha' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Alpha.php', + 'SimpleValidator\\Validators\\AlphaNumeric' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/AlphaNumeric.php', + 'SimpleValidator\\Validators\\Base' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Base.php', + 'SimpleValidator\\Validators\\Date' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Date.php', + 'SimpleValidator\\Validators\\Email' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Email.php', + 'SimpleValidator\\Validators\\Equals' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Equals.php', + 'SimpleValidator\\Validators\\Exists' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Exists.php', + 'SimpleValidator\\Validators\\GreaterThan' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/GreaterThan.php', + 'SimpleValidator\\Validators\\InArray' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/InArray.php', + 'SimpleValidator\\Validators\\Integer' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Integer.php', + 'SimpleValidator\\Validators\\Ip' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Ip.php', + 'SimpleValidator\\Validators\\Length' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Length.php', + 'SimpleValidator\\Validators\\MaxLength' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/MaxLength.php', + 'SimpleValidator\\Validators\\MinLength' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/MinLength.php', + 'SimpleValidator\\Validators\\NotEmpty' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/NotEmpty.php', + 'SimpleValidator\\Validators\\NotEquals' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/NotEquals.php', + 'SimpleValidator\\Validators\\NotInArray' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/NotInArray.php', + 'SimpleValidator\\Validators\\Numeric' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Numeric.php', + 'SimpleValidator\\Validators\\Range' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Range.php', + 'SimpleValidator\\Validators\\Required' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Required.php', + 'SimpleValidator\\Validators\\Unique' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Unique.php', + 'Symfony\\Component\\Console\\Application' => $vendorDir . '/symfony/console/Application.php', + 'Symfony\\Component\\Console\\Command\\Command' => $vendorDir . '/symfony/console/Command/Command.php', + 'Symfony\\Component\\Console\\Command\\HelpCommand' => $vendorDir . '/symfony/console/Command/HelpCommand.php', + 'Symfony\\Component\\Console\\Command\\ListCommand' => $vendorDir . '/symfony/console/Command/ListCommand.php', + 'Symfony\\Component\\Console\\ConsoleEvents' => $vendorDir . '/symfony/console/ConsoleEvents.php', + 'Symfony\\Component\\Console\\Descriptor\\ApplicationDescription' => $vendorDir . '/symfony/console/Descriptor/ApplicationDescription.php', + 'Symfony\\Component\\Console\\Descriptor\\Descriptor' => $vendorDir . '/symfony/console/Descriptor/Descriptor.php', + 'Symfony\\Component\\Console\\Descriptor\\DescriptorInterface' => $vendorDir . '/symfony/console/Descriptor/DescriptorInterface.php', + 'Symfony\\Component\\Console\\Descriptor\\JsonDescriptor' => $vendorDir . '/symfony/console/Descriptor/JsonDescriptor.php', + 'Symfony\\Component\\Console\\Descriptor\\MarkdownDescriptor' => $vendorDir . '/symfony/console/Descriptor/MarkdownDescriptor.php', + 'Symfony\\Component\\Console\\Descriptor\\TextDescriptor' => $vendorDir . '/symfony/console/Descriptor/TextDescriptor.php', + 'Symfony\\Component\\Console\\Descriptor\\XmlDescriptor' => $vendorDir . '/symfony/console/Descriptor/XmlDescriptor.php', + 'Symfony\\Component\\Console\\Event\\ConsoleCommandEvent' => $vendorDir . '/symfony/console/Event/ConsoleCommandEvent.php', + 'Symfony\\Component\\Console\\Event\\ConsoleEvent' => $vendorDir . '/symfony/console/Event/ConsoleEvent.php', + 'Symfony\\Component\\Console\\Event\\ConsoleExceptionEvent' => $vendorDir . '/symfony/console/Event/ConsoleExceptionEvent.php', + 'Symfony\\Component\\Console\\Event\\ConsoleTerminateEvent' => $vendorDir . '/symfony/console/Event/ConsoleTerminateEvent.php', + 'Symfony\\Component\\Console\\Exception\\CommandNotFoundException' => $vendorDir . '/symfony/console/Exception/CommandNotFoundException.php', + 'Symfony\\Component\\Console\\Exception\\ExceptionInterface' => $vendorDir . '/symfony/console/Exception/ExceptionInterface.php', + 'Symfony\\Component\\Console\\Exception\\InvalidArgumentException' => $vendorDir . '/symfony/console/Exception/InvalidArgumentException.php', + 'Symfony\\Component\\Console\\Exception\\InvalidOptionException' => $vendorDir . '/symfony/console/Exception/InvalidOptionException.php', + 'Symfony\\Component\\Console\\Exception\\LogicException' => $vendorDir . '/symfony/console/Exception/LogicException.php', + 'Symfony\\Component\\Console\\Exception\\RuntimeException' => $vendorDir . '/symfony/console/Exception/RuntimeException.php', + 'Symfony\\Component\\Console\\Formatter\\OutputFormatter' => $vendorDir . '/symfony/console/Formatter/OutputFormatter.php', + 'Symfony\\Component\\Console\\Formatter\\OutputFormatterInterface' => $vendorDir . '/symfony/console/Formatter/OutputFormatterInterface.php', + 'Symfony\\Component\\Console\\Formatter\\OutputFormatterStyle' => $vendorDir . '/symfony/console/Formatter/OutputFormatterStyle.php', + 'Symfony\\Component\\Console\\Formatter\\OutputFormatterStyleInterface' => $vendorDir . '/symfony/console/Formatter/OutputFormatterStyleInterface.php', + 'Symfony\\Component\\Console\\Formatter\\OutputFormatterStyleStack' => $vendorDir . '/symfony/console/Formatter/OutputFormatterStyleStack.php', + 'Symfony\\Component\\Console\\Helper\\DebugFormatterHelper' => $vendorDir . '/symfony/console/Helper/DebugFormatterHelper.php', + 'Symfony\\Component\\Console\\Helper\\DescriptorHelper' => $vendorDir . '/symfony/console/Helper/DescriptorHelper.php', + 'Symfony\\Component\\Console\\Helper\\DialogHelper' => $vendorDir . '/symfony/console/Helper/DialogHelper.php', + 'Symfony\\Component\\Console\\Helper\\FormatterHelper' => $vendorDir . '/symfony/console/Helper/FormatterHelper.php', + 'Symfony\\Component\\Console\\Helper\\Helper' => $vendorDir . '/symfony/console/Helper/Helper.php', + 'Symfony\\Component\\Console\\Helper\\HelperInterface' => $vendorDir . '/symfony/console/Helper/HelperInterface.php', + 'Symfony\\Component\\Console\\Helper\\HelperSet' => $vendorDir . '/symfony/console/Helper/HelperSet.php', + 'Symfony\\Component\\Console\\Helper\\InputAwareHelper' => $vendorDir . '/symfony/console/Helper/InputAwareHelper.php', + 'Symfony\\Component\\Console\\Helper\\ProcessHelper' => $vendorDir . '/symfony/console/Helper/ProcessHelper.php', + 'Symfony\\Component\\Console\\Helper\\ProgressBar' => $vendorDir . '/symfony/console/Helper/ProgressBar.php', + 'Symfony\\Component\\Console\\Helper\\ProgressHelper' => $vendorDir . '/symfony/console/Helper/ProgressHelper.php', + 'Symfony\\Component\\Console\\Helper\\ProgressIndicator' => $vendorDir . '/symfony/console/Helper/ProgressIndicator.php', + 'Symfony\\Component\\Console\\Helper\\QuestionHelper' => $vendorDir . '/symfony/console/Helper/QuestionHelper.php', + 'Symfony\\Component\\Console\\Helper\\SymfonyQuestionHelper' => $vendorDir . '/symfony/console/Helper/SymfonyQuestionHelper.php', + 'Symfony\\Component\\Console\\Helper\\Table' => $vendorDir . '/symfony/console/Helper/Table.php', + 'Symfony\\Component\\Console\\Helper\\TableCell' => $vendorDir . '/symfony/console/Helper/TableCell.php', + 'Symfony\\Component\\Console\\Helper\\TableHelper' => $vendorDir . '/symfony/console/Helper/TableHelper.php', + 'Symfony\\Component\\Console\\Helper\\TableSeparator' => $vendorDir . '/symfony/console/Helper/TableSeparator.php', + 'Symfony\\Component\\Console\\Helper\\TableStyle' => $vendorDir . '/symfony/console/Helper/TableStyle.php', + 'Symfony\\Component\\Console\\Input\\ArgvInput' => $vendorDir . '/symfony/console/Input/ArgvInput.php', + 'Symfony\\Component\\Console\\Input\\ArrayInput' => $vendorDir . '/symfony/console/Input/ArrayInput.php', + 'Symfony\\Component\\Console\\Input\\Input' => $vendorDir . '/symfony/console/Input/Input.php', + 'Symfony\\Component\\Console\\Input\\InputArgument' => $vendorDir . '/symfony/console/Input/InputArgument.php', + 'Symfony\\Component\\Console\\Input\\InputAwareInterface' => $vendorDir . '/symfony/console/Input/InputAwareInterface.php', + 'Symfony\\Component\\Console\\Input\\InputDefinition' => $vendorDir . '/symfony/console/Input/InputDefinition.php', + 'Symfony\\Component\\Console\\Input\\InputInterface' => $vendorDir . '/symfony/console/Input/InputInterface.php', + 'Symfony\\Component\\Console\\Input\\InputOption' => $vendorDir . '/symfony/console/Input/InputOption.php', + 'Symfony\\Component\\Console\\Input\\StringInput' => $vendorDir . '/symfony/console/Input/StringInput.php', + 'Symfony\\Component\\Console\\Logger\\ConsoleLogger' => $vendorDir . '/symfony/console/Logger/ConsoleLogger.php', + 'Symfony\\Component\\Console\\Output\\BufferedOutput' => $vendorDir . '/symfony/console/Output/BufferedOutput.php', + 'Symfony\\Component\\Console\\Output\\ConsoleOutput' => $vendorDir . '/symfony/console/Output/ConsoleOutput.php', + 'Symfony\\Component\\Console\\Output\\ConsoleOutputInterface' => $vendorDir . '/symfony/console/Output/ConsoleOutputInterface.php', + 'Symfony\\Component\\Console\\Output\\NullOutput' => $vendorDir . '/symfony/console/Output/NullOutput.php', + 'Symfony\\Component\\Console\\Output\\Output' => $vendorDir . '/symfony/console/Output/Output.php', + 'Symfony\\Component\\Console\\Output\\OutputInterface' => $vendorDir . '/symfony/console/Output/OutputInterface.php', + 'Symfony\\Component\\Console\\Output\\StreamOutput' => $vendorDir . '/symfony/console/Output/StreamOutput.php', + 'Symfony\\Component\\Console\\Question\\ChoiceQuestion' => $vendorDir . '/symfony/console/Question/ChoiceQuestion.php', + 'Symfony\\Component\\Console\\Question\\ConfirmationQuestion' => $vendorDir . '/symfony/console/Question/ConfirmationQuestion.php', + 'Symfony\\Component\\Console\\Question\\Question' => $vendorDir . '/symfony/console/Question/Question.php', + 'Symfony\\Component\\Console\\Shell' => $vendorDir . '/symfony/console/Shell.php', + 'Symfony\\Component\\Console\\Style\\OutputStyle' => $vendorDir . '/symfony/console/Style/OutputStyle.php', + 'Symfony\\Component\\Console\\Style\\StyleInterface' => $vendorDir . '/symfony/console/Style/StyleInterface.php', + 'Symfony\\Component\\Console\\Style\\SymfonyStyle' => $vendorDir . '/symfony/console/Style/SymfonyStyle.php', + 'Symfony\\Component\\Console\\Tester\\ApplicationTester' => $vendorDir . '/symfony/console/Tester/ApplicationTester.php', + 'Symfony\\Component\\Console\\Tester\\CommandTester' => $vendorDir . '/symfony/console/Tester/CommandTester.php', + 'Symfony\\Component\\EventDispatcher\\ContainerAwareEventDispatcher' => $vendorDir . '/symfony/event-dispatcher/ContainerAwareEventDispatcher.php', + 'Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcher' => $vendorDir . '/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php', + 'Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcherInterface' => $vendorDir . '/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php', + 'Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener' => $vendorDir . '/symfony/event-dispatcher/Debug/WrappedListener.php', + 'Symfony\\Component\\EventDispatcher\\DependencyInjection\\RegisterListenersPass' => $vendorDir . '/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php', + 'Symfony\\Component\\EventDispatcher\\Event' => $vendorDir . '/symfony/event-dispatcher/Event.php', + 'Symfony\\Component\\EventDispatcher\\EventDispatcher' => $vendorDir . '/symfony/event-dispatcher/EventDispatcher.php', + 'Symfony\\Component\\EventDispatcher\\EventDispatcherInterface' => $vendorDir . '/symfony/event-dispatcher/EventDispatcherInterface.php', + 'Symfony\\Component\\EventDispatcher\\EventSubscriberInterface' => $vendorDir . '/symfony/event-dispatcher/EventSubscriberInterface.php', + 'Symfony\\Component\\EventDispatcher\\GenericEvent' => $vendorDir . '/symfony/event-dispatcher/GenericEvent.php', + 'Symfony\\Component\\EventDispatcher\\ImmutableEventDispatcher' => $vendorDir . '/symfony/event-dispatcher/ImmutableEventDispatcher.php', + 'Symfony\\Polyfill\\Mbstring\\Mbstring' => $vendorDir . '/symfony/polyfill-mbstring/Mbstring.php', + 'ZendXml\\Exception\\ExceptionInterface' => $vendorDir . '/zendframework/zendxml/library/ZendXml/Exception/ExceptionInterface.php', + 'ZendXml\\Exception\\InvalidArgumentException' => $vendorDir . '/zendframework/zendxml/library/ZendXml/Exception/InvalidArgumentException.php', + 'ZendXml\\Exception\\RuntimeException' => $vendorDir . '/zendframework/zendxml/library/ZendXml/Exception/RuntimeException.php', + 'ZendXml\\Security' => $vendorDir . '/zendframework/zendxml/library/ZendXml/Security.php', +); diff --git a/vendor/composer/autoload_files.php b/vendor/composer/autoload_files.php new file mode 100644 index 0000000000..e7fae22d1e --- /dev/null +++ b/vendor/composer/autoload_files.php @@ -0,0 +1,15 @@ + $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php', + '5255c38a0faeba867671b61dfda6d864' => $vendorDir . '/paragonie/random_compat/lib/random.php', + '8cd2fca4db21bffce1ad0612f7caeec4' => $vendorDir . '/ramsey/array_column/src/array_column.php', + '2c102faa651ef8ea5874edb585946bce' => $vendorDir . '/swiftmailer/swiftmailer/lib/swift_required.php', + 'f51af1d1e172536bcdb5baf6f649449d' => $baseDir . '/app/functions.php', + 'da6e17f7b0fa11d4819751ff2afd0bac' => $baseDir . '/app/Library/password.php', +); diff --git a/vendor/composer/autoload_namespaces.php b/vendor/composer/autoload_namespaces.php new file mode 100644 index 0000000000..c2ba047d03 --- /dev/null +++ b/vendor/composer/autoload_namespaces.php @@ -0,0 +1,20 @@ + array($vendorDir . '/zendframework/zendxml/library'), + 'SimpleValidator' => array($vendorDir . '/fguillot/simple-validator/src'), + 'SimpleLogger' => array($vendorDir . '/fguillot/simpleLogger/src'), + 'Pimple' => array($vendorDir . '/pimple/pimple/src'), + 'PicoFeed' => array($vendorDir . '/miniflux/picofeed/lib'), + 'PicoDb' => array($vendorDir . '/fguillot/picodb/lib'), + 'Parsedown' => array($vendorDir . '/erusev/parsedown'), + 'PHPQRCode' => array($vendorDir . '/aferrandini/phpqrcode/lib'), + 'Otp' => array($vendorDir . '/christian-riesen/otp/src'), + 'JsonRPC' => array($vendorDir . '/fguillot/json-rpc/src'), + 'Eluceo\\iCal' => array($vendorDir . '/eluceo/ical/src'), +); diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php new file mode 100644 index 0000000000..7367aa46ce --- /dev/null +++ b/vendor/composer/autoload_psr4.php @@ -0,0 +1,17 @@ + array($vendorDir . '/symfony/polyfill-mbstring'), + 'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'), + 'Symfony\\Component\\Console\\' => array($vendorDir . '/symfony/console'), + 'SimpleQueue\\' => array($vendorDir . '/fguillot/simple-queue/src'), + 'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'), + 'Kanboard\\' => array($baseDir . '/app'), + 'Gregwar\\Captcha\\' => array($vendorDir . '/gregwar/captcha'), + 'Base32\\' => array($vendorDir . '/christian-riesen/base32/src'), +); diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php new file mode 100644 index 0000000000..2f490fe863 --- /dev/null +++ b/vendor/composer/autoload_real.php @@ -0,0 +1,70 @@ += 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); + if ($useStaticLoader) { + require_once __DIR__ . '/autoload_static.php'; + + call_user_func(\Composer\Autoload\ComposerStaticInit6edea6294a88689e3f5c56484bb70c9b::getInitializer($loader)); + } else { + $map = require __DIR__ . '/autoload_namespaces.php'; + foreach ($map as $namespace => $path) { + $loader->set($namespace, $path); + } + + $map = require __DIR__ . '/autoload_psr4.php'; + foreach ($map as $namespace => $path) { + $loader->setPsr4($namespace, $path); + } + + $classMap = require __DIR__ . '/autoload_classmap.php'; + if ($classMap) { + $loader->addClassMap($classMap); + } + } + + $loader->register(true); + + if ($useStaticLoader) { + $includeFiles = Composer\Autoload\ComposerStaticInit6edea6294a88689e3f5c56484bb70c9b::$files; + } else { + $includeFiles = require __DIR__ . '/autoload_files.php'; + } + foreach ($includeFiles as $fileIdentifier => $file) { + composerRequire6edea6294a88689e3f5c56484bb70c9b($fileIdentifier, $file); + } + + return $loader; + } +} + +function composerRequire6edea6294a88689e3f5c56484bb70c9b($fileIdentifier, $file) +{ + if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { + require $file; + + $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; + } +} diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php new file mode 100644 index 0000000000..626fed60d4 --- /dev/null +++ b/vendor/composer/autoload_static.php @@ -0,0 +1,1049 @@ + __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php', + '5255c38a0faeba867671b61dfda6d864' => __DIR__ . '/..' . '/paragonie/random_compat/lib/random.php', + '8cd2fca4db21bffce1ad0612f7caeec4' => __DIR__ . '/..' . '/ramsey/array_column/src/array_column.php', + '2c102faa651ef8ea5874edb585946bce' => __DIR__ . '/..' . '/swiftmailer/swiftmailer/lib/swift_required.php', + 'f51af1d1e172536bcdb5baf6f649449d' => __DIR__ . '/../..' . '/app/functions.php', + 'da6e17f7b0fa11d4819751ff2afd0bac' => __DIR__ . '/../..' . '/app/Library/password.php', + ); + + public static $prefixLengthsPsr4 = array ( + 'S' => + array ( + 'Symfony\\Polyfill\\Mbstring\\' => 26, + 'Symfony\\Component\\EventDispatcher\\' => 34, + 'Symfony\\Component\\Console\\' => 26, + 'SimpleQueue\\' => 12, + ), + 'P' => + array ( + 'Psr\\Log\\' => 8, + ), + 'K' => + array ( + 'Kanboard\\' => 9, + ), + 'G' => + array ( + 'Gregwar\\Captcha\\' => 16, + ), + 'B' => + array ( + 'Base32\\' => 7, + ), + ); + + public static $prefixDirsPsr4 = array ( + 'Symfony\\Polyfill\\Mbstring\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring', + ), + 'Symfony\\Component\\EventDispatcher\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/event-dispatcher', + ), + 'Symfony\\Component\\Console\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/console', + ), + 'SimpleQueue\\' => + array ( + 0 => __DIR__ . '/..' . '/fguillot/simple-queue/src', + ), + 'Psr\\Log\\' => + array ( + 0 => __DIR__ . '/..' . '/psr/log/Psr/Log', + ), + 'Kanboard\\' => + array ( + 0 => __DIR__ . '/../..' . '/app', + ), + 'Gregwar\\Captcha\\' => + array ( + 0 => __DIR__ . '/..' . '/gregwar/captcha', + ), + 'Base32\\' => + array ( + 0 => __DIR__ . '/..' . '/christian-riesen/base32/src', + ), + ); + + public static $prefixesPsr0 = array ( + 'Z' => + array ( + 'ZendXml\\' => + array ( + 0 => __DIR__ . '/..' . '/zendframework/zendxml/library', + ), + ), + 'S' => + array ( + 'SimpleValidator' => + array ( + 0 => __DIR__ . '/..' . '/fguillot/simple-validator/src', + ), + 'SimpleLogger' => + array ( + 0 => __DIR__ . '/..' . '/fguillot/simpleLogger/src', + ), + ), + 'P' => + array ( + 'Pimple' => + array ( + 0 => __DIR__ . '/..' . '/pimple/pimple/src', + ), + 'PicoFeed' => + array ( + 0 => __DIR__ . '/..' . '/miniflux/picofeed/lib', + ), + 'PicoDb' => + array ( + 0 => __DIR__ . '/..' . '/fguillot/picodb/lib', + ), + 'Parsedown' => + array ( + 0 => __DIR__ . '/..' . '/erusev/parsedown', + ), + 'PHPQRCode' => + array ( + 0 => __DIR__ . '/..' . '/aferrandini/phpqrcode/lib', + ), + ), + 'O' => + array ( + 'Otp' => + array ( + 0 => __DIR__ . '/..' . '/christian-riesen/otp/src', + ), + ), + 'J' => + array ( + 'JsonRPC' => + array ( + 0 => __DIR__ . '/..' . '/fguillot/json-rpc/src', + ), + ), + 'E' => + array ( + 'Eluceo\\iCal' => + array ( + 0 => __DIR__ . '/..' . '/eluceo/ical/src', + ), + ), + ); + + public static $classMap = array ( + 'Base32\\Base32' => __DIR__ . '/..' . '/christian-riesen/base32/src/Base32.php', + 'Eluceo\\iCal\\Component' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/Component.php', + 'Eluceo\\iCal\\Component\\Alarm' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/Component/Alarm.php', + 'Eluceo\\iCal\\Component\\Calendar' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/Component/Calendar.php', + 'Eluceo\\iCal\\Component\\Event' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/Component/Event.php', + 'Eluceo\\iCal\\Component\\Timezone' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/Component/Timezone.php', + 'Eluceo\\iCal\\Component\\TimezoneRule' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/Component/TimezoneRule.php', + 'Eluceo\\iCal\\ParameterBag' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/ParameterBag.php', + 'Eluceo\\iCal\\Property' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/Property.php', + 'Eluceo\\iCal\\PropertyBag' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/PropertyBag.php', + 'Eluceo\\iCal\\Property\\ArrayValue' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/Property/ArrayValue.php', + 'Eluceo\\iCal\\Property\\DateTimeProperty' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/Property/DateTimeProperty.php', + 'Eluceo\\iCal\\Property\\DateTimesProperty' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/Property/DateTimesProperty.php', + 'Eluceo\\iCal\\Property\\Event\\Attendees' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/Property/Event/Attendees.php', + 'Eluceo\\iCal\\Property\\Event\\Description' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/Property/Event/Description.php', + 'Eluceo\\iCal\\Property\\Event\\Organizer' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/Property/Event/Organizer.php', + 'Eluceo\\iCal\\Property\\Event\\RecurrenceId' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/Property/Event/RecurrenceId.php', + 'Eluceo\\iCal\\Property\\Event\\RecurrenceRule' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/Property/Event/RecurrenceRule.php', + 'Eluceo\\iCal\\Property\\StringValue' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/Property/StringValue.php', + 'Eluceo\\iCal\\Property\\ValueInterface' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/Property/ValueInterface.php', + 'Eluceo\\iCal\\Util\\ComponentUtil' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/Util/ComponentUtil.php', + 'Eluceo\\iCal\\Util\\DateUtil' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/Util/DateUtil.php', + 'Eluceo\\iCal\\Util\\PropertyValueUtil' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/Util/PropertyValueUtil.php', + 'Gregwar\\Captcha\\CaptchaBuilder' => __DIR__ . '/..' . '/gregwar/captcha/CaptchaBuilder.php', + 'Gregwar\\Captcha\\CaptchaBuilderInterface' => __DIR__ . '/..' . '/gregwar/captcha/CaptchaBuilderInterface.php', + 'Gregwar\\Captcha\\ImageFileHandler' => __DIR__ . '/..' . '/gregwar/captcha/ImageFileHandler.php', + 'Gregwar\\Captcha\\PhraseBuilder' => __DIR__ . '/..' . '/gregwar/captcha/PhraseBuilder.php', + 'Gregwar\\Captcha\\PhraseBuilderInterface' => __DIR__ . '/..' . '/gregwar/captcha/PhraseBuilderInterface.php', + 'JsonRPC\\Client' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Client.php', + 'JsonRPC\\Exception\\AccessDeniedException' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Exception/AccessDeniedException.php', + 'JsonRPC\\Exception\\AuthenticationFailureException' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Exception/AuthenticationFailureException.php', + 'JsonRPC\\Exception\\ConnectionFailureException' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Exception/ConnectionFailureException.php', + 'JsonRPC\\Exception\\InvalidJsonFormatException' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Exception/InvalidJsonFormatException.php', + 'JsonRPC\\Exception\\InvalidJsonRpcFormatException' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Exception/InvalidJsonRpcFormatException.php', + 'JsonRPC\\Exception\\ResponseEncodingFailureException' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Exception/ResponseEncodingFailureException.php', + 'JsonRPC\\Exception\\ResponseException' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Exception/ResponseException.php', + 'JsonRPC\\Exception\\ServerErrorException' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Exception/ServerErrorException.php', + 'JsonRPC\\HttpClient' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/HttpClient.php', + 'JsonRPC\\MiddlewareHandler' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/MiddlewareHandler.php', + 'JsonRPC\\MiddlewareInterface' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/MiddlewareInterface.php', + 'JsonRPC\\ProcedureHandler' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/ProcedureHandler.php', + 'JsonRPC\\Request\\BatchRequestParser' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Request/BatchRequestParser.php', + 'JsonRPC\\Request\\RequestBuilder' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Request/RequestBuilder.php', + 'JsonRPC\\Request\\RequestParser' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Request/RequestParser.php', + 'JsonRPC\\Response\\ResponseBuilder' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Response/ResponseBuilder.php', + 'JsonRPC\\Response\\ResponseParser' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Response/ResponseParser.php', + 'JsonRPC\\Server' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Server.php', + 'JsonRPC\\Validator\\HostValidator' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Validator/HostValidator.php', + 'JsonRPC\\Validator\\JsonEncodingValidator' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Validator/JsonEncodingValidator.php', + 'JsonRPC\\Validator\\JsonFormatValidator' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Validator/JsonFormatValidator.php', + 'JsonRPC\\Validator\\RpcFormatValidator' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Validator/RpcFormatValidator.php', + 'JsonRPC\\Validator\\UserValidator' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Validator/UserValidator.php', + 'Kanboard\\Action\\Base' => __DIR__ . '/../..' . '/app/Action/Base.php', + 'Kanboard\\Action\\CommentCreation' => __DIR__ . '/../..' . '/app/Action/CommentCreation.php', + 'Kanboard\\Action\\CommentCreationMoveTaskColumn' => __DIR__ . '/../..' . '/app/Action/CommentCreationMoveTaskColumn.php', + 'Kanboard\\Action\\TaskAssignCategoryColor' => __DIR__ . '/../..' . '/app/Action/TaskAssignCategoryColor.php', + 'Kanboard\\Action\\TaskAssignCategoryLabel' => __DIR__ . '/../..' . '/app/Action/TaskAssignCategoryLabel.php', + 'Kanboard\\Action\\TaskAssignCategoryLink' => __DIR__ . '/../..' . '/app/Action/TaskAssignCategoryLink.php', + 'Kanboard\\Action\\TaskAssignColorCategory' => __DIR__ . '/../..' . '/app/Action/TaskAssignColorCategory.php', + 'Kanboard\\Action\\TaskAssignColorColumn' => __DIR__ . '/../..' . '/app/Action/TaskAssignColorColumn.php', + 'Kanboard\\Action\\TaskAssignColorLink' => __DIR__ . '/../..' . '/app/Action/TaskAssignColorLink.php', + 'Kanboard\\Action\\TaskAssignColorOnDueDate' => __DIR__ . '/../..' . '/app/Action/TaskAssignColorOnDueDate.php', + 'Kanboard\\Action\\TaskAssignColorPriority' => __DIR__ . '/../..' . '/app/Action/TaskAssignColorPriority.php', + 'Kanboard\\Action\\TaskAssignColorSwimlane' => __DIR__ . '/../..' . '/app/Action/TaskAssignColorSwimlane.php', + 'Kanboard\\Action\\TaskAssignColorUser' => __DIR__ . '/../..' . '/app/Action/TaskAssignColorUser.php', + 'Kanboard\\Action\\TaskAssignCreator' => __DIR__ . '/../..' . '/app/Action/TaskAssignCreator.php', + 'Kanboard\\Action\\TaskAssignCurrentUser' => __DIR__ . '/../..' . '/app/Action/TaskAssignCurrentUser.php', + 'Kanboard\\Action\\TaskAssignCurrentUserColumn' => __DIR__ . '/../..' . '/app/Action/TaskAssignCurrentUserColumn.php', + 'Kanboard\\Action\\TaskAssignDueDateOnCreation' => __DIR__ . '/../..' . '/app/Action/TaskAssignDueDateOnCreation.php', + 'Kanboard\\Action\\TaskAssignPrioritySwimlane' => __DIR__ . '/../..' . '/app/Action/TaskAssignPrioritySwimlane.php', + 'Kanboard\\Action\\TaskAssignSpecificUser' => __DIR__ . '/../..' . '/app/Action/TaskAssignSpecificUser.php', + 'Kanboard\\Action\\TaskAssignUser' => __DIR__ . '/../..' . '/app/Action/TaskAssignUser.php', + 'Kanboard\\Action\\TaskClose' => __DIR__ . '/../..' . '/app/Action/TaskClose.php', + 'Kanboard\\Action\\TaskCloseColumn' => __DIR__ . '/../..' . '/app/Action/TaskCloseColumn.php', + 'Kanboard\\Action\\TaskCloseNoActivity' => __DIR__ . '/../..' . '/app/Action/TaskCloseNoActivity.php', + 'Kanboard\\Action\\TaskCloseNoActivityColumn' => __DIR__ . '/../..' . '/app/Action/TaskCloseNoActivityColumn.php', + 'Kanboard\\Action\\TaskCloseNotMovedColumn' => __DIR__ . '/../..' . '/app/Action/TaskCloseNotMovedColumn.php', + 'Kanboard\\Action\\TaskCreation' => __DIR__ . '/../..' . '/app/Action/TaskCreation.php', + 'Kanboard\\Action\\TaskDuplicateAnotherProject' => __DIR__ . '/../..' . '/app/Action/TaskDuplicateAnotherProject.php', + 'Kanboard\\Action\\TaskEmail' => __DIR__ . '/../..' . '/app/Action/TaskEmail.php', + 'Kanboard\\Action\\TaskEmailNoActivity' => __DIR__ . '/../..' . '/app/Action/TaskEmailNoActivity.php', + 'Kanboard\\Action\\TaskMoveAnotherProject' => __DIR__ . '/../..' . '/app/Action/TaskMoveAnotherProject.php', + 'Kanboard\\Action\\TaskMoveColumnAssigned' => __DIR__ . '/../..' . '/app/Action/TaskMoveColumnAssigned.php', + 'Kanboard\\Action\\TaskMoveColumnCategoryChange' => __DIR__ . '/../..' . '/app/Action/TaskMoveColumnCategoryChange.php', + 'Kanboard\\Action\\TaskMoveColumnClosed' => __DIR__ . '/../..' . '/app/Action/TaskMoveColumnClosed.php', + 'Kanboard\\Action\\TaskMoveColumnNotMovedPeriod' => __DIR__ . '/../..' . '/app/Action/TaskMoveColumnNotMovedPeriod.php', + 'Kanboard\\Action\\TaskMoveColumnUnAssigned' => __DIR__ . '/../..' . '/app/Action/TaskMoveColumnUnAssigned.php', + 'Kanboard\\Action\\TaskOpen' => __DIR__ . '/../..' . '/app/Action/TaskOpen.php', + 'Kanboard\\Action\\TaskUpdateStartDate' => __DIR__ . '/../..' . '/app/Action/TaskUpdateStartDate.php', + 'Kanboard\\Analytic\\AverageLeadCycleTimeAnalytic' => __DIR__ . '/../..' . '/app/Analytic/AverageLeadCycleTimeAnalytic.php', + 'Kanboard\\Analytic\\AverageTimeSpentColumnAnalytic' => __DIR__ . '/../..' . '/app/Analytic/AverageTimeSpentColumnAnalytic.php', + 'Kanboard\\Analytic\\EstimatedTimeComparisonAnalytic' => __DIR__ . '/../..' . '/app/Analytic/EstimatedTimeComparisonAnalytic.php', + 'Kanboard\\Analytic\\TaskDistributionAnalytic' => __DIR__ . '/../..' . '/app/Analytic/TaskDistributionAnalytic.php', + 'Kanboard\\Analytic\\UserDistributionAnalytic' => __DIR__ . '/../..' . '/app/Analytic/UserDistributionAnalytic.php', + 'Kanboard\\Api\\Authorization\\ActionAuthorization' => __DIR__ . '/../..' . '/app/Api/Authorization/ActionAuthorization.php', + 'Kanboard\\Api\\Authorization\\CategoryAuthorization' => __DIR__ . '/../..' . '/app/Api/Authorization/CategoryAuthorization.php', + 'Kanboard\\Api\\Authorization\\ColumnAuthorization' => __DIR__ . '/../..' . '/app/Api/Authorization/ColumnAuthorization.php', + 'Kanboard\\Api\\Authorization\\CommentAuthorization' => __DIR__ . '/../..' . '/app/Api/Authorization/CommentAuthorization.php', + 'Kanboard\\Api\\Authorization\\ProcedureAuthorization' => __DIR__ . '/../..' . '/app/Api/Authorization/ProcedureAuthorization.php', + 'Kanboard\\Api\\Authorization\\ProjectAuthorization' => __DIR__ . '/../..' . '/app/Api/Authorization/ProjectAuthorization.php', + 'Kanboard\\Api\\Authorization\\SubtaskAuthorization' => __DIR__ . '/../..' . '/app/Api/Authorization/SubtaskAuthorization.php', + 'Kanboard\\Api\\Authorization\\TagAuthorization' => __DIR__ . '/../..' . '/app/Api/Authorization/TagAuthorization.php', + 'Kanboard\\Api\\Authorization\\TaskAuthorization' => __DIR__ . '/../..' . '/app/Api/Authorization/TaskAuthorization.php', + 'Kanboard\\Api\\Authorization\\TaskFileAuthorization' => __DIR__ . '/../..' . '/app/Api/Authorization/TaskFileAuthorization.php', + 'Kanboard\\Api\\Authorization\\TaskLinkAuthorization' => __DIR__ . '/../..' . '/app/Api/Authorization/TaskLinkAuthorization.php', + 'Kanboard\\Api\\Authorization\\UserAuthorization' => __DIR__ . '/../..' . '/app/Api/Authorization/UserAuthorization.php', + 'Kanboard\\Api\\Middleware\\AuthenticationMiddleware' => __DIR__ . '/../..' . '/app/Api/Middleware/AuthenticationMiddleware.php', + 'Kanboard\\Api\\Procedure\\ActionProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/ActionProcedure.php', + 'Kanboard\\Api\\Procedure\\AppProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/AppProcedure.php', + 'Kanboard\\Api\\Procedure\\BaseProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/BaseProcedure.php', + 'Kanboard\\Api\\Procedure\\BoardProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/BoardProcedure.php', + 'Kanboard\\Api\\Procedure\\CategoryProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/CategoryProcedure.php', + 'Kanboard\\Api\\Procedure\\ColumnProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/ColumnProcedure.php', + 'Kanboard\\Api\\Procedure\\CommentProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/CommentProcedure.php', + 'Kanboard\\Api\\Procedure\\GroupMemberProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/GroupMemberProcedure.php', + 'Kanboard\\Api\\Procedure\\GroupProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/GroupProcedure.php', + 'Kanboard\\Api\\Procedure\\LinkProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/LinkProcedure.php', + 'Kanboard\\Api\\Procedure\\MeProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/MeProcedure.php', + 'Kanboard\\Api\\Procedure\\ProjectFileProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/ProjectFileProcedure.php', + 'Kanboard\\Api\\Procedure\\ProjectPermissionProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/ProjectPermissionProcedure.php', + 'Kanboard\\Api\\Procedure\\ProjectProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/ProjectProcedure.php', + 'Kanboard\\Api\\Procedure\\SubtaskProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/SubtaskProcedure.php', + 'Kanboard\\Api\\Procedure\\SubtaskTimeTrackingProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/SubtaskTimeTrackingProcedure.php', + 'Kanboard\\Api\\Procedure\\SwimlaneProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/SwimlaneProcedure.php', + 'Kanboard\\Api\\Procedure\\TagProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/TagProcedure.php', + 'Kanboard\\Api\\Procedure\\TaskExternalLinkProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/TaskExternalLinkProcedure.php', + 'Kanboard\\Api\\Procedure\\TaskFileProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/TaskFileProcedure.php', + 'Kanboard\\Api\\Procedure\\TaskLinkProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/TaskLinkProcedure.php', + 'Kanboard\\Api\\Procedure\\TaskMetadataProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/TaskMetadataProcedure.php', + 'Kanboard\\Api\\Procedure\\TaskProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/TaskProcedure.php', + 'Kanboard\\Api\\Procedure\\TaskTagProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/TaskTagProcedure.php', + 'Kanboard\\Api\\Procedure\\UserProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/UserProcedure.php', + 'Kanboard\\Auth\\ApiAccessTokenAuth' => __DIR__ . '/../..' . '/app/Auth/ApiAccessTokenAuth.php', + 'Kanboard\\Auth\\DatabaseAuth' => __DIR__ . '/../..' . '/app/Auth/DatabaseAuth.php', + 'Kanboard\\Auth\\LdapAuth' => __DIR__ . '/../..' . '/app/Auth/LdapAuth.php', + 'Kanboard\\Auth\\RememberMeAuth' => __DIR__ . '/../..' . '/app/Auth/RememberMeAuth.php', + 'Kanboard\\Auth\\ReverseProxyAuth' => __DIR__ . '/../..' . '/app/Auth/ReverseProxyAuth.php', + 'Kanboard\\Auth\\TotpAuth' => __DIR__ . '/../..' . '/app/Auth/TotpAuth.php', + 'Kanboard\\Console\\BaseCommand' => __DIR__ . '/../..' . '/app/Console/BaseCommand.php', + 'Kanboard\\Console\\CronjobCommand' => __DIR__ . '/../..' . '/app/Console/CronjobCommand.php', + 'Kanboard\\Console\\DatabaseMigrationCommand' => __DIR__ . '/../..' . '/app/Console/DatabaseMigrationCommand.php', + 'Kanboard\\Console\\DatabaseVersionCommand' => __DIR__ . '/../..' . '/app/Console/DatabaseVersionCommand.php', + 'Kanboard\\Console\\JobCommand' => __DIR__ . '/../..' . '/app/Console/JobCommand.php', + 'Kanboard\\Console\\LocaleComparatorCommand' => __DIR__ . '/../..' . '/app/Console/LocaleComparatorCommand.php', + 'Kanboard\\Console\\LocaleSyncCommand' => __DIR__ . '/../..' . '/app/Console/LocaleSyncCommand.php', + 'Kanboard\\Console\\PluginInstallCommand' => __DIR__ . '/../..' . '/app/Console/PluginInstallCommand.php', + 'Kanboard\\Console\\PluginUninstallCommand' => __DIR__ . '/../..' . '/app/Console/PluginUninstallCommand.php', + 'Kanboard\\Console\\PluginUpgradeCommand' => __DIR__ . '/../..' . '/app/Console/PluginUpgradeCommand.php', + 'Kanboard\\Console\\ProjectDailyColumnStatsExportCommand' => __DIR__ . '/../..' . '/app/Console/ProjectDailyColumnStatsExportCommand.php', + 'Kanboard\\Console\\ProjectDailyStatsCalculationCommand' => __DIR__ . '/../..' . '/app/Console/ProjectDailyStatsCalculationCommand.php', + 'Kanboard\\Console\\ResetPasswordCommand' => __DIR__ . '/../..' . '/app/Console/ResetPasswordCommand.php', + 'Kanboard\\Console\\ResetTwoFactorCommand' => __DIR__ . '/../..' . '/app/Console/ResetTwoFactorCommand.php', + 'Kanboard\\Console\\SubtaskExportCommand' => __DIR__ . '/../..' . '/app/Console/SubtaskExportCommand.php', + 'Kanboard\\Console\\TaskExportCommand' => __DIR__ . '/../..' . '/app/Console/TaskExportCommand.php', + 'Kanboard\\Console\\TaskOverdueNotificationCommand' => __DIR__ . '/../..' . '/app/Console/TaskOverdueNotificationCommand.php', + 'Kanboard\\Console\\TaskTriggerCommand' => __DIR__ . '/../..' . '/app/Console/TaskTriggerCommand.php', + 'Kanboard\\Console\\TransitionExportCommand' => __DIR__ . '/../..' . '/app/Console/TransitionExportCommand.php', + 'Kanboard\\Console\\WorkerCommand' => __DIR__ . '/../..' . '/app/Console/WorkerCommand.php', + 'Kanboard\\Controller\\ActionController' => __DIR__ . '/../..' . '/app/Controller/ActionController.php', + 'Kanboard\\Controller\\ActionCreationController' => __DIR__ . '/../..' . '/app/Controller/ActionCreationController.php', + 'Kanboard\\Controller\\ActivityController' => __DIR__ . '/../..' . '/app/Controller/ActivityController.php', + 'Kanboard\\Controller\\AnalyticController' => __DIR__ . '/../..' . '/app/Controller/AnalyticController.php', + 'Kanboard\\Controller\\AppController' => __DIR__ . '/../..' . '/app/Controller/AppController.php', + 'Kanboard\\Controller\\AuthController' => __DIR__ . '/../..' . '/app/Controller/AuthController.php', + 'Kanboard\\Controller\\AvatarFileController' => __DIR__ . '/../..' . '/app/Controller/AvatarFileController.php', + 'Kanboard\\Controller\\BaseController' => __DIR__ . '/../..' . '/app/Controller/BaseController.php', + 'Kanboard\\Controller\\BoardAjaxController' => __DIR__ . '/../..' . '/app/Controller/BoardAjaxController.php', + 'Kanboard\\Controller\\BoardPopoverController' => __DIR__ . '/../..' . '/app/Controller/BoardPopoverController.php', + 'Kanboard\\Controller\\BoardTooltipController' => __DIR__ . '/../..' . '/app/Controller/BoardTooltipController.php', + 'Kanboard\\Controller\\BoardViewController' => __DIR__ . '/../..' . '/app/Controller/BoardViewController.php', + 'Kanboard\\Controller\\CaptchaController' => __DIR__ . '/../..' . '/app/Controller/CaptchaController.php', + 'Kanboard\\Controller\\CategoryController' => __DIR__ . '/../..' . '/app/Controller/CategoryController.php', + 'Kanboard\\Controller\\ColumnController' => __DIR__ . '/../..' . '/app/Controller/ColumnController.php', + 'Kanboard\\Controller\\ColumnMoveRestrictionController' => __DIR__ . '/../..' . '/app/Controller/ColumnMoveRestrictionController.php', + 'Kanboard\\Controller\\ColumnRestrictionController' => __DIR__ . '/../..' . '/app/Controller/ColumnRestrictionController.php', + 'Kanboard\\Controller\\CommentController' => __DIR__ . '/../..' . '/app/Controller/CommentController.php', + 'Kanboard\\Controller\\CommentListController' => __DIR__ . '/../..' . '/app/Controller/CommentListController.php', + 'Kanboard\\Controller\\CommentMailController' => __DIR__ . '/../..' . '/app/Controller/CommentMailController.php', + 'Kanboard\\Controller\\ConfigController' => __DIR__ . '/../..' . '/app/Controller/ConfigController.php', + 'Kanboard\\Controller\\CurrencyController' => __DIR__ . '/../..' . '/app/Controller/CurrencyController.php', + 'Kanboard\\Controller\\CustomFilterController' => __DIR__ . '/../..' . '/app/Controller/CustomFilterController.php', + 'Kanboard\\Controller\\DashboardController' => __DIR__ . '/../..' . '/app/Controller/DashboardController.php', + 'Kanboard\\Controller\\DocumentationController' => __DIR__ . '/../..' . '/app/Controller/DocumentationController.php', + 'Kanboard\\Controller\\ExportController' => __DIR__ . '/../..' . '/app/Controller/ExportController.php', + 'Kanboard\\Controller\\ExternalTaskCreationController' => __DIR__ . '/../..' . '/app/Controller/ExternalTaskCreationController.php', + 'Kanboard\\Controller\\ExternalTaskViewController' => __DIR__ . '/../..' . '/app/Controller/ExternalTaskViewController.php', + 'Kanboard\\Controller\\FeedController' => __DIR__ . '/../..' . '/app/Controller/FeedController.php', + 'Kanboard\\Controller\\FileViewerController' => __DIR__ . '/../..' . '/app/Controller/FileViewerController.php', + 'Kanboard\\Controller\\GroupAjaxController' => __DIR__ . '/../..' . '/app/Controller/GroupAjaxController.php', + 'Kanboard\\Controller\\GroupCreationController' => __DIR__ . '/../..' . '/app/Controller/GroupCreationController.php', + 'Kanboard\\Controller\\GroupListController' => __DIR__ . '/../..' . '/app/Controller/GroupListController.php', + 'Kanboard\\Controller\\GroupModificationController' => __DIR__ . '/../..' . '/app/Controller/GroupModificationController.php', + 'Kanboard\\Controller\\ICalendarController' => __DIR__ . '/../..' . '/app/Controller/ICalendarController.php', + 'Kanboard\\Controller\\LinkController' => __DIR__ . '/../..' . '/app/Controller/LinkController.php', + 'Kanboard\\Controller\\OAuthController' => __DIR__ . '/../..' . '/app/Controller/OAuthController.php', + 'Kanboard\\Controller\\PasswordResetController' => __DIR__ . '/../..' . '/app/Controller/PasswordResetController.php', + 'Kanboard\\Controller\\PluginController' => __DIR__ . '/../..' . '/app/Controller/PluginController.php', + 'Kanboard\\Controller\\ProjectActionDuplicationController' => __DIR__ . '/../..' . '/app/Controller/ProjectActionDuplicationController.php', + 'Kanboard\\Controller\\ProjectCreationController' => __DIR__ . '/../..' . '/app/Controller/ProjectCreationController.php', + 'Kanboard\\Controller\\ProjectEditController' => __DIR__ . '/../..' . '/app/Controller/ProjectEditController.php', + 'Kanboard\\Controller\\ProjectFileController' => __DIR__ . '/../..' . '/app/Controller/ProjectFileController.php', + 'Kanboard\\Controller\\ProjectListController' => __DIR__ . '/../..' . '/app/Controller/ProjectListController.php', + 'Kanboard\\Controller\\ProjectOverviewController' => __DIR__ . '/../..' . '/app/Controller/ProjectOverviewController.php', + 'Kanboard\\Controller\\ProjectPermissionController' => __DIR__ . '/../..' . '/app/Controller/ProjectPermissionController.php', + 'Kanboard\\Controller\\ProjectRoleController' => __DIR__ . '/../..' . '/app/Controller/ProjectRoleController.php', + 'Kanboard\\Controller\\ProjectRoleRestrictionController' => __DIR__ . '/../..' . '/app/Controller/ProjectRoleRestrictionController.php', + 'Kanboard\\Controller\\ProjectStatusController' => __DIR__ . '/../..' . '/app/Controller/ProjectStatusController.php', + 'Kanboard\\Controller\\ProjectTagController' => __DIR__ . '/../..' . '/app/Controller/ProjectTagController.php', + 'Kanboard\\Controller\\ProjectUserOverviewController' => __DIR__ . '/../..' . '/app/Controller/ProjectUserOverviewController.php', + 'Kanboard\\Controller\\ProjectViewController' => __DIR__ . '/../..' . '/app/Controller/ProjectViewController.php', + 'Kanboard\\Controller\\SearchController' => __DIR__ . '/../..' . '/app/Controller/SearchController.php', + 'Kanboard\\Controller\\SubtaskController' => __DIR__ . '/../..' . '/app/Controller/SubtaskController.php', + 'Kanboard\\Controller\\SubtaskConverterController' => __DIR__ . '/../..' . '/app/Controller/SubtaskConverterController.php', + 'Kanboard\\Controller\\SubtaskRestrictionController' => __DIR__ . '/../..' . '/app/Controller/SubtaskRestrictionController.php', + 'Kanboard\\Controller\\SubtaskStatusController' => __DIR__ . '/../..' . '/app/Controller/SubtaskStatusController.php', + 'Kanboard\\Controller\\SwimlaneController' => __DIR__ . '/../..' . '/app/Controller/SwimlaneController.php', + 'Kanboard\\Controller\\TagController' => __DIR__ . '/../..' . '/app/Controller/TagController.php', + 'Kanboard\\Controller\\TaskAjaxController' => __DIR__ . '/../..' . '/app/Controller/TaskAjaxController.php', + 'Kanboard\\Controller\\TaskBulkController' => __DIR__ . '/../..' . '/app/Controller/TaskBulkController.php', + 'Kanboard\\Controller\\TaskCreationController' => __DIR__ . '/../..' . '/app/Controller/TaskCreationController.php', + 'Kanboard\\Controller\\TaskDuplicationController' => __DIR__ . '/../..' . '/app/Controller/TaskDuplicationController.php', + 'Kanboard\\Controller\\TaskExternalLinkController' => __DIR__ . '/../..' . '/app/Controller/TaskExternalLinkController.php', + 'Kanboard\\Controller\\TaskFileController' => __DIR__ . '/../..' . '/app/Controller/TaskFileController.php', + 'Kanboard\\Controller\\TaskImportController' => __DIR__ . '/../..' . '/app/Controller/TaskImportController.php', + 'Kanboard\\Controller\\TaskInternalLinkController' => __DIR__ . '/../..' . '/app/Controller/TaskInternalLinkController.php', + 'Kanboard\\Controller\\TaskListController' => __DIR__ . '/../..' . '/app/Controller/TaskListController.php', + 'Kanboard\\Controller\\TaskMailController' => __DIR__ . '/../..' . '/app/Controller/TaskMailController.php', + 'Kanboard\\Controller\\TaskModificationController' => __DIR__ . '/../..' . '/app/Controller/TaskModificationController.php', + 'Kanboard\\Controller\\TaskMovePositionController' => __DIR__ . '/../..' . '/app/Controller/TaskMovePositionController.php', + 'Kanboard\\Controller\\TaskPopoverController' => __DIR__ . '/../..' . '/app/Controller/TaskPopoverController.php', + 'Kanboard\\Controller\\TaskRecurrenceController' => __DIR__ . '/../..' . '/app/Controller/TaskRecurrenceController.php', + 'Kanboard\\Controller\\TaskStatusController' => __DIR__ . '/../..' . '/app/Controller/TaskStatusController.php', + 'Kanboard\\Controller\\TaskSuppressionController' => __DIR__ . '/../..' . '/app/Controller/TaskSuppressionController.php', + 'Kanboard\\Controller\\TaskViewController' => __DIR__ . '/../..' . '/app/Controller/TaskViewController.php', + 'Kanboard\\Controller\\TwoFactorController' => __DIR__ . '/../..' . '/app/Controller/TwoFactorController.php', + 'Kanboard\\Controller\\UserAjaxController' => __DIR__ . '/../..' . '/app/Controller/UserAjaxController.php', + 'Kanboard\\Controller\\UserApiAccessController' => __DIR__ . '/../..' . '/app/Controller/UserApiAccessController.php', + 'Kanboard\\Controller\\UserCreationController' => __DIR__ . '/../..' . '/app/Controller/UserCreationController.php', + 'Kanboard\\Controller\\UserCredentialController' => __DIR__ . '/../..' . '/app/Controller/UserCredentialController.php', + 'Kanboard\\Controller\\UserImportController' => __DIR__ . '/../..' . '/app/Controller/UserImportController.php', + 'Kanboard\\Controller\\UserInviteController' => __DIR__ . '/../..' . '/app/Controller/UserInviteController.php', + 'Kanboard\\Controller\\UserListController' => __DIR__ . '/../..' . '/app/Controller/UserListController.php', + 'Kanboard\\Controller\\UserModificationController' => __DIR__ . '/../..' . '/app/Controller/UserModificationController.php', + 'Kanboard\\Controller\\UserStatusController' => __DIR__ . '/../..' . '/app/Controller/UserStatusController.php', + 'Kanboard\\Controller\\UserViewController' => __DIR__ . '/../..' . '/app/Controller/UserViewController.php', + 'Kanboard\\Controller\\WebNotificationController' => __DIR__ . '/../..' . '/app/Controller/WebNotificationController.php', + 'Kanboard\\Core\\Action\\ActionManager' => __DIR__ . '/../..' . '/app/Core/Action/ActionManager.php', + 'Kanboard\\Core\\Base' => __DIR__ . '/../..' . '/app/Core/Base.php', + 'Kanboard\\Core\\Cache\\BaseCache' => __DIR__ . '/../..' . '/app/Core/Cache/BaseCache.php', + 'Kanboard\\Core\\Cache\\CacheInterface' => __DIR__ . '/../..' . '/app/Core/Cache/CacheInterface.php', + 'Kanboard\\Core\\Cache\\FileCache' => __DIR__ . '/../..' . '/app/Core/Cache/FileCache.php', + 'Kanboard\\Core\\Cache\\MemoryCache' => __DIR__ . '/../..' . '/app/Core/Cache/MemoryCache.php', + 'Kanboard\\Core\\Controller\\AccessForbiddenException' => __DIR__ . '/../..' . '/app/Core/Controller/AccessForbiddenException.php', + 'Kanboard\\Core\\Controller\\BaseException' => __DIR__ . '/../..' . '/app/Core/Controller/BaseException.php', + 'Kanboard\\Core\\Controller\\BaseMiddleware' => __DIR__ . '/../..' . '/app/Core/Controller/BaseMiddleware.php', + 'Kanboard\\Core\\Controller\\PageNotFoundException' => __DIR__ . '/../..' . '/app/Core/Controller/PageNotFoundException.php', + 'Kanboard\\Core\\Controller\\Runner' => __DIR__ . '/../..' . '/app/Core/Controller/Runner.php', + 'Kanboard\\Core\\Csv' => __DIR__ . '/../..' . '/app/Core/Csv.php', + 'Kanboard\\Core\\DateParser' => __DIR__ . '/../..' . '/app/Core/DateParser.php', + 'Kanboard\\Core\\Event\\EventManager' => __DIR__ . '/../..' . '/app/Core/Event/EventManager.php', + 'Kanboard\\Core\\ExternalLink\\ExternalLinkInterface' => __DIR__ . '/../..' . '/app/Core/ExternalLink/ExternalLinkInterface.php', + 'Kanboard\\Core\\ExternalLink\\ExternalLinkManager' => __DIR__ . '/../..' . '/app/Core/ExternalLink/ExternalLinkManager.php', + 'Kanboard\\Core\\ExternalLink\\ExternalLinkProviderInterface' => __DIR__ . '/../..' . '/app/Core/ExternalLink/ExternalLinkProviderInterface.php', + 'Kanboard\\Core\\ExternalLink\\ExternalLinkProviderNotFound' => __DIR__ . '/../..' . '/app/Core/ExternalLink/ExternalLinkProviderNotFound.php', + 'Kanboard\\Core\\ExternalTask\\AccessForbiddenException' => __DIR__ . '/../..' . '/app/Core/ExternalTask/AccessForbiddenException.php', + 'Kanboard\\Core\\ExternalTask\\ExternalTaskException' => __DIR__ . '/../..' . '/app/Core/ExternalTask/ExternalTaskException.php', + 'Kanboard\\Core\\ExternalTask\\ExternalTaskInterface' => __DIR__ . '/../..' . '/app/Core/ExternalTask/ExternalTaskInterface.php', + 'Kanboard\\Core\\ExternalTask\\ExternalTaskManager' => __DIR__ . '/../..' . '/app/Core/ExternalTask/ExternalTaskManager.php', + 'Kanboard\\Core\\ExternalTask\\ExternalTaskProviderInterface' => __DIR__ . '/../..' . '/app/Core/ExternalTask/ExternalTaskProviderInterface.php', + 'Kanboard\\Core\\ExternalTask\\NotFoundException' => __DIR__ . '/../..' . '/app/Core/ExternalTask/NotFoundException.php', + 'Kanboard\\Core\\ExternalTask\\ProviderNotFoundException' => __DIR__ . '/../..' . '/app/Core/ExternalTask/ProviderNotFoundException.php', + 'Kanboard\\Core\\Filter\\CriteriaInterface' => __DIR__ . '/../..' . '/app/Core/Filter/CriteriaInterface.php', + 'Kanboard\\Core\\Filter\\FilterInterface' => __DIR__ . '/../..' . '/app/Core/Filter/FilterInterface.php', + 'Kanboard\\Core\\Filter\\FormatterInterface' => __DIR__ . '/../..' . '/app/Core/Filter/FormatterInterface.php', + 'Kanboard\\Core\\Filter\\Lexer' => __DIR__ . '/../..' . '/app/Core/Filter/Lexer.php', + 'Kanboard\\Core\\Filter\\LexerBuilder' => __DIR__ . '/../..' . '/app/Core/Filter/LexerBuilder.php', + 'Kanboard\\Core\\Filter\\OrCriteria' => __DIR__ . '/../..' . '/app/Core/Filter/OrCriteria.php', + 'Kanboard\\Core\\Filter\\QueryBuilder' => __DIR__ . '/../..' . '/app/Core/Filter/QueryBuilder.php', + 'Kanboard\\Core\\Group\\GroupBackendProviderInterface' => __DIR__ . '/../..' . '/app/Core/Group/GroupBackendProviderInterface.php', + 'Kanboard\\Core\\Group\\GroupManager' => __DIR__ . '/../..' . '/app/Core/Group/GroupManager.php', + 'Kanboard\\Core\\Group\\GroupProviderInterface' => __DIR__ . '/../..' . '/app/Core/Group/GroupProviderInterface.php', + 'Kanboard\\Core\\Helper' => __DIR__ . '/../..' . '/app/Core/Helper.php', + 'Kanboard\\Core\\Http\\Client' => __DIR__ . '/../..' . '/app/Core/Http/Client.php', + 'Kanboard\\Core\\Http\\OAuth2' => __DIR__ . '/../..' . '/app/Core/Http/OAuth2.php', + 'Kanboard\\Core\\Http\\RememberMeCookie' => __DIR__ . '/../..' . '/app/Core/Http/RememberMeCookie.php', + 'Kanboard\\Core\\Http\\Request' => __DIR__ . '/../..' . '/app/Core/Http/Request.php', + 'Kanboard\\Core\\Http\\Response' => __DIR__ . '/../..' . '/app/Core/Http/Response.php', + 'Kanboard\\Core\\Http\\Route' => __DIR__ . '/../..' . '/app/Core/Http/Route.php', + 'Kanboard\\Core\\Http\\Router' => __DIR__ . '/../..' . '/app/Core/Http/Router.php', + 'Kanboard\\Core\\Ldap\\Client' => __DIR__ . '/../..' . '/app/Core/Ldap/Client.php', + 'Kanboard\\Core\\Ldap\\ClientException' => __DIR__ . '/../..' . '/app/Core/Ldap/ClientException.php', + 'Kanboard\\Core\\Ldap\\ConnectionException' => __DIR__ . '/../..' . '/app/Core/Ldap/ConnectionException.php', + 'Kanboard\\Core\\Ldap\\Entries' => __DIR__ . '/../..' . '/app/Core/Ldap/Entries.php', + 'Kanboard\\Core\\Ldap\\Entry' => __DIR__ . '/../..' . '/app/Core/Ldap/Entry.php', + 'Kanboard\\Core\\Ldap\\Group' => __DIR__ . '/../..' . '/app/Core/Ldap/Group.php', + 'Kanboard\\Core\\Ldap\\Query' => __DIR__ . '/../..' . '/app/Core/Ldap/Query.php', + 'Kanboard\\Core\\Ldap\\User' => __DIR__ . '/../..' . '/app/Core/Ldap/User.php', + 'Kanboard\\Core\\Mail\\Client' => __DIR__ . '/../..' . '/app/Core/Mail/Client.php', + 'Kanboard\\Core\\Mail\\ClientInterface' => __DIR__ . '/../..' . '/app/Core/Mail/ClientInterface.php', + 'Kanboard\\Core\\Mail\\Transport\\Mail' => __DIR__ . '/../..' . '/app/Core/Mail/Transport/Mail.php', + 'Kanboard\\Core\\Mail\\Transport\\Sendmail' => __DIR__ . '/../..' . '/app/Core/Mail/Transport/Sendmail.php', + 'Kanboard\\Core\\Mail\\Transport\\Smtp' => __DIR__ . '/../..' . '/app/Core/Mail/Transport/Smtp.php', + 'Kanboard\\Core\\Markdown' => __DIR__ . '/../..' . '/app/Core/Markdown.php', + 'Kanboard\\Core\\Notification\\NotificationInterface' => __DIR__ . '/../..' . '/app/Core/Notification/NotificationInterface.php', + 'Kanboard\\Core\\ObjectStorage\\FileStorage' => __DIR__ . '/../..' . '/app/Core/ObjectStorage/FileStorage.php', + 'Kanboard\\Core\\ObjectStorage\\ObjectStorageException' => __DIR__ . '/../..' . '/app/Core/ObjectStorage/ObjectStorageException.php', + 'Kanboard\\Core\\ObjectStorage\\ObjectStorageInterface' => __DIR__ . '/../..' . '/app/Core/ObjectStorage/ObjectStorageInterface.php', + 'Kanboard\\Core\\Paginator' => __DIR__ . '/../..' . '/app/Core/Paginator.php', + 'Kanboard\\Core\\Plugin\\Base' => __DIR__ . '/../..' . '/app/Core/Plugin/Base.php', + 'Kanboard\\Core\\Plugin\\Directory' => __DIR__ . '/../..' . '/app/Core/Plugin/Directory.php', + 'Kanboard\\Core\\Plugin\\Hook' => __DIR__ . '/../..' . '/app/Core/Plugin/Hook.php', + 'Kanboard\\Core\\Plugin\\Installer' => __DIR__ . '/../..' . '/app/Core/Plugin/Installer.php', + 'Kanboard\\Core\\Plugin\\Loader' => __DIR__ . '/../..' . '/app/Core/Plugin/Loader.php', + 'Kanboard\\Core\\Plugin\\PluginException' => __DIR__ . '/../..' . '/app/Core/Plugin/PluginException.php', + 'Kanboard\\Core\\Plugin\\PluginInstallerException' => __DIR__ . '/../..' . '/app/Core/Plugin/PluginInstallerException.php', + 'Kanboard\\Core\\Plugin\\SchemaHandler' => __DIR__ . '/../..' . '/app/Core/Plugin/SchemaHandler.php', + 'Kanboard\\Core\\Plugin\\Version' => __DIR__ . '/../..' . '/app/Core/Plugin/Version.php', + 'Kanboard\\Core\\Queue\\JobHandler' => __DIR__ . '/../..' . '/app/Core/Queue/JobHandler.php', + 'Kanboard\\Core\\Queue\\QueueManager' => __DIR__ . '/../..' . '/app/Core/Queue/QueueManager.php', + 'Kanboard\\Core\\Security\\AccessMap' => __DIR__ . '/../..' . '/app/Core/Security/AccessMap.php', + 'Kanboard\\Core\\Security\\AuthenticationManager' => __DIR__ . '/../..' . '/app/Core/Security/AuthenticationManager.php', + 'Kanboard\\Core\\Security\\AuthenticationProviderInterface' => __DIR__ . '/../..' . '/app/Core/Security/AuthenticationProviderInterface.php', + 'Kanboard\\Core\\Security\\Authorization' => __DIR__ . '/../..' . '/app/Core/Security/Authorization.php', + 'Kanboard\\Core\\Security\\OAuthAuthenticationProviderInterface' => __DIR__ . '/../..' . '/app/Core/Security/OAuthAuthenticationProviderInterface.php', + 'Kanboard\\Core\\Security\\PasswordAuthenticationProviderInterface' => __DIR__ . '/../..' . '/app/Core/Security/PasswordAuthenticationProviderInterface.php', + 'Kanboard\\Core\\Security\\PostAuthenticationProviderInterface' => __DIR__ . '/../..' . '/app/Core/Security/PostAuthenticationProviderInterface.php', + 'Kanboard\\Core\\Security\\PreAuthenticationProviderInterface' => __DIR__ . '/../..' . '/app/Core/Security/PreAuthenticationProviderInterface.php', + 'Kanboard\\Core\\Security\\Role' => __DIR__ . '/../..' . '/app/Core/Security/Role.php', + 'Kanboard\\Core\\Security\\SessionCheckProviderInterface' => __DIR__ . '/../..' . '/app/Core/Security/SessionCheckProviderInterface.php', + 'Kanboard\\Core\\Security\\Token' => __DIR__ . '/../..' . '/app/Core/Security/Token.php', + 'Kanboard\\Core\\Session\\FlashMessage' => __DIR__ . '/../..' . '/app/Core/Session/FlashMessage.php', + 'Kanboard\\Core\\Session\\SessionManager' => __DIR__ . '/../..' . '/app/Core/Session/SessionManager.php', + 'Kanboard\\Core\\Session\\SessionStorage' => __DIR__ . '/../..' . '/app/Core/Session/SessionStorage.php', + 'Kanboard\\Core\\Template' => __DIR__ . '/../..' . '/app/Core/Template.php', + 'Kanboard\\Core\\Thumbnail' => __DIR__ . '/../..' . '/app/Core/Thumbnail.php', + 'Kanboard\\Core\\Tool' => __DIR__ . '/../..' . '/app/Core/Tool.php', + 'Kanboard\\Core\\Translator' => __DIR__ . '/../..' . '/app/Core/Translator.php', + 'Kanboard\\Core\\User\\Avatar\\AvatarManager' => __DIR__ . '/../..' . '/app/Core/User/Avatar/AvatarManager.php', + 'Kanboard\\Core\\User\\Avatar\\AvatarProviderInterface' => __DIR__ . '/../..' . '/app/Core/User/Avatar/AvatarProviderInterface.php', + 'Kanboard\\Core\\User\\GroupSync' => __DIR__ . '/../..' . '/app/Core/User/GroupSync.php', + 'Kanboard\\Core\\User\\UserProfile' => __DIR__ . '/../..' . '/app/Core/User/UserProfile.php', + 'Kanboard\\Core\\User\\UserProperty' => __DIR__ . '/../..' . '/app/Core/User/UserProperty.php', + 'Kanboard\\Core\\User\\UserProviderInterface' => __DIR__ . '/../..' . '/app/Core/User/UserProviderInterface.php', + 'Kanboard\\Core\\User\\UserSession' => __DIR__ . '/../..' . '/app/Core/User/UserSession.php', + 'Kanboard\\Core\\User\\UserSync' => __DIR__ . '/../..' . '/app/Core/User/UserSync.php', + 'Kanboard\\Decorator\\ColumnMoveRestrictionCacheDecorator' => __DIR__ . '/../..' . '/app/Decorator/ColumnMoveRestrictionCacheDecorator.php', + 'Kanboard\\Decorator\\ColumnRestrictionCacheDecorator' => __DIR__ . '/../..' . '/app/Decorator/ColumnRestrictionCacheDecorator.php', + 'Kanboard\\Decorator\\MetadataCacheDecorator' => __DIR__ . '/../..' . '/app/Decorator/MetadataCacheDecorator.php', + 'Kanboard\\Decorator\\ProjectRoleRestrictionCacheDecorator' => __DIR__ . '/../..' . '/app/Decorator/ProjectRoleRestrictionCacheDecorator.php', + 'Kanboard\\Decorator\\UserCacheDecorator' => __DIR__ . '/../..' . '/app/Decorator/UserCacheDecorator.php', + 'Kanboard\\EventBuilder\\BaseEventBuilder' => __DIR__ . '/../..' . '/app/EventBuilder/BaseEventBuilder.php', + 'Kanboard\\EventBuilder\\CommentEventBuilder' => __DIR__ . '/../..' . '/app/EventBuilder/CommentEventBuilder.php', + 'Kanboard\\EventBuilder\\EventIteratorBuilder' => __DIR__ . '/../..' . '/app/EventBuilder/EventIteratorBuilder.php', + 'Kanboard\\EventBuilder\\ProjectFileEventBuilder' => __DIR__ . '/../..' . '/app/EventBuilder/ProjectFileEventBuilder.php', + 'Kanboard\\EventBuilder\\SubtaskEventBuilder' => __DIR__ . '/../..' . '/app/EventBuilder/SubtaskEventBuilder.php', + 'Kanboard\\EventBuilder\\TaskEventBuilder' => __DIR__ . '/../..' . '/app/EventBuilder/TaskEventBuilder.php', + 'Kanboard\\EventBuilder\\TaskFileEventBuilder' => __DIR__ . '/../..' . '/app/EventBuilder/TaskFileEventBuilder.php', + 'Kanboard\\EventBuilder\\TaskLinkEventBuilder' => __DIR__ . '/../..' . '/app/EventBuilder/TaskLinkEventBuilder.php', + 'Kanboard\\Event\\AuthFailureEvent' => __DIR__ . '/../..' . '/app/Event/AuthFailureEvent.php', + 'Kanboard\\Event\\AuthSuccessEvent' => __DIR__ . '/../..' . '/app/Event/AuthSuccessEvent.php', + 'Kanboard\\Event\\CommentEvent' => __DIR__ . '/../..' . '/app/Event/CommentEvent.php', + 'Kanboard\\Event\\GenericEvent' => __DIR__ . '/../..' . '/app/Event/GenericEvent.php', + 'Kanboard\\Event\\ProjectFileEvent' => __DIR__ . '/../..' . '/app/Event/ProjectFileEvent.php', + 'Kanboard\\Event\\SubtaskEvent' => __DIR__ . '/../..' . '/app/Event/SubtaskEvent.php', + 'Kanboard\\Event\\TaskEvent' => __DIR__ . '/../..' . '/app/Event/TaskEvent.php', + 'Kanboard\\Event\\TaskFileEvent' => __DIR__ . '/../..' . '/app/Event/TaskFileEvent.php', + 'Kanboard\\Event\\TaskLinkEvent' => __DIR__ . '/../..' . '/app/Event/TaskLinkEvent.php', + 'Kanboard\\Event\\TaskListEvent' => __DIR__ . '/../..' . '/app/Event/TaskListEvent.php', + 'Kanboard\\Event\\UserProfileSyncEvent' => __DIR__ . '/../..' . '/app/Event/UserProfileSyncEvent.php', + 'Kanboard\\Export\\SubtaskExport' => __DIR__ . '/../..' . '/app/Export/SubtaskExport.php', + 'Kanboard\\Export\\TaskExport' => __DIR__ . '/../..' . '/app/Export/TaskExport.php', + 'Kanboard\\Export\\TransitionExport' => __DIR__ . '/../..' . '/app/Export/TransitionExport.php', + 'Kanboard\\ExternalLink\\AttachmentLink' => __DIR__ . '/../..' . '/app/ExternalLink/AttachmentLink.php', + 'Kanboard\\ExternalLink\\AttachmentLinkProvider' => __DIR__ . '/../..' . '/app/ExternalLink/AttachmentLinkProvider.php', + 'Kanboard\\ExternalLink\\BaseLink' => __DIR__ . '/../..' . '/app/ExternalLink/BaseLink.php', + 'Kanboard\\ExternalLink\\BaseLinkProvider' => __DIR__ . '/../..' . '/app/ExternalLink/BaseLinkProvider.php', + 'Kanboard\\ExternalLink\\FileLink' => __DIR__ . '/../..' . '/app/ExternalLink/FileLink.php', + 'Kanboard\\ExternalLink\\FileLinkProvider' => __DIR__ . '/../..' . '/app/ExternalLink/FileLinkProvider.php', + 'Kanboard\\ExternalLink\\WebLink' => __DIR__ . '/../..' . '/app/ExternalLink/WebLink.php', + 'Kanboard\\ExternalLink\\WebLinkProvider' => __DIR__ . '/../..' . '/app/ExternalLink/WebLinkProvider.php', + 'Kanboard\\Filter\\BaseComparisonFilter' => __DIR__ . '/../..' . '/app/Filter/BaseComparisonFilter.php', + 'Kanboard\\Filter\\BaseDateFilter' => __DIR__ . '/../..' . '/app/Filter/BaseDateFilter.php', + 'Kanboard\\Filter\\BaseFilter' => __DIR__ . '/../..' . '/app/Filter/BaseFilter.php', + 'Kanboard\\Filter\\ProjectActivityCreationDateFilter' => __DIR__ . '/../..' . '/app/Filter/ProjectActivityCreationDateFilter.php', + 'Kanboard\\Filter\\ProjectActivityCreatorFilter' => __DIR__ . '/../..' . '/app/Filter/ProjectActivityCreatorFilter.php', + 'Kanboard\\Filter\\ProjectActivityProjectIdFilter' => __DIR__ . '/../..' . '/app/Filter/ProjectActivityProjectIdFilter.php', + 'Kanboard\\Filter\\ProjectActivityProjectIdsFilter' => __DIR__ . '/../..' . '/app/Filter/ProjectActivityProjectIdsFilter.php', + 'Kanboard\\Filter\\ProjectActivityProjectNameFilter' => __DIR__ . '/../..' . '/app/Filter/ProjectActivityProjectNameFilter.php', + 'Kanboard\\Filter\\ProjectActivityTaskIdFilter' => __DIR__ . '/../..' . '/app/Filter/ProjectActivityTaskIdFilter.php', + 'Kanboard\\Filter\\ProjectActivityTaskStatusFilter' => __DIR__ . '/../..' . '/app/Filter/ProjectActivityTaskStatusFilter.php', + 'Kanboard\\Filter\\ProjectActivityTaskTitleFilter' => __DIR__ . '/../..' . '/app/Filter/ProjectActivityTaskTitleFilter.php', + 'Kanboard\\Filter\\ProjectGroupRoleProjectFilter' => __DIR__ . '/../..' . '/app/Filter/ProjectGroupRoleProjectFilter.php', + 'Kanboard\\Filter\\ProjectGroupRoleUsernameFilter' => __DIR__ . '/../..' . '/app/Filter/ProjectGroupRoleUsernameFilter.php', + 'Kanboard\\Filter\\ProjectIdsFilter' => __DIR__ . '/../..' . '/app/Filter/ProjectIdsFilter.php', + 'Kanboard\\Filter\\ProjectStatusFilter' => __DIR__ . '/../..' . '/app/Filter/ProjectStatusFilter.php', + 'Kanboard\\Filter\\ProjectTypeFilter' => __DIR__ . '/../..' . '/app/Filter/ProjectTypeFilter.php', + 'Kanboard\\Filter\\ProjectUserRoleProjectFilter' => __DIR__ . '/../..' . '/app/Filter/ProjectUserRoleProjectFilter.php', + 'Kanboard\\Filter\\ProjectUserRoleUsernameFilter' => __DIR__ . '/../..' . '/app/Filter/ProjectUserRoleUsernameFilter.php', + 'Kanboard\\Filter\\TaskAssigneeFilter' => __DIR__ . '/../..' . '/app/Filter/TaskAssigneeFilter.php', + 'Kanboard\\Filter\\TaskCategoryFilter' => __DIR__ . '/../..' . '/app/Filter/TaskCategoryFilter.php', + 'Kanboard\\Filter\\TaskColorFilter' => __DIR__ . '/../..' . '/app/Filter/TaskColorFilter.php', + 'Kanboard\\Filter\\TaskColumnFilter' => __DIR__ . '/../..' . '/app/Filter/TaskColumnFilter.php', + 'Kanboard\\Filter\\TaskCommentFilter' => __DIR__ . '/../..' . '/app/Filter/TaskCommentFilter.php', + 'Kanboard\\Filter\\TaskCompletionDateFilter' => __DIR__ . '/../..' . '/app/Filter/TaskCompletionDateFilter.php', + 'Kanboard\\Filter\\TaskCreationDateFilter' => __DIR__ . '/../..' . '/app/Filter/TaskCreationDateFilter.php', + 'Kanboard\\Filter\\TaskCreatorFilter' => __DIR__ . '/../..' . '/app/Filter/TaskCreatorFilter.php', + 'Kanboard\\Filter\\TaskDescriptionFilter' => __DIR__ . '/../..' . '/app/Filter/TaskDescriptionFilter.php', + 'Kanboard\\Filter\\TaskDueDateFilter' => __DIR__ . '/../..' . '/app/Filter/TaskDueDateFilter.php', + 'Kanboard\\Filter\\TaskDueDateRangeFilter' => __DIR__ . '/../..' . '/app/Filter/TaskDueDateRangeFilter.php', + 'Kanboard\\Filter\\TaskIdExclusionFilter' => __DIR__ . '/../..' . '/app/Filter/TaskIdExclusionFilter.php', + 'Kanboard\\Filter\\TaskIdFilter' => __DIR__ . '/../..' . '/app/Filter/TaskIdFilter.php', + 'Kanboard\\Filter\\TaskLinkFilter' => __DIR__ . '/../..' . '/app/Filter/TaskLinkFilter.php', + 'Kanboard\\Filter\\TaskModificationDateFilter' => __DIR__ . '/../..' . '/app/Filter/TaskModificationDateFilter.php', + 'Kanboard\\Filter\\TaskMovedDateFilter' => __DIR__ . '/../..' . '/app/Filter/TaskMovedDateFilter.php', + 'Kanboard\\Filter\\TaskPriorityFilter' => __DIR__ . '/../..' . '/app/Filter/TaskPriorityFilter.php', + 'Kanboard\\Filter\\TaskProjectFilter' => __DIR__ . '/../..' . '/app/Filter/TaskProjectFilter.php', + 'Kanboard\\Filter\\TaskProjectsFilter' => __DIR__ . '/../..' . '/app/Filter/TaskProjectsFilter.php', + 'Kanboard\\Filter\\TaskReferenceFilter' => __DIR__ . '/../..' . '/app/Filter/TaskReferenceFilter.php', + 'Kanboard\\Filter\\TaskScoreFilter' => __DIR__ . '/../..' . '/app/Filter/TaskScoreFilter.php', + 'Kanboard\\Filter\\TaskStartDateFilter' => __DIR__ . '/../..' . '/app/Filter/TaskStartDateFilter.php', + 'Kanboard\\Filter\\TaskStartsWithIdFilter' => __DIR__ . '/../..' . '/app/Filter/TaskStartsWithIdFilter.php', + 'Kanboard\\Filter\\TaskStatusFilter' => __DIR__ . '/../..' . '/app/Filter/TaskStatusFilter.php', + 'Kanboard\\Filter\\TaskSubtaskAssigneeFilter' => __DIR__ . '/../..' . '/app/Filter/TaskSubtaskAssigneeFilter.php', + 'Kanboard\\Filter\\TaskSwimlaneFilter' => __DIR__ . '/../..' . '/app/Filter/TaskSwimlaneFilter.php', + 'Kanboard\\Filter\\TaskTagFilter' => __DIR__ . '/../..' . '/app/Filter/TaskTagFilter.php', + 'Kanboard\\Filter\\TaskTitleFilter' => __DIR__ . '/../..' . '/app/Filter/TaskTitleFilter.php', + 'Kanboard\\Filter\\UserNameFilter' => __DIR__ . '/../..' . '/app/Filter/UserNameFilter.php', + 'Kanboard\\Formatter\\BaseFormatter' => __DIR__ . '/../..' . '/app/Formatter/BaseFormatter.php', + 'Kanboard\\Formatter\\BaseTaskCalendarFormatter' => __DIR__ . '/../..' . '/app/Formatter/BaseTaskCalendarFormatter.php', + 'Kanboard\\Formatter\\BoardColumnFormatter' => __DIR__ . '/../..' . '/app/Formatter/BoardColumnFormatter.php', + 'Kanboard\\Formatter\\BoardFormatter' => __DIR__ . '/../..' . '/app/Formatter/BoardFormatter.php', + 'Kanboard\\Formatter\\BoardSwimlaneFormatter' => __DIR__ . '/../..' . '/app/Formatter/BoardSwimlaneFormatter.php', + 'Kanboard\\Formatter\\BoardTaskFormatter' => __DIR__ . '/../..' . '/app/Formatter/BoardTaskFormatter.php', + 'Kanboard\\Formatter\\GroupAutoCompleteFormatter' => __DIR__ . '/../..' . '/app/Formatter/GroupAutoCompleteFormatter.php', + 'Kanboard\\Formatter\\ProjectActivityEventFormatter' => __DIR__ . '/../..' . '/app/Formatter/ProjectActivityEventFormatter.php', + 'Kanboard\\Formatter\\ProjectApiFormatter' => __DIR__ . '/../..' . '/app/Formatter/ProjectApiApiFormatter.php', + 'Kanboard\\Formatter\\ProjectsApiFormatter' => __DIR__ . '/../..' . '/app/Formatter/ProjectsApiFormatter.php', + 'Kanboard\\Formatter\\SubtaskListFormatter' => __DIR__ . '/../..' . '/app/Formatter/SubtaskListFormatter.php', + 'Kanboard\\Formatter\\SubtaskTimeTrackingCalendarFormatter' => __DIR__ . '/../..' . '/app/Formatter/SubtaskTimeTrackingCalendarFormatter.php', + 'Kanboard\\Formatter\\TaskApiFormatter' => __DIR__ . '/../..' . '/app/Formatter/TaskApiFormatter.php', + 'Kanboard\\Formatter\\TaskAutoCompleteFormatter' => __DIR__ . '/../..' . '/app/Formatter/TaskAutoCompleteFormatter.php', + 'Kanboard\\Formatter\\TaskICalFormatter' => __DIR__ . '/../..' . '/app/Formatter/TaskICalFormatter.php', + 'Kanboard\\Formatter\\TaskListFormatter' => __DIR__ . '/../..' . '/app/Formatter/TaskListFormatter.php', + 'Kanboard\\Formatter\\TaskListSubtaskAssigneeFormatter' => __DIR__ . '/../..' . '/app/Formatter/TaskListSubtaskAssigneeFormatter.php', + 'Kanboard\\Formatter\\TaskListSubtaskFormatter' => __DIR__ . '/../..' . '/app/Formatter/TaskListSubtaskFormatter.php', + 'Kanboard\\Formatter\\TaskSuggestMenuFormatter' => __DIR__ . '/../..' . '/app/Formatter/TaskSuggestMenuFormatter.php', + 'Kanboard\\Formatter\\TasksApiFormatter' => __DIR__ . '/../..' . '/app/Formatter/TasksApiFormatter.php', + 'Kanboard\\Formatter\\UserAutoCompleteFormatter' => __DIR__ . '/../..' . '/app/Formatter/UserAutoCompleteFormatter.php', + 'Kanboard\\Formatter\\UserMentionFormatter' => __DIR__ . '/../..' . '/app/Formatter/UserMentionFormatter.php', + 'Kanboard\\Group\\DatabaseBackendGroupProvider' => __DIR__ . '/../..' . '/app/Group/DatabaseBackendGroupProvider.php', + 'Kanboard\\Group\\DatabaseGroupProvider' => __DIR__ . '/../..' . '/app/Group/DatabaseGroupProvider.php', + 'Kanboard\\Group\\LdapBackendGroupProvider' => __DIR__ . '/../..' . '/app/Group/LdapBackendGroupProvider.php', + 'Kanboard\\Group\\LdapGroupProvider' => __DIR__ . '/../..' . '/app/Group/LdapGroupProvider.php', + 'Kanboard\\Helper\\AppHelper' => __DIR__ . '/../..' . '/app/Helper/AppHelper.php', + 'Kanboard\\Helper\\AssetHelper' => __DIR__ . '/../..' . '/app/Helper/AssetHelper.php', + 'Kanboard\\Helper\\AvatarHelper' => __DIR__ . '/../..' . '/app/Helper/AvatarHelper.php', + 'Kanboard\\Helper\\BoardHelper' => __DIR__ . '/../..' . '/app/Helper/BoardHelper.php', + 'Kanboard\\Helper\\CommentHelper' => __DIR__ . '/../..' . '/app/Helper/CommentHelper.php', + 'Kanboard\\Helper\\DateHelper' => __DIR__ . '/../..' . '/app/Helper/DateHelper.php', + 'Kanboard\\Helper\\FileHelper' => __DIR__ . '/../..' . '/app/Helper/FileHelper.php', + 'Kanboard\\Helper\\FormHelper' => __DIR__ . '/../..' . '/app/Helper/FormHelper.php', + 'Kanboard\\Helper\\HookHelper' => __DIR__ . '/../..' . '/app/Helper/HookHelper.php', + 'Kanboard\\Helper\\ICalHelper' => __DIR__ . '/../..' . '/app/Helper/ICalHelper.php', + 'Kanboard\\Helper\\LayoutHelper' => __DIR__ . '/../..' . '/app/Helper/LayoutHelper.php', + 'Kanboard\\Helper\\MailHelper' => __DIR__ . '/../..' . '/app/Helper/MailHelper.php', + 'Kanboard\\Helper\\ModalHelper' => __DIR__ . '/../..' . '/app/Helper/ModalHelper.php', + 'Kanboard\\Helper\\ModelHelper' => __DIR__ . '/../..' . '/app/Helper/ModelHelper.php', + 'Kanboard\\Helper\\ProjectActivityHelper' => __DIR__ . '/../..' . '/app/Helper/ProjectActivityHelper.php', + 'Kanboard\\Helper\\ProjectHeaderHelper' => __DIR__ . '/../..' . '/app/Helper/ProjectHeaderHelper.php', + 'Kanboard\\Helper\\ProjectRoleHelper' => __DIR__ . '/../..' . '/app/Helper/ProjectRoleHelper.php', + 'Kanboard\\Helper\\SubtaskHelper' => __DIR__ . '/../..' . '/app/Helper/SubtaskHelper.php', + 'Kanboard\\Helper\\TaskHelper' => __DIR__ . '/../..' . '/app/Helper/TaskHelper.php', + 'Kanboard\\Helper\\TextHelper' => __DIR__ . '/../..' . '/app/Helper/TextHelper.php', + 'Kanboard\\Helper\\UrlHelper' => __DIR__ . '/../..' . '/app/Helper/UrlHelper.php', + 'Kanboard\\Helper\\UserHelper' => __DIR__ . '/../..' . '/app/Helper/UserHelper.php', + 'Kanboard\\Import\\TaskImport' => __DIR__ . '/../..' . '/app/Import/TaskImport.php', + 'Kanboard\\Import\\UserImport' => __DIR__ . '/../..' . '/app/Import/UserImport.php', + 'Kanboard\\Job\\BaseJob' => __DIR__ . '/../..' . '/app/Job/BaseJob.php', + 'Kanboard\\Job\\CommentEventJob' => __DIR__ . '/../..' . '/app/Job/CommentEventJob.php', + 'Kanboard\\Job\\EmailJob' => __DIR__ . '/../..' . '/app/Job/EmailJob.php', + 'Kanboard\\Job\\HttpAsyncJob' => __DIR__ . '/../..' . '/app/Job/HttpAsyncJob.php', + 'Kanboard\\Job\\NotificationJob' => __DIR__ . '/../..' . '/app/Job/NotificationJob.php', + 'Kanboard\\Job\\ProjectFileEventJob' => __DIR__ . '/../..' . '/app/Job/ProjectFileEventJob.php', + 'Kanboard\\Job\\ProjectMetricJob' => __DIR__ . '/../..' . '/app/Job/ProjectMetricJob.php', + 'Kanboard\\Job\\SubtaskEventJob' => __DIR__ . '/../..' . '/app/Job/SubtaskEventJob.php', + 'Kanboard\\Job\\TaskEventJob' => __DIR__ . '/../..' . '/app/Job/TaskEventJob.php', + 'Kanboard\\Job\\TaskFileEventJob' => __DIR__ . '/../..' . '/app/Job/TaskFileEventJob.php', + 'Kanboard\\Job\\TaskLinkEventJob' => __DIR__ . '/../..' . '/app/Job/TaskLinkEventJob.php', + 'Kanboard\\Job\\UserMentionJob' => __DIR__ . '/../..' . '/app/Job/UserMentionJob.php', + 'Kanboard\\Middleware\\ApplicationAuthorizationMiddleware' => __DIR__ . '/../..' . '/app/Middleware/ApplicationAuthorizationMiddleware.php', + 'Kanboard\\Middleware\\AuthenticationMiddleware' => __DIR__ . '/../..' . '/app/Middleware/AuthenticationMiddleware.php', + 'Kanboard\\Middleware\\BootstrapMiddleware' => __DIR__ . '/../..' . '/app/Middleware/BootstrapMiddleware.php', + 'Kanboard\\Middleware\\PostAuthenticationMiddleware' => __DIR__ . '/../..' . '/app/Middleware/PostAuthenticationMiddleware.php', + 'Kanboard\\Middleware\\ProjectAuthorizationMiddleware' => __DIR__ . '/../..' . '/app/Middleware/ProjectAuthorizationMiddleware.php', + 'Kanboard\\Model\\ActionModel' => __DIR__ . '/../..' . '/app/Model/ActionModel.php', + 'Kanboard\\Model\\ActionParameterModel' => __DIR__ . '/../..' . '/app/Model/ActionParameterModel.php', + 'Kanboard\\Model\\AvatarFileModel' => __DIR__ . '/../..' . '/app/Model/AvatarFileModel.php', + 'Kanboard\\Model\\BoardModel' => __DIR__ . '/../..' . '/app/Model/BoardModel.php', + 'Kanboard\\Model\\CategoryModel' => __DIR__ . '/../..' . '/app/Model/CategoryModel.php', + 'Kanboard\\Model\\ColorModel' => __DIR__ . '/../..' . '/app/Model/ColorModel.php', + 'Kanboard\\Model\\ColumnModel' => __DIR__ . '/../..' . '/app/Model/ColumnModel.php', + 'Kanboard\\Model\\ColumnMoveRestrictionModel' => __DIR__ . '/../..' . '/app/Model/ColumnMoveRestrictionModel.php', + 'Kanboard\\Model\\ColumnRestrictionModel' => __DIR__ . '/../..' . '/app/Model/ColumnRestrictionModel.php', + 'Kanboard\\Model\\CommentModel' => __DIR__ . '/../..' . '/app/Model/CommentModel.php', + 'Kanboard\\Model\\ConfigModel' => __DIR__ . '/../..' . '/app/Model/ConfigModel.php', + 'Kanboard\\Model\\CurrencyModel' => __DIR__ . '/../..' . '/app/Model/CurrencyModel.php', + 'Kanboard\\Model\\CustomFilterModel' => __DIR__ . '/../..' . '/app/Model/CustomFilterModel.php', + 'Kanboard\\Model\\FileModel' => __DIR__ . '/../..' . '/app/Model/FileModel.php', + 'Kanboard\\Model\\GroupMemberModel' => __DIR__ . '/../..' . '/app/Model/GroupMemberModel.php', + 'Kanboard\\Model\\GroupModel' => __DIR__ . '/../..' . '/app/Model/GroupModel.php', + 'Kanboard\\Model\\InviteModel' => __DIR__ . '/../..' . '/app/Model/InviteModel.php', + 'Kanboard\\Model\\LanguageModel' => __DIR__ . '/../..' . '/app/Model/LanguageModel.php', + 'Kanboard\\Model\\LastLoginModel' => __DIR__ . '/../..' . '/app/Model/LastLoginModel.php', + 'Kanboard\\Model\\LinkModel' => __DIR__ . '/../..' . '/app/Model/LinkModel.php', + 'Kanboard\\Model\\MetadataModel' => __DIR__ . '/../..' . '/app/Model/MetadataModel.php', + 'Kanboard\\Model\\NotificationModel' => __DIR__ . '/../..' . '/app/Model/NotificationModel.php', + 'Kanboard\\Model\\NotificationTypeModel' => __DIR__ . '/../..' . '/app/Model/NotificationTypeModel.php', + 'Kanboard\\Model\\PasswordResetModel' => __DIR__ . '/../..' . '/app/Model/PasswordResetModel.php', + 'Kanboard\\Model\\ProjectActivityModel' => __DIR__ . '/../..' . '/app/Model/ProjectActivityModel.php', + 'Kanboard\\Model\\ProjectDailyColumnStatsModel' => __DIR__ . '/../..' . '/app/Model/ProjectDailyColumnStatsModel.php', + 'Kanboard\\Model\\ProjectDailyStatsModel' => __DIR__ . '/../..' . '/app/Model/ProjectDailyStatsModel.php', + 'Kanboard\\Model\\ProjectDuplicationModel' => __DIR__ . '/../..' . '/app/Model/ProjectDuplicationModel.php', + 'Kanboard\\Model\\ProjectFileModel' => __DIR__ . '/../..' . '/app/Model/ProjectFileModel.php', + 'Kanboard\\Model\\ProjectGroupRoleModel' => __DIR__ . '/../..' . '/app/Model/ProjectGroupRoleModel.php', + 'Kanboard\\Model\\ProjectMetadataModel' => __DIR__ . '/../..' . '/app/Model/ProjectMetadataModel.php', + 'Kanboard\\Model\\ProjectModel' => __DIR__ . '/../..' . '/app/Model/ProjectModel.php', + 'Kanboard\\Model\\ProjectNotificationModel' => __DIR__ . '/../..' . '/app/Model/ProjectNotificationModel.php', + 'Kanboard\\Model\\ProjectNotificationTypeModel' => __DIR__ . '/../..' . '/app/Model/ProjectNotificationTypeModel.php', + 'Kanboard\\Model\\ProjectPermissionModel' => __DIR__ . '/../..' . '/app/Model/ProjectPermissionModel.php', + 'Kanboard\\Model\\ProjectRoleModel' => __DIR__ . '/../..' . '/app/Model/ProjectRoleModel.php', + 'Kanboard\\Model\\ProjectRoleRestrictionModel' => __DIR__ . '/../..' . '/app/Model/ProjectRoleRestrictionModel.php', + 'Kanboard\\Model\\ProjectTaskDuplicationModel' => __DIR__ . '/../..' . '/app/Model/ProjectTaskDuplicationModel.php', + 'Kanboard\\Model\\ProjectTaskPriorityModel' => __DIR__ . '/../..' . '/app/Model/ProjectTaskPriorityModel.php', + 'Kanboard\\Model\\ProjectUserRoleModel' => __DIR__ . '/../..' . '/app/Model/ProjectUserRoleModel.php', + 'Kanboard\\Model\\RememberMeSessionModel' => __DIR__ . '/../..' . '/app/Model/RememberMeSessionModel.php', + 'Kanboard\\Model\\SettingModel' => __DIR__ . '/../..' . '/app/Model/SettingModel.php', + 'Kanboard\\Model\\SubtaskModel' => __DIR__ . '/../..' . '/app/Model/SubtaskModel.php', + 'Kanboard\\Model\\SubtaskPositionModel' => __DIR__ . '/../..' . '/app/Model/SubtaskPositionModel.php', + 'Kanboard\\Model\\SubtaskStatusModel' => __DIR__ . '/../..' . '/app/Model/SubtaskStatusModel.php', + 'Kanboard\\Model\\SubtaskTaskConversionModel' => __DIR__ . '/../..' . '/app/Model/SubtaskTaskConversionModel.php', + 'Kanboard\\Model\\SubtaskTimeTrackingModel' => __DIR__ . '/../..' . '/app/Model/SubtaskTimeTrackingModel.php', + 'Kanboard\\Model\\SwimlaneModel' => __DIR__ . '/../..' . '/app/Model/SwimlaneModel.php', + 'Kanboard\\Model\\TagDuplicationModel' => __DIR__ . '/../..' . '/app/Model/TagDuplicationModel.php', + 'Kanboard\\Model\\TagModel' => __DIR__ . '/../..' . '/app/Model/TagModel.php', + 'Kanboard\\Model\\TaskAnalyticModel' => __DIR__ . '/../..' . '/app/Model/TaskAnalyticModel.php', + 'Kanboard\\Model\\TaskCreationModel' => __DIR__ . '/../..' . '/app/Model/TaskCreationModel.php', + 'Kanboard\\Model\\TaskDuplicationModel' => __DIR__ . '/../..' . '/app/Model/TaskDuplicationModel.php', + 'Kanboard\\Model\\TaskExternalLinkModel' => __DIR__ . '/../..' . '/app/Model/TaskExternalLinkModel.php', + 'Kanboard\\Model\\TaskFileModel' => __DIR__ . '/../..' . '/app/Model/TaskFileModel.php', + 'Kanboard\\Model\\TaskFinderModel' => __DIR__ . '/../..' . '/app/Model/TaskFinderModel.php', + 'Kanboard\\Model\\TaskLinkModel' => __DIR__ . '/../..' . '/app/Model/TaskLinkModel.php', + 'Kanboard\\Model\\TaskMetadataModel' => __DIR__ . '/../..' . '/app/Model/TaskMetadataModel.php', + 'Kanboard\\Model\\TaskModel' => __DIR__ . '/../..' . '/app/Model/TaskModel.php', + 'Kanboard\\Model\\TaskModificationModel' => __DIR__ . '/../..' . '/app/Model/TaskModificationModel.php', + 'Kanboard\\Model\\TaskPositionModel' => __DIR__ . '/../..' . '/app/Model/TaskPositionModel.php', + 'Kanboard\\Model\\TaskProjectDuplicationModel' => __DIR__ . '/../..' . '/app/Model/TaskProjectDuplicationModel.php', + 'Kanboard\\Model\\TaskProjectMoveModel' => __DIR__ . '/../..' . '/app/Model/TaskProjectMoveModel.php', + 'Kanboard\\Model\\TaskRecurrenceModel' => __DIR__ . '/../..' . '/app/Model/TaskRecurrenceModel.php', + 'Kanboard\\Model\\TaskStatusModel' => __DIR__ . '/../..' . '/app/Model/TaskStatusModel.php', + 'Kanboard\\Model\\TaskTagModel' => __DIR__ . '/../..' . '/app/Model/TaskTagModel.php', + 'Kanboard\\Model\\TimezoneModel' => __DIR__ . '/../..' . '/app/Model/TimezoneModel.php', + 'Kanboard\\Model\\TransitionModel' => __DIR__ . '/../..' . '/app/Model/TransitionModel.php', + 'Kanboard\\Model\\UserLockingModel' => __DIR__ . '/../..' . '/app/Model/UserLockingModel.php', + 'Kanboard\\Model\\UserMetadataModel' => __DIR__ . '/../..' . '/app/Model/UserMetadataModel.php', + 'Kanboard\\Model\\UserModel' => __DIR__ . '/../..' . '/app/Model/UserModel.php', + 'Kanboard\\Model\\UserNotificationFilterModel' => __DIR__ . '/../..' . '/app/Model/UserNotificationFilterModel.php', + 'Kanboard\\Model\\UserNotificationModel' => __DIR__ . '/../..' . '/app/Model/UserNotificationModel.php', + 'Kanboard\\Model\\UserNotificationTypeModel' => __DIR__ . '/../..' . '/app/Model/UserNotificationTypeModel.php', + 'Kanboard\\Model\\UserUnreadNotificationModel' => __DIR__ . '/../..' . '/app/Model/UserUnreadNotificationModel.php', + 'Kanboard\\Notification\\ActivityStreamNotification' => __DIR__ . '/../..' . '/app/Notification/ActivityStreamNotification.php', + 'Kanboard\\Notification\\MailNotification' => __DIR__ . '/../..' . '/app/Notification/MailNotification.php', + 'Kanboard\\Notification\\WebNotification' => __DIR__ . '/../..' . '/app/Notification/WebNotification.php', + 'Kanboard\\Notification\\WebhookNotification' => __DIR__ . '/../..' . '/app/Notification/WebhookNotification.php', + 'Kanboard\\Pagination\\DashboardPagination' => __DIR__ . '/../..' . '/app/Pagination/DashboardPagination.php', + 'Kanboard\\Pagination\\ProjectPagination' => __DIR__ . '/../..' . '/app/Pagination/ProjectPagination.php', + 'Kanboard\\Pagination\\SubtaskPagination' => __DIR__ . '/../..' . '/app/Pagination/SubtaskPagination.php', + 'Kanboard\\Pagination\\TaskPagination' => __DIR__ . '/../..' . '/app/Pagination/TaskPagination.php', + 'Kanboard\\Pagination\\UserPagination' => __DIR__ . '/../..' . '/app/Pagination/UserPagination.php', + 'Kanboard\\ServiceProvider\\ActionProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/ActionProvider.php', + 'Kanboard\\ServiceProvider\\ApiProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/ApiProvider.php', + 'Kanboard\\ServiceProvider\\AuthenticationProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/AuthenticationProvider.php', + 'Kanboard\\ServiceProvider\\AvatarProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/AvatarProvider.php', + 'Kanboard\\ServiceProvider\\CacheProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/CacheProvider.php', + 'Kanboard\\ServiceProvider\\ClassProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/ClassProvider.php', + 'Kanboard\\ServiceProvider\\CommandProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/CommandProvider.php', + 'Kanboard\\ServiceProvider\\DatabaseProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/DatabaseProvider.php', + 'Kanboard\\ServiceProvider\\EventDispatcherProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/EventDispatcherProvider.php', + 'Kanboard\\ServiceProvider\\ExternalLinkProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/ExternalLinkProvider.php', + 'Kanboard\\ServiceProvider\\ExternalTaskProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/ExternalTaskProvider.php', + 'Kanboard\\ServiceProvider\\FilterProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/FilterProvider.php', + 'Kanboard\\ServiceProvider\\FormatterProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/FormatterProvider.php', + 'Kanboard\\ServiceProvider\\GroupProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/GroupProvider.php', + 'Kanboard\\ServiceProvider\\HelperProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/HelperProvider.php', + 'Kanboard\\ServiceProvider\\JobProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/JobProvider.php', + 'Kanboard\\ServiceProvider\\LoggingProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/LoggingProvider.php', + 'Kanboard\\ServiceProvider\\MailProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/MailProvider.php', + 'Kanboard\\ServiceProvider\\NotificationProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/NotificationProvider.php', + 'Kanboard\\ServiceProvider\\ObjectStorageProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/ObjectStorageProvider.php', + 'Kanboard\\ServiceProvider\\PluginProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/PluginProvider.php', + 'Kanboard\\ServiceProvider\\QueueProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/QueueProvider.php', + 'Kanboard\\ServiceProvider\\RouteProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/RouteProvider.php', + 'Kanboard\\ServiceProvider\\SessionProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/SessionProvider.php', + 'Kanboard\\Subscriber\\AuthSubscriber' => __DIR__ . '/../..' . '/app/Subscriber/AuthSubscriber.php', + 'Kanboard\\Subscriber\\BaseSubscriber' => __DIR__ . '/../..' . '/app/Subscriber/BaseSubscriber.php', + 'Kanboard\\Subscriber\\BootstrapSubscriber' => __DIR__ . '/../..' . '/app/Subscriber/BootstrapSubscriber.php', + 'Kanboard\\Subscriber\\LdapUserPhotoSubscriber' => __DIR__ . '/../..' . '/app/Subscriber/LdapUserPhotoSubscriber.php', + 'Kanboard\\Subscriber\\NotificationSubscriber' => __DIR__ . '/../..' . '/app/Subscriber/NotificationSubscriber.php', + 'Kanboard\\Subscriber\\ProjectDailySummarySubscriber' => __DIR__ . '/../..' . '/app/Subscriber/ProjectDailySummarySubscriber.php', + 'Kanboard\\Subscriber\\ProjectModificationDateSubscriber' => __DIR__ . '/../..' . '/app/Subscriber/ProjectModificationDateSubscriber.php', + 'Kanboard\\Subscriber\\RecurringTaskSubscriber' => __DIR__ . '/../..' . '/app/Subscriber/RecurringTaskSubscriber.php', + 'Kanboard\\Subscriber\\TransitionSubscriber' => __DIR__ . '/../..' . '/app/Subscriber/TransitionSubscriber.php', + 'Kanboard\\User\\Avatar\\AvatarFileProvider' => __DIR__ . '/../..' . '/app/User/Avatar/AvatarFileProvider.php', + 'Kanboard\\User\\Avatar\\LetterAvatarProvider' => __DIR__ . '/../..' . '/app/User/Avatar/LetterAvatarProvider.php', + 'Kanboard\\User\\DatabaseUserProvider' => __DIR__ . '/../..' . '/app/User/DatabaseUserProvider.php', + 'Kanboard\\User\\LdapUserProvider' => __DIR__ . '/../..' . '/app/User/LdapUserProvider.php', + 'Kanboard\\User\\OAuthUserProvider' => __DIR__ . '/../..' . '/app/User/OAuthUserProvider.php', + 'Kanboard\\User\\ReverseProxyUserProvider' => __DIR__ . '/../..' . '/app/User/ReverseProxyUserProvider.php', + 'Kanboard\\Validator\\ActionValidator' => __DIR__ . '/../..' . '/app/Validator/ActionValidator.php', + 'Kanboard\\Validator\\AuthValidator' => __DIR__ . '/../..' . '/app/Validator/AuthValidator.php', + 'Kanboard\\Validator\\BaseValidator' => __DIR__ . '/../..' . '/app/Validator/BaseValidator.php', + 'Kanboard\\Validator\\CategoryValidator' => __DIR__ . '/../..' . '/app/Validator/CategoryValidator.php', + 'Kanboard\\Validator\\ColumnMoveRestrictionValidator' => __DIR__ . '/../..' . '/app/Validator/ColumnMoveRestrictionValidator.php', + 'Kanboard\\Validator\\ColumnRestrictionValidator' => __DIR__ . '/../..' . '/app/Validator/ColumnRestrictionValidator.php', + 'Kanboard\\Validator\\ColumnValidator' => __DIR__ . '/../..' . '/app/Validator/ColumnValidator.php', + 'Kanboard\\Validator\\CommentValidator' => __DIR__ . '/../..' . '/app/Validator/CommentValidator.php', + 'Kanboard\\Validator\\CurrencyValidator' => __DIR__ . '/../..' . '/app/Validator/CurrencyValidator.php', + 'Kanboard\\Validator\\CustomFilterValidator' => __DIR__ . '/../..' . '/app/Validator/CustomFilterValidator.php', + 'Kanboard\\Validator\\ExternalLinkValidator' => __DIR__ . '/../..' . '/app/Validator/ExternalLinkValidator.php', + 'Kanboard\\Validator\\GroupValidator' => __DIR__ . '/../..' . '/app/Validator/GroupValidator.php', + 'Kanboard\\Validator\\LinkValidator' => __DIR__ . '/../..' . '/app/Validator/LinkValidator.php', + 'Kanboard\\Validator\\PasswordResetValidator' => __DIR__ . '/../..' . '/app/Validator/PasswordResetValidator.php', + 'Kanboard\\Validator\\ProjectRoleValidator' => __DIR__ . '/../..' . '/app/Validator/ProjectRoleValidator.php', + 'Kanboard\\Validator\\ProjectValidator' => __DIR__ . '/../..' . '/app/Validator/ProjectValidator.php', + 'Kanboard\\Validator\\SubtaskValidator' => __DIR__ . '/../..' . '/app/Validator/SubtaskValidator.php', + 'Kanboard\\Validator\\SwimlaneValidator' => __DIR__ . '/../..' . '/app/Validator/SwimlaneValidator.php', + 'Kanboard\\Validator\\TagValidator' => __DIR__ . '/../..' . '/app/Validator/TagValidator.php', + 'Kanboard\\Validator\\TaskLinkValidator' => __DIR__ . '/../..' . '/app/Validator/TaskLinkValidator.php', + 'Kanboard\\Validator\\TaskValidator' => __DIR__ . '/../..' . '/app/Validator/TaskValidator.php', + 'Kanboard\\Validator\\UserValidator' => __DIR__ . '/../..' . '/app/Validator/UserValidator.php', + 'Otp\\GoogleAuthenticator' => __DIR__ . '/..' . '/christian-riesen/otp/src/Otp/GoogleAuthenticator.php', + 'Otp\\Otp' => __DIR__ . '/..' . '/christian-riesen/otp/src/Otp/Otp.php', + 'Otp\\OtpInterface' => __DIR__ . '/..' . '/christian-riesen/otp/src/Otp/OtpInterface.php', + 'PHPQRCode' => __DIR__ . '/..' . '/aferrandini/phpqrcode/lib/PHPQRCode.php', + 'PHPQRCode\\Autoloader' => __DIR__ . '/..' . '/aferrandini/phpqrcode/lib/PHPQRCode/Autoloader.php', + 'PHPQRCode\\Constants' => __DIR__ . '/..' . '/aferrandini/phpqrcode/lib/PHPQRCode/Constants.php', + 'PHPQRCode\\FrameFiller' => __DIR__ . '/..' . '/aferrandini/phpqrcode/lib/PHPQRCode/FrameFiller.php', + 'PHPQRCode\\QRbitstream' => __DIR__ . '/..' . '/aferrandini/phpqrcode/lib/PHPQRCode/QRbitstream.php', + 'PHPQRCode\\QRcode' => __DIR__ . '/..' . '/aferrandini/phpqrcode/lib/PHPQRCode/QRcode.php', + 'PHPQRCode\\QRencode' => __DIR__ . '/..' . '/aferrandini/phpqrcode/lib/PHPQRCode/QRencode.php', + 'PHPQRCode\\QRimage' => __DIR__ . '/..' . '/aferrandini/phpqrcode/lib/PHPQRCode/QRimage.php', + 'PHPQRCode\\QRinput' => __DIR__ . '/..' . '/aferrandini/phpqrcode/lib/PHPQRCode/QRinput.php', + 'PHPQRCode\\QRinputItem' => __DIR__ . '/..' . '/aferrandini/phpqrcode/lib/PHPQRCode/QRinputItem.php', + 'PHPQRCode\\QRmask' => __DIR__ . '/..' . '/aferrandini/phpqrcode/lib/PHPQRCode/QRmask.php', + 'PHPQRCode\\QRrawcode' => __DIR__ . '/..' . '/aferrandini/phpqrcode/lib/PHPQRCode/QRrawcode.php', + 'PHPQRCode\\QRrs' => __DIR__ . '/..' . '/aferrandini/phpqrcode/lib/PHPQRCode/QRrs.php', + 'PHPQRCode\\QRrsItem' => __DIR__ . '/..' . '/aferrandini/phpqrcode/lib/PHPQRCode/QRrsItem.php', + 'PHPQRCode\\QRrsblock' => __DIR__ . '/..' . '/aferrandini/phpqrcode/lib/PHPQRCode/QRrsblock.php', + 'PHPQRCode\\QRspec' => __DIR__ . '/..' . '/aferrandini/phpqrcode/lib/PHPQRCode/QRspec.php', + 'PHPQRCode\\QRsplit' => __DIR__ . '/..' . '/aferrandini/phpqrcode/lib/PHPQRCode/QRsplit.php', + 'PHPQRCode\\QRstr' => __DIR__ . '/..' . '/aferrandini/phpqrcode/lib/PHPQRCode/QRstr.php', + 'PHPQRCode\\QRtools' => __DIR__ . '/..' . '/aferrandini/phpqrcode/lib/PHPQRCode/QRtools.php', + 'Parsedown' => __DIR__ . '/..' . '/erusev/parsedown/Parsedown.php', + 'ParsedownTest' => __DIR__ . '/..' . '/erusev/parsedown/test/ParsedownTest.php', + 'PicoDb\\Builder\\BaseBuilder' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Builder/BaseBuilder.php', + 'PicoDb\\Builder\\ConditionBuilder' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Builder/ConditionBuilder.php', + 'PicoDb\\Builder\\InsertBuilder' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Builder/InsertBuilder.php', + 'PicoDb\\Builder\\OrConditionBuilder' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Builder/OrConditionBuilder.php', + 'PicoDb\\Builder\\UpdateBuilder' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Builder/UpdateBuilder.php', + 'PicoDb\\Database' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Database.php', + 'PicoDb\\DriverFactory' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/DriverFactory.php', + 'PicoDb\\Driver\\Base' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Driver/Base.php', + 'PicoDb\\Driver\\Mssql' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Driver/Mssql.php', + 'PicoDb\\Driver\\Mysql' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Driver/Mysql.php', + 'PicoDb\\Driver\\Postgres' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Driver/Postgres.php', + 'PicoDb\\Driver\\Sqlite' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Driver/Sqlite.php', + 'PicoDb\\Hashtable' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Hashtable.php', + 'PicoDb\\LargeObject' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/LargeObject.php', + 'PicoDb\\SQLException' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/SQLException.php', + 'PicoDb\\Schema' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Schema.php', + 'PicoDb\\StatementHandler' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/StatementHandler.php', + 'PicoDb\\Table' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Table.php', + 'PicoDb\\UrlParser' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/UrlParser.php', + 'PicoFeed\\Base' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Base.php', + 'PicoFeed\\Client\\Client' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Client/Client.php', + 'PicoFeed\\Client\\ClientException' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Client/ClientException.php', + 'PicoFeed\\Client\\Curl' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Client/Curl.php', + 'PicoFeed\\Client\\ForbiddenException' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Client/ForbiddenException.php', + 'PicoFeed\\Client\\HttpHeaders' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Client/HttpHeaders.php', + 'PicoFeed\\Client\\InvalidCertificateException' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Client/InvalidCertificateException.php', + 'PicoFeed\\Client\\InvalidUrlException' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Client/InvalidUrlException.php', + 'PicoFeed\\Client\\MaxRedirectException' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Client/MaxRedirectException.php', + 'PicoFeed\\Client\\MaxSizeException' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Client/MaxSizeException.php', + 'PicoFeed\\Client\\Stream' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Client/Stream.php', + 'PicoFeed\\Client\\TimeoutException' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Client/TimeoutException.php', + 'PicoFeed\\Client\\UnauthorizedException' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Client/UnauthorizedException.php', + 'PicoFeed\\Client\\Url' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Client/Url.php', + 'PicoFeed\\Config\\Config' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Config/Config.php', + 'PicoFeed\\Encoding\\Encoding' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Encoding/Encoding.php', + 'PicoFeed\\Filter\\Attribute' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Filter/Attribute.php', + 'PicoFeed\\Filter\\Filter' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Filter/Filter.php', + 'PicoFeed\\Filter\\Html' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Filter/Html.php', + 'PicoFeed\\Filter\\Tag' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Filter/Tag.php', + 'PicoFeed\\Generator\\ContentGeneratorInterface' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Generator/ContentGeneratorInterface.php', + 'PicoFeed\\Generator\\FileContentGenerator' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Generator/FileContentGenerator.php', + 'PicoFeed\\Generator\\YoutubeContentGenerator' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Generator/YoutubeContentGenerator.php', + 'PicoFeed\\Logging\\Logger' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Logging/Logger.php', + 'PicoFeed\\Parser\\Atom' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Parser/Atom.php', + 'PicoFeed\\Parser\\DateParser' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Parser/DateParser.php', + 'PicoFeed\\Parser\\Feed' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Parser/Feed.php', + 'PicoFeed\\Parser\\Item' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Parser/Item.php', + 'PicoFeed\\Parser\\MalformedXmlException' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Parser/MalformedXmlException.php', + 'PicoFeed\\Parser\\Parser' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Parser/Parser.php', + 'PicoFeed\\Parser\\ParserException' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Parser/ParserException.php', + 'PicoFeed\\Parser\\ParserInterface' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Parser/ParserInterface.php', + 'PicoFeed\\Parser\\Rss10' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Parser/Rss10.php', + 'PicoFeed\\Parser\\Rss20' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Parser/Rss20.php', + 'PicoFeed\\Parser\\Rss91' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Parser/Rss91.php', + 'PicoFeed\\Parser\\Rss92' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Parser/Rss92.php', + 'PicoFeed\\Parser\\XmlEntityException' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Parser/XmlEntityException.php', + 'PicoFeed\\Parser\\XmlParser' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Parser/XmlParser.php', + 'PicoFeed\\PicoFeedException' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/PicoFeedException.php', + 'PicoFeed\\Processor\\ContentFilterProcessor' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Processor/ContentFilterProcessor.php', + 'PicoFeed\\Processor\\ContentGeneratorProcessor' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Processor/ContentGeneratorProcessor.php', + 'PicoFeed\\Processor\\ItemPostProcessor' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Processor/ItemPostProcessor.php', + 'PicoFeed\\Processor\\ItemProcessorInterface' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Processor/ItemProcessorInterface.php', + 'PicoFeed\\Processor\\ScraperProcessor' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Processor/ScraperProcessor.php', + 'PicoFeed\\Reader\\Favicon' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Reader/Favicon.php', + 'PicoFeed\\Reader\\Reader' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Reader/Reader.php', + 'PicoFeed\\Reader\\ReaderException' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Reader/ReaderException.php', + 'PicoFeed\\Reader\\SubscriptionNotFoundException' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Reader/SubscriptionNotFoundException.php', + 'PicoFeed\\Reader\\UnsupportedFeedFormatException' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Reader/UnsupportedFeedFormatException.php', + 'PicoFeed\\Scraper\\CandidateParser' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Scraper/CandidateParser.php', + 'PicoFeed\\Scraper\\ParserInterface' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Scraper/ParserInterface.php', + 'PicoFeed\\Scraper\\RuleLoader' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Scraper/RuleLoader.php', + 'PicoFeed\\Scraper\\RuleParser' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Scraper/RuleParser.php', + 'PicoFeed\\Scraper\\Scraper' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Scraper/Scraper.php', + 'PicoFeed\\Serialization\\Subscription' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Serialization/Subscription.php', + 'PicoFeed\\Serialization\\SubscriptionList' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionList.php', + 'PicoFeed\\Serialization\\SubscriptionListBuilder' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionListBuilder.php', + 'PicoFeed\\Serialization\\SubscriptionListParser' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionListParser.php', + 'PicoFeed\\Serialization\\SubscriptionParser' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionParser.php', + 'PicoFeed\\Syndication\\AtomFeedBuilder' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Syndication/AtomFeedBuilder.php', + 'PicoFeed\\Syndication\\AtomHelper' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Syndication/AtomHelper.php', + 'PicoFeed\\Syndication\\AtomItemBuilder' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Syndication/AtomItemBuilder.php', + 'PicoFeed\\Syndication\\FeedBuilder' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Syndication/FeedBuilder.php', + 'PicoFeed\\Syndication\\ItemBuilder' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Syndication/ItemBuilder.php', + 'PicoFeed\\Syndication\\Rss20FeedBuilder' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20FeedBuilder.php', + 'PicoFeed\\Syndication\\Rss20Helper' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20Helper.php', + 'PicoFeed\\Syndication\\Rss20ItemBuilder' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20ItemBuilder.php', + 'Pimple\\Container' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Container.php', + 'Pimple\\ServiceProviderInterface' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/ServiceProviderInterface.php', + 'Pimple\\Tests\\Fixtures\\Invokable' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Tests/Fixtures/Invokable.php', + 'Pimple\\Tests\\Fixtures\\NonInvokable' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Tests/Fixtures/NonInvokable.php', + 'Pimple\\Tests\\Fixtures\\PimpleServiceProvider' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Tests/Fixtures/PimpleServiceProvider.php', + 'Pimple\\Tests\\Fixtures\\Service' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Tests/Fixtures/Service.php', + 'Pimple\\Tests\\PimpleServiceProviderInterfaceTest' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Tests/PimpleServiceProviderInterfaceTest.php', + 'Pimple\\Tests\\PimpleTest' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Tests/PimpleTest.php', + 'Psr\\Log\\AbstractLogger' => __DIR__ . '/..' . '/psr/log/Psr/Log/AbstractLogger.php', + 'Psr\\Log\\InvalidArgumentException' => __DIR__ . '/..' . '/psr/log/Psr/Log/InvalidArgumentException.php', + 'Psr\\Log\\LogLevel' => __DIR__ . '/..' . '/psr/log/Psr/Log/LogLevel.php', + 'Psr\\Log\\LoggerAwareInterface' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerAwareInterface.php', + 'Psr\\Log\\LoggerAwareTrait' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerAwareTrait.php', + 'Psr\\Log\\LoggerInterface' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerInterface.php', + 'Psr\\Log\\LoggerTrait' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerTrait.php', + 'Psr\\Log\\NullLogger' => __DIR__ . '/..' . '/psr/log/Psr/Log/NullLogger.php', + 'Psr\\Log\\Test\\DummyTest' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php', + 'Psr\\Log\\Test\\LoggerInterfaceTest' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php', + 'SimpleLogger\\Base' => __DIR__ . '/..' . '/fguillot/simpleLogger/src/SimpleLogger/Base.php', + 'SimpleLogger\\File' => __DIR__ . '/..' . '/fguillot/simpleLogger/src/SimpleLogger/File.php', + 'SimpleLogger\\Logger' => __DIR__ . '/..' . '/fguillot/simpleLogger/src/SimpleLogger/Logger.php', + 'SimpleLogger\\Stderr' => __DIR__ . '/..' . '/fguillot/simpleLogger/src/SimpleLogger/Stderr.php', + 'SimpleLogger\\Stdout' => __DIR__ . '/..' . '/fguillot/simpleLogger/src/SimpleLogger/Stdout.php', + 'SimpleLogger\\Syslog' => __DIR__ . '/..' . '/fguillot/simpleLogger/src/SimpleLogger/Syslog.php', + 'SimpleQueue\\Adapter\\AmqpQueueAdapter' => __DIR__ . '/..' . '/fguillot/simple-queue/src/Adapter/AmqpQueueAdapter.php', + 'SimpleQueue\\Adapter\\AwsSqsQueueAdapter' => __DIR__ . '/..' . '/fguillot/simple-queue/src/Adapter/AwsSqsQueueAdapter.php', + 'SimpleQueue\\Adapter\\BeanstalkQueueAdapter' => __DIR__ . '/..' . '/fguillot/simple-queue/src/Adapter/BeanstalkQueueAdapter.php', + 'SimpleQueue\\Adapter\\DisqueQueueAdapter' => __DIR__ . '/..' . '/fguillot/simple-queue/src/Adapter/DisqueQueueAdapter.php', + 'SimpleQueue\\Adapter\\MemoryQueueAdapter' => __DIR__ . '/..' . '/fguillot/simple-queue/src/Adapter/MemoryQueueAdapter.php', + 'SimpleQueue\\Exception\\NotSupportedException' => __DIR__ . '/..' . '/fguillot/simple-queue/src/Exception/NotSupportedException.php', + 'SimpleQueue\\Job' => __DIR__ . '/..' . '/fguillot/simple-queue/src/Job.php', + 'SimpleQueue\\Queue' => __DIR__ . '/..' . '/fguillot/simple-queue/src/Queue.php', + 'SimpleQueue\\QueueAdapterInterface' => __DIR__ . '/..' . '/fguillot/simple-queue/src/QueueAdapterInterface.php', + 'SimpleValidator\\Validator' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validator.php', + 'SimpleValidator\\Validators\\Alpha' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validators/Alpha.php', + 'SimpleValidator\\Validators\\AlphaNumeric' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validators/AlphaNumeric.php', + 'SimpleValidator\\Validators\\Base' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validators/Base.php', + 'SimpleValidator\\Validators\\Date' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validators/Date.php', + 'SimpleValidator\\Validators\\Email' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validators/Email.php', + 'SimpleValidator\\Validators\\Equals' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validators/Equals.php', + 'SimpleValidator\\Validators\\Exists' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validators/Exists.php', + 'SimpleValidator\\Validators\\GreaterThan' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validators/GreaterThan.php', + 'SimpleValidator\\Validators\\InArray' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validators/InArray.php', + 'SimpleValidator\\Validators\\Integer' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validators/Integer.php', + 'SimpleValidator\\Validators\\Ip' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validators/Ip.php', + 'SimpleValidator\\Validators\\Length' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validators/Length.php', + 'SimpleValidator\\Validators\\MaxLength' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validators/MaxLength.php', + 'SimpleValidator\\Validators\\MinLength' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validators/MinLength.php', + 'SimpleValidator\\Validators\\NotEmpty' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validators/NotEmpty.php', + 'SimpleValidator\\Validators\\NotEquals' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validators/NotEquals.php', + 'SimpleValidator\\Validators\\NotInArray' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validators/NotInArray.php', + 'SimpleValidator\\Validators\\Numeric' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validators/Numeric.php', + 'SimpleValidator\\Validators\\Range' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validators/Range.php', + 'SimpleValidator\\Validators\\Required' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validators/Required.php', + 'SimpleValidator\\Validators\\Unique' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validators/Unique.php', + 'Symfony\\Component\\Console\\Application' => __DIR__ . '/..' . '/symfony/console/Application.php', + 'Symfony\\Component\\Console\\Command\\Command' => __DIR__ . '/..' . '/symfony/console/Command/Command.php', + 'Symfony\\Component\\Console\\Command\\HelpCommand' => __DIR__ . '/..' . '/symfony/console/Command/HelpCommand.php', + 'Symfony\\Component\\Console\\Command\\ListCommand' => __DIR__ . '/..' . '/symfony/console/Command/ListCommand.php', + 'Symfony\\Component\\Console\\ConsoleEvents' => __DIR__ . '/..' . '/symfony/console/ConsoleEvents.php', + 'Symfony\\Component\\Console\\Descriptor\\ApplicationDescription' => __DIR__ . '/..' . '/symfony/console/Descriptor/ApplicationDescription.php', + 'Symfony\\Component\\Console\\Descriptor\\Descriptor' => __DIR__ . '/..' . '/symfony/console/Descriptor/Descriptor.php', + 'Symfony\\Component\\Console\\Descriptor\\DescriptorInterface' => __DIR__ . '/..' . '/symfony/console/Descriptor/DescriptorInterface.php', + 'Symfony\\Component\\Console\\Descriptor\\JsonDescriptor' => __DIR__ . '/..' . '/symfony/console/Descriptor/JsonDescriptor.php', + 'Symfony\\Component\\Console\\Descriptor\\MarkdownDescriptor' => __DIR__ . '/..' . '/symfony/console/Descriptor/MarkdownDescriptor.php', + 'Symfony\\Component\\Console\\Descriptor\\TextDescriptor' => __DIR__ . '/..' . '/symfony/console/Descriptor/TextDescriptor.php', + 'Symfony\\Component\\Console\\Descriptor\\XmlDescriptor' => __DIR__ . '/..' . '/symfony/console/Descriptor/XmlDescriptor.php', + 'Symfony\\Component\\Console\\Event\\ConsoleCommandEvent' => __DIR__ . '/..' . '/symfony/console/Event/ConsoleCommandEvent.php', + 'Symfony\\Component\\Console\\Event\\ConsoleEvent' => __DIR__ . '/..' . '/symfony/console/Event/ConsoleEvent.php', + 'Symfony\\Component\\Console\\Event\\ConsoleExceptionEvent' => __DIR__ . '/..' . '/symfony/console/Event/ConsoleExceptionEvent.php', + 'Symfony\\Component\\Console\\Event\\ConsoleTerminateEvent' => __DIR__ . '/..' . '/symfony/console/Event/ConsoleTerminateEvent.php', + 'Symfony\\Component\\Console\\Exception\\CommandNotFoundException' => __DIR__ . '/..' . '/symfony/console/Exception/CommandNotFoundException.php', + 'Symfony\\Component\\Console\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/symfony/console/Exception/ExceptionInterface.php', + 'Symfony\\Component\\Console\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/symfony/console/Exception/InvalidArgumentException.php', + 'Symfony\\Component\\Console\\Exception\\InvalidOptionException' => __DIR__ . '/..' . '/symfony/console/Exception/InvalidOptionException.php', + 'Symfony\\Component\\Console\\Exception\\LogicException' => __DIR__ . '/..' . '/symfony/console/Exception/LogicException.php', + 'Symfony\\Component\\Console\\Exception\\RuntimeException' => __DIR__ . '/..' . '/symfony/console/Exception/RuntimeException.php', + 'Symfony\\Component\\Console\\Formatter\\OutputFormatter' => __DIR__ . '/..' . '/symfony/console/Formatter/OutputFormatter.php', + 'Symfony\\Component\\Console\\Formatter\\OutputFormatterInterface' => __DIR__ . '/..' . '/symfony/console/Formatter/OutputFormatterInterface.php', + 'Symfony\\Component\\Console\\Formatter\\OutputFormatterStyle' => __DIR__ . '/..' . '/symfony/console/Formatter/OutputFormatterStyle.php', + 'Symfony\\Component\\Console\\Formatter\\OutputFormatterStyleInterface' => __DIR__ . '/..' . '/symfony/console/Formatter/OutputFormatterStyleInterface.php', + 'Symfony\\Component\\Console\\Formatter\\OutputFormatterStyleStack' => __DIR__ . '/..' . '/symfony/console/Formatter/OutputFormatterStyleStack.php', + 'Symfony\\Component\\Console\\Helper\\DebugFormatterHelper' => __DIR__ . '/..' . '/symfony/console/Helper/DebugFormatterHelper.php', + 'Symfony\\Component\\Console\\Helper\\DescriptorHelper' => __DIR__ . '/..' . '/symfony/console/Helper/DescriptorHelper.php', + 'Symfony\\Component\\Console\\Helper\\DialogHelper' => __DIR__ . '/..' . '/symfony/console/Helper/DialogHelper.php', + 'Symfony\\Component\\Console\\Helper\\FormatterHelper' => __DIR__ . '/..' . '/symfony/console/Helper/FormatterHelper.php', + 'Symfony\\Component\\Console\\Helper\\Helper' => __DIR__ . '/..' . '/symfony/console/Helper/Helper.php', + 'Symfony\\Component\\Console\\Helper\\HelperInterface' => __DIR__ . '/..' . '/symfony/console/Helper/HelperInterface.php', + 'Symfony\\Component\\Console\\Helper\\HelperSet' => __DIR__ . '/..' . '/symfony/console/Helper/HelperSet.php', + 'Symfony\\Component\\Console\\Helper\\InputAwareHelper' => __DIR__ . '/..' . '/symfony/console/Helper/InputAwareHelper.php', + 'Symfony\\Component\\Console\\Helper\\ProcessHelper' => __DIR__ . '/..' . '/symfony/console/Helper/ProcessHelper.php', + 'Symfony\\Component\\Console\\Helper\\ProgressBar' => __DIR__ . '/..' . '/symfony/console/Helper/ProgressBar.php', + 'Symfony\\Component\\Console\\Helper\\ProgressHelper' => __DIR__ . '/..' . '/symfony/console/Helper/ProgressHelper.php', + 'Symfony\\Component\\Console\\Helper\\ProgressIndicator' => __DIR__ . '/..' . '/symfony/console/Helper/ProgressIndicator.php', + 'Symfony\\Component\\Console\\Helper\\QuestionHelper' => __DIR__ . '/..' . '/symfony/console/Helper/QuestionHelper.php', + 'Symfony\\Component\\Console\\Helper\\SymfonyQuestionHelper' => __DIR__ . '/..' . '/symfony/console/Helper/SymfonyQuestionHelper.php', + 'Symfony\\Component\\Console\\Helper\\Table' => __DIR__ . '/..' . '/symfony/console/Helper/Table.php', + 'Symfony\\Component\\Console\\Helper\\TableCell' => __DIR__ . '/..' . '/symfony/console/Helper/TableCell.php', + 'Symfony\\Component\\Console\\Helper\\TableHelper' => __DIR__ . '/..' . '/symfony/console/Helper/TableHelper.php', + 'Symfony\\Component\\Console\\Helper\\TableSeparator' => __DIR__ . '/..' . '/symfony/console/Helper/TableSeparator.php', + 'Symfony\\Component\\Console\\Helper\\TableStyle' => __DIR__ . '/..' . '/symfony/console/Helper/TableStyle.php', + 'Symfony\\Component\\Console\\Input\\ArgvInput' => __DIR__ . '/..' . '/symfony/console/Input/ArgvInput.php', + 'Symfony\\Component\\Console\\Input\\ArrayInput' => __DIR__ . '/..' . '/symfony/console/Input/ArrayInput.php', + 'Symfony\\Component\\Console\\Input\\Input' => __DIR__ . '/..' . '/symfony/console/Input/Input.php', + 'Symfony\\Component\\Console\\Input\\InputArgument' => __DIR__ . '/..' . '/symfony/console/Input/InputArgument.php', + 'Symfony\\Component\\Console\\Input\\InputAwareInterface' => __DIR__ . '/..' . '/symfony/console/Input/InputAwareInterface.php', + 'Symfony\\Component\\Console\\Input\\InputDefinition' => __DIR__ . '/..' . '/symfony/console/Input/InputDefinition.php', + 'Symfony\\Component\\Console\\Input\\InputInterface' => __DIR__ . '/..' . '/symfony/console/Input/InputInterface.php', + 'Symfony\\Component\\Console\\Input\\InputOption' => __DIR__ . '/..' . '/symfony/console/Input/InputOption.php', + 'Symfony\\Component\\Console\\Input\\StringInput' => __DIR__ . '/..' . '/symfony/console/Input/StringInput.php', + 'Symfony\\Component\\Console\\Logger\\ConsoleLogger' => __DIR__ . '/..' . '/symfony/console/Logger/ConsoleLogger.php', + 'Symfony\\Component\\Console\\Output\\BufferedOutput' => __DIR__ . '/..' . '/symfony/console/Output/BufferedOutput.php', + 'Symfony\\Component\\Console\\Output\\ConsoleOutput' => __DIR__ . '/..' . '/symfony/console/Output/ConsoleOutput.php', + 'Symfony\\Component\\Console\\Output\\ConsoleOutputInterface' => __DIR__ . '/..' . '/symfony/console/Output/ConsoleOutputInterface.php', + 'Symfony\\Component\\Console\\Output\\NullOutput' => __DIR__ . '/..' . '/symfony/console/Output/NullOutput.php', + 'Symfony\\Component\\Console\\Output\\Output' => __DIR__ . '/..' . '/symfony/console/Output/Output.php', + 'Symfony\\Component\\Console\\Output\\OutputInterface' => __DIR__ . '/..' . '/symfony/console/Output/OutputInterface.php', + 'Symfony\\Component\\Console\\Output\\StreamOutput' => __DIR__ . '/..' . '/symfony/console/Output/StreamOutput.php', + 'Symfony\\Component\\Console\\Question\\ChoiceQuestion' => __DIR__ . '/..' . '/symfony/console/Question/ChoiceQuestion.php', + 'Symfony\\Component\\Console\\Question\\ConfirmationQuestion' => __DIR__ . '/..' . '/symfony/console/Question/ConfirmationQuestion.php', + 'Symfony\\Component\\Console\\Question\\Question' => __DIR__ . '/..' . '/symfony/console/Question/Question.php', + 'Symfony\\Component\\Console\\Shell' => __DIR__ . '/..' . '/symfony/console/Shell.php', + 'Symfony\\Component\\Console\\Style\\OutputStyle' => __DIR__ . '/..' . '/symfony/console/Style/OutputStyle.php', + 'Symfony\\Component\\Console\\Style\\StyleInterface' => __DIR__ . '/..' . '/symfony/console/Style/StyleInterface.php', + 'Symfony\\Component\\Console\\Style\\SymfonyStyle' => __DIR__ . '/..' . '/symfony/console/Style/SymfonyStyle.php', + 'Symfony\\Component\\Console\\Tester\\ApplicationTester' => __DIR__ . '/..' . '/symfony/console/Tester/ApplicationTester.php', + 'Symfony\\Component\\Console\\Tester\\CommandTester' => __DIR__ . '/..' . '/symfony/console/Tester/CommandTester.php', + 'Symfony\\Component\\EventDispatcher\\ContainerAwareEventDispatcher' => __DIR__ . '/..' . '/symfony/event-dispatcher/ContainerAwareEventDispatcher.php', + 'Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcher' => __DIR__ . '/..' . '/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php', + 'Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcherInterface' => __DIR__ . '/..' . '/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php', + 'Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener' => __DIR__ . '/..' . '/symfony/event-dispatcher/Debug/WrappedListener.php', + 'Symfony\\Component\\EventDispatcher\\DependencyInjection\\RegisterListenersPass' => __DIR__ . '/..' . '/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php', + 'Symfony\\Component\\EventDispatcher\\Event' => __DIR__ . '/..' . '/symfony/event-dispatcher/Event.php', + 'Symfony\\Component\\EventDispatcher\\EventDispatcher' => __DIR__ . '/..' . '/symfony/event-dispatcher/EventDispatcher.php', + 'Symfony\\Component\\EventDispatcher\\EventDispatcherInterface' => __DIR__ . '/..' . '/symfony/event-dispatcher/EventDispatcherInterface.php', + 'Symfony\\Component\\EventDispatcher\\EventSubscriberInterface' => __DIR__ . '/..' . '/symfony/event-dispatcher/EventSubscriberInterface.php', + 'Symfony\\Component\\EventDispatcher\\GenericEvent' => __DIR__ . '/..' . '/symfony/event-dispatcher/GenericEvent.php', + 'Symfony\\Component\\EventDispatcher\\ImmutableEventDispatcher' => __DIR__ . '/..' . '/symfony/event-dispatcher/ImmutableEventDispatcher.php', + 'Symfony\\Polyfill\\Mbstring\\Mbstring' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/Mbstring.php', + 'ZendXml\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/zendframework/zendxml/library/ZendXml/Exception/ExceptionInterface.php', + 'ZendXml\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/zendframework/zendxml/library/ZendXml/Exception/InvalidArgumentException.php', + 'ZendXml\\Exception\\RuntimeException' => __DIR__ . '/..' . '/zendframework/zendxml/library/ZendXml/Exception/RuntimeException.php', + 'ZendXml\\Security' => __DIR__ . '/..' . '/zendframework/zendxml/library/ZendXml/Security.php', + ); + + public static function getInitializer(ClassLoader $loader) + { + return \Closure::bind(function () use ($loader) { + $loader->prefixLengthsPsr4 = ComposerStaticInit6edea6294a88689e3f5c56484bb70c9b::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInit6edea6294a88689e3f5c56484bb70c9b::$prefixDirsPsr4; + $loader->prefixesPsr0 = ComposerStaticInit6edea6294a88689e3f5c56484bb70c9b::$prefixesPsr0; + $loader->classMap = ComposerStaticInit6edea6294a88689e3f5c56484bb70c9b::$classMap; + + }, null, ClassLoader::class); + } +} diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json new file mode 100644 index 0000000000..12b9857f0e --- /dev/null +++ b/vendor/composer/installed.json @@ -0,0 +1,1051 @@ +[ + { + "name": "aferrandini/phpqrcode", + "version": "1.0.1", + "version_normalized": "1.0.1.0", + "source": { + "type": "git", + "url": "https://github.com/aferrandini/PHPQRCode.git", + "reference": "3c1c0454d43710ab5bbe19a51ad4cb41c22e3d46" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/aferrandini/PHPQRCode/zipball/3c1c0454d43710ab5bbe19a51ad4cb41c22e3d46", + "reference": "3c1c0454d43710ab5bbe19a51ad4cb41c22e3d46", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "time": "2013-07-08T09:39:08+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-0": { + "PHPQRCode": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ariel Ferrandini", + "email": "arielferrandini@gmail.com", + "homepage": "http://www.ferrandini.com/", + "role": "Developer" + } + ], + "description": "PHPQRCode porting and changed for PHP 5.3 compatibility", + "homepage": "https://github.com/aferrandini/PHPQRCode", + "keywords": [ + "barcode", + "php", + "qrcode" + ] + }, + { + "name": "christian-riesen/base32", + "version": "1.3.1", + "version_normalized": "1.3.1.0", + "source": { + "type": "git", + "url": "https://github.com/ChristianRiesen/base32.git", + "reference": "0a31e50c0fa9b1692d077c86ac188eecdcbaf7fa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ChristianRiesen/base32/zipball/0a31e50c0fa9b1692d077c86ac188eecdcbaf7fa", + "reference": "0a31e50c0fa9b1692d077c86ac188eecdcbaf7fa", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*", + "satooshi/php-coveralls": "0.*" + }, + "time": "2016-05-05T11:49:03+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Base32\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Riesen", + "email": "chris.riesen@gmail.com", + "homepage": "http://christianriesen.com", + "role": "Developer" + } + ], + "description": "Base32 encoder/decoder according to RFC 4648", + "homepage": "https://github.com/ChristianRiesen/base32", + "keywords": [ + "base32", + "decode", + "encode", + "rfc4648" + ] + }, + { + "name": "christian-riesen/otp", + "version": "1.4.3", + "version_normalized": "1.4.3.0", + "source": { + "type": "git", + "url": "https://github.com/ChristianRiesen/otp.git", + "reference": "20a539ce6280eb029030f4e7caefd5709a75e1ad" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ChristianRiesen/otp/zipball/20a539ce6280eb029030f4e7caefd5709a75e1ad", + "reference": "20a539ce6280eb029030f4e7caefd5709a75e1ad", + "shasum": "" + }, + "require": { + "christian-riesen/base32": ">=1.0", + "php": ">=5.3.0" + }, + "suggest": { + "paragonie/random_compat": "Optional polyfill for a more secure random generator for pre PHP7 versions" + }, + "time": "2015-10-08T08:17:59+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-0": { + "Otp": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Riesen", + "email": "chris.riesen@gmail.com", + "homepage": "http://christianriesen.com", + "role": "Developer" + } + ], + "description": "One Time Passwords, hotp and totp according to RFC4226 and RFC6238", + "homepage": "https://github.com/ChristianRiesen/otp", + "keywords": [ + "googleauthenticator", + "hotp", + "otp", + "rfc4226", + "rfc6238", + "totp" + ] + }, + { + "name": "eluceo/ical", + "version": "0.10.1", + "version_normalized": "0.10.1.0", + "source": { + "type": "git", + "url": "https://github.com/markuspoerschke/iCal.git", + "reference": "2dd99c12c0aa961c541380ab0c113135e14af33e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/markuspoerschke/iCal/zipball/2dd99c12c0aa961c541380ab0c113135e14af33e", + "reference": "2dd99c12c0aa961c541380ab0c113135e14af33e", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.3" + }, + "time": "2016-06-09T09:08:55+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-0": { + "Eluceo\\iCal": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Maciej Łebkowski", + "email": "m.lebkowski@gmail.com", + "role": "Contributor" + }, + { + "name": "Markus Poerschke", + "email": "markus@eluceo.de", + "role": "Developer" + } + ], + "description": "The eluceo/iCal package offers a abstraction layer for creating iCalendars. You can easily create iCal files by using PHP object instead of typing your *.ics file by hand. The output will follow RFC 2445 as best as possible.", + "homepage": "https://github.com/markuspoerschke/iCal", + "keywords": [ + "calendar", + "iCalendar", + "ical", + "ics", + "php calendar" + ] + }, + { + "name": "erusev/parsedown", + "version": "1.6.0", + "version_normalized": "1.6.0.0", + "source": { + "type": "git", + "url": "https://github.com/erusev/parsedown.git", + "reference": "3ebbd730b5c2cf5ce78bc1bf64071407fc6674b7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/erusev/parsedown/zipball/3ebbd730b5c2cf5ce78bc1bf64071407fc6674b7", + "reference": "3ebbd730b5c2cf5ce78bc1bf64071407fc6674b7", + "shasum": "" + }, + "time": "2015-10-04T16:44:32+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-0": { + "Parsedown": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Emanuil Rusev", + "email": "hello@erusev.com", + "homepage": "http://erusev.com" + } + ], + "description": "Parser for Markdown.", + "homepage": "http://parsedown.org", + "keywords": [ + "markdown", + "parser" + ] + }, + { + "name": "fguillot/json-rpc", + "version": "v1.2.1", + "version_normalized": "1.2.1.0", + "source": { + "type": "git", + "url": "https://github.com/fguillot/JsonRPC.git", + "reference": "d491bb549bfa11aff4c37abcea2ffb28c9523f69" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fguillot/JsonRPC/zipball/d491bb549bfa11aff4c37abcea2ffb28c9523f69", + "reference": "d491bb549bfa11aff4c37abcea2ffb28c9523f69", + "shasum": "" + }, + "require": { + "php": ">=5.3.4" + }, + "require-dev": { + "phpunit/phpunit": "4.8.*" + }, + "time": "2016-06-25T23:11:10+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-0": { + "JsonRPC": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frédéric Guillot" + } + ], + "description": "Simple Json-RPC client/server library that just works", + "homepage": "https://github.com/fguillot/JsonRPC" + }, + { + "name": "fguillot/picodb", + "version": "v1.0.14", + "version_normalized": "1.0.14.0", + "source": { + "type": "git", + "url": "https://github.com/fguillot/picoDb.git", + "reference": "86a831302ab10af800c83dbe4b3b01c88d5433f1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fguillot/picoDb/zipball/86a831302ab10af800c83dbe4b3b01c88d5433f1", + "reference": "86a831302ab10af800c83dbe4b3b01c88d5433f1", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "4.8.*" + }, + "time": "2016-07-16T22:59:59+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-0": { + "PicoDb": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frédéric Guillot", + "homepage": "https://github.com/fguillot/" + } + ], + "description": "Minimalist database query builder", + "homepage": "https://github.com/fguillot/picoDb" + }, + { + "name": "fguillot/simple-queue", + "version": "v1.0.1", + "version_normalized": "1.0.1.0", + "source": { + "type": "git", + "url": "https://github.com/fguillot/simple-queue.git", + "reference": "ce7801c507f9501bcca455129fb04c3d2107d5ff" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fguillot/simple-queue/zipball/ce7801c507f9501bcca455129fb04c3d2107d5ff", + "reference": "ce7801c507f9501bcca455129fb04c3d2107d5ff", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "aws/aws-sdk-php": "~3.0", + "mariano/disque-php": "~2.0", + "pda/pheanstalk": "~3.0", + "php-amqplib/php-amqplib": "2.6.*", + "phpunit/phpunit": "5.3.*" + }, + "suggest": { + "aws/aws-sdk-php": "Required to use the AWS SQS queue driver (~3.0).", + "mariano/disque-php": "Required to use the Disque queue driver (~2.0).", + "pda/pheanstalk": "Required to use the Beanstalk queue driver (~3.0).", + "php-amqplib/php-amqplib": "Required to use the RabbitMQ queue driver (2.6.*)." + }, + "time": "2016-06-05T21:34:56+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "SimpleQueue\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frédéric Guillot" + } + ], + "description": "Abstraction layer for multiple queue systems", + "homepage": "https://github.com/fguillot/simple-queue" + }, + { + "name": "fguillot/simple-validator", + "version": "v1.0.1", + "version_normalized": "1.0.1.0", + "source": { + "type": "git", + "url": "https://github.com/fguillot/simpleValidator.git", + "reference": "23b0a99c5f11ad74d05f8845feaafbcfd9223eda" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fguillot/simpleValidator/zipball/23b0a99c5f11ad74d05f8845feaafbcfd9223eda", + "reference": "23b0a99c5f11ad74d05f8845feaafbcfd9223eda", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "time": "2016-06-26T15:09:26+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-0": { + "SimpleValidator": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frédéric Guillot" + } + ], + "description": "Simple validator library", + "homepage": "https://github.com/fguillot/simpleValidator" + }, + { + "name": "psr/log", + "version": "1.0.2", + "version_normalized": "1.0.2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "time": "2016-10-10T12:19:37+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ] + }, + { + "name": "fguillot/simpleLogger", + "version": "v1.0.1", + "version_normalized": "1.0.1.0", + "source": { + "type": "git", + "url": "https://github.com/fguillot/simpleLogger.git", + "reference": "c6831841193bb265b7900ecc8b6a8918371a7c98" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fguillot/simpleLogger/zipball/c6831841193bb265b7900ecc8b6a8918371a7c98", + "reference": "c6831841193bb265b7900ecc8b6a8918371a7c98", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "psr/log": "~1.0" + }, + "time": "2016-05-07T18:01:57+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-0": { + "SimpleLogger": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frédéric Guillot" + } + ], + "description": "PHP library to write logs (compatible with PSR-3)", + "homepage": "https://github.com/fguillot/simpleLogger" + }, + { + "name": "gregwar/captcha", + "version": "v1.1.1", + "version_normalized": "1.1.1.0", + "source": { + "type": "git", + "url": "https://github.com/Gregwar/Captcha.git", + "reference": "1240ab993ca713680573b2d4166900da5f758623" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Gregwar/Captcha/zipball/1240ab993ca713680573b2d4166900da5f758623", + "reference": "1240ab993ca713680573b2d4166900da5f758623", + "shasum": "" + }, + "require": { + "ext-gd": "*", + "php": ">=5.3.0" + }, + "time": "2015-09-11T15:23:20+00:00", + "type": "captcha", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Gregwar\\Captcha\\": "/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grégoire Passault", + "email": "g.passault@gmail.com", + "homepage": "http://www.gregwar.com/" + }, + { + "name": "Jeremy Livingston", + "email": "jeremy.j.livingston@gmail.com" + } + ], + "description": "Captcha generator", + "homepage": "https://github.com/Gregwar/Captcha", + "keywords": [ + "bot", + "captcha", + "spam" + ] + }, + { + "name": "zendframework/zendxml", + "version": "1.0.2", + "version_normalized": "1.0.2.0", + "source": { + "type": "git", + "url": "https://github.com/zendframework/ZendXml.git", + "reference": "7b64507bc35d841c9c5802d67f6f87ef8e1a58c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/ZendXml/zipball/7b64507bc35d841c9c5802d67f6f87ef8e1a58c9", + "reference": "7b64507bc35d841c9c5802d67f6f87ef8e1a58c9", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^3.7 || ^4.0", + "squizlabs/php_codesniffer": "^1.5" + }, + "time": "2016-02-04T21:02:08+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "ZendXml\\": "library/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Utility library for XML usage, best practices, and security in PHP", + "homepage": "http://packages.zendframework.com/", + "keywords": [ + "security", + "xml", + "zf2" + ] + }, + { + "name": "miniflux/picofeed", + "version": "v0.1.34", + "version_normalized": "0.1.34.0", + "source": { + "type": "git", + "url": "https://github.com/miniflux/picoFeed.git", + "reference": "5c8a731d4e7a3589e562e4fdaa98bcb57fa8a2ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/miniflux/picoFeed/zipball/5c8a731d4e7a3589e562e4fdaa98bcb57fa8a2ea", + "reference": "5c8a731d4e7a3589e562e4fdaa98bcb57fa8a2ea", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-iconv": "*", + "ext-libxml": "*", + "ext-simplexml": "*", + "ext-xml": "*", + "php": ">=5.3.0", + "zendframework/zendxml": "^1.0" + }, + "require-dev": { + "phpdocumentor/reflection-docblock": "2.0.4", + "phpunit/phpunit": "4.8.26", + "symfony/yaml": "2.8.7" + }, + "suggest": { + "ext-curl": "PicoFeed will use cURL if present" + }, + "time": "2017-06-12T00:22:06+00:00", + "bin": [ + "picofeed" + ], + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-0": { + "PicoFeed": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frédéric Guillot" + } + ], + "description": "Modern library to handle RSS/Atom feeds", + "homepage": "https://github.com/miniflux/picoFeed" + }, + { + "name": "paragonie/random_compat", + "version": "v2.0.2", + "version_normalized": "2.0.2.0", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "088c04e2f261c33bed6ca5245491cfca69195ccf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/088c04e2f261c33bed6ca5245491cfca69195ccf", + "reference": "088c04e2f261c33bed6ca5245491cfca69195ccf", + "shasum": "" + }, + "require": { + "php": ">=5.2.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "time": "2016-04-03T06:00:07+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "files": [ + "lib/random.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "pseudorandom", + "random" + ] + }, + { + "name": "pimple/pimple", + "version": "v3.0.2", + "version_normalized": "3.0.2.0", + "source": { + "type": "git", + "url": "https://github.com/silexphp/Pimple.git", + "reference": "a30f7d6e57565a2e1a316e1baf2a483f788b258a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/silexphp/Pimple/zipball/a30f7d6e57565a2e1a316e1baf2a483f788b258a", + "reference": "a30f7d6e57565a2e1a316e1baf2a483f788b258a", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "time": "2015-09-11T15:10:35+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Pimple": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Pimple, a simple Dependency Injection Container", + "homepage": "http://pimple.sensiolabs.org", + "keywords": [ + "container", + "dependency injection" + ] + }, + { + "name": "ramsey/array_column", + "version": "1.1.3", + "version_normalized": "1.1.3.0", + "source": { + "type": "git", + "url": "https://github.com/ramsey/array_column.git", + "reference": "f8e52eb28e67eb50e613b451dd916abcf783c1db" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/array_column/zipball/f8e52eb28e67eb50e613b451dd916abcf783c1db", + "reference": "f8e52eb28e67eb50e613b451dd916abcf783c1db", + "shasum": "" + }, + "require-dev": { + "jakub-onderka/php-parallel-lint": "0.8.*", + "phpunit/phpunit": "~4.5", + "satooshi/php-coveralls": "0.6.*", + "squizlabs/php_codesniffer": "~2.2" + }, + "time": "2015-03-20T22:07:39+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "files": [ + "src/array_column.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ben Ramsey", + "homepage": "http://benramsey.com" + } + ], + "description": "Provides functionality for array_column() to projects using PHP earlier than version 5.5.", + "homepage": "https://github.com/ramsey/array_column", + "keywords": [ + "array", + "array_column", + "column" + ] + }, + { + "name": "swiftmailer/swiftmailer", + "version": "v5.4.5", + "version_normalized": "5.4.5.0", + "source": { + "type": "git", + "url": "https://github.com/swiftmailer/swiftmailer.git", + "reference": "cd142238a339459b10da3d8234220963f392540c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/cd142238a339459b10da3d8234220963f392540c", + "reference": "cd142238a339459b10da3d8234220963f392540c", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "mockery/mockery": "~0.9.1", + "symfony/phpunit-bridge": "~3.2" + }, + "time": "2016-12-29T10:02:40+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "files": [ + "lib/swift_required.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Corbyn" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Swiftmailer, free feature-rich PHP mailer", + "homepage": "http://swiftmailer.org", + "keywords": [ + "email", + "mail", + "mailer" + ] + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.4.0", + "version_normalized": "1.4.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "f29dca382a6485c3cbe6379f0c61230167681937" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/f29dca382a6485c3cbe6379f0c61230167681937", + "reference": "f29dca382a6485c3cbe6379f0c61230167681937", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "time": "2017-06-09T14:24:12+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ] + }, + { + "name": "symfony/console", + "version": "v2.8.7", + "version_normalized": "2.8.7.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "5ac8bc9aa77bb2edf06af3a1bb6bc1020d23acd3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/5ac8bc9aa77bb2edf06af3a1bb6bc1020d23acd3", + "reference": "5ac8bc9aa77bb2edf06af3a1bb6bc1020d23acd3", + "shasum": "" + }, + "require": { + "php": ">=5.3.9", + "symfony/polyfill-mbstring": "~1.0" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/event-dispatcher": "~2.1|~3.0.0", + "symfony/process": "~2.1|~3.0.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/process": "" + }, + "time": "2016-06-06T15:06:25+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.8-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Console Component", + "homepage": "https://symfony.com" + }, + { + "name": "symfony/event-dispatcher", + "version": "v2.7.14", + "version_normalized": "2.7.14.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "d3e09ed1224503791f31b913d22196f65f9afed5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/d3e09ed1224503791f31b913d22196f65f9afed5", + "reference": "d3e09ed1224503791f31b913d22196f65f9afed5", + "shasum": "" + }, + "require": { + "php": ">=5.3.9" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~2.0,>=2.0.5", + "symfony/dependency-injection": "~2.6", + "symfony/expression-language": "~2.6", + "symfony/stopwatch": "~2.3" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "time": "2016-06-06T11:03:51+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony EventDispatcher Component", + "homepage": "https://symfony.com" + } +] diff --git a/vendor/eluceo/ical/.gitignore b/vendor/eluceo/ical/.gitignore new file mode 100644 index 0000000000..4e06e7d44c --- /dev/null +++ b/vendor/eluceo/ical/.gitignore @@ -0,0 +1,3 @@ +vendor +composer.lock +bin diff --git a/vendor/eluceo/ical/.php_cs b/vendor/eluceo/ical/.php_cs new file mode 100644 index 0000000000..1d54d34a22 --- /dev/null +++ b/vendor/eluceo/ical/.php_cs @@ -0,0 +1,26 @@ + + +This source file is subject to the MIT license that is bundled +with this source code in the file LICENSE. +EOF +); + +$finder = Symfony\CS\Finder\DefaultFinder::create(); +$finder->in(__DIR__ . '/src'); + +return Symfony\CS\Config\Config::create() + ->fixers(array( + 'header_comment', + 'concat_with_spaces', + 'align_equals', + 'align_double_arrow', + 'unused_use', + 'long_array_syntax', + )) + ->finder($finder) +; diff --git a/vendor/eluceo/ical/.scrutinizer.yml b/vendor/eluceo/ical/.scrutinizer.yml new file mode 100644 index 0000000000..d3b381a2c7 --- /dev/null +++ b/vendor/eluceo/ical/.scrutinizer.yml @@ -0,0 +1,20 @@ +filter: + excluded_paths: + - tests/* + +tools: + php_cs_fixer: true + php_code_sniffer: + config: + standard: PSR2 + php_mess_detector: true + php_analyzer: true + sensiolabs_security_checker: true + external_code_coverage: + timeout: 300 + runs: 1 + +checks: + php: + code_rating: true + duplication: true diff --git a/vendor/eluceo/ical/.travis.yml b/vendor/eluceo/ical/.travis.yml new file mode 100644 index 0000000000..cbe31ac99b --- /dev/null +++ b/vendor/eluceo/ical/.travis.yml @@ -0,0 +1,19 @@ +language: php + +php: + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - 7.0 + - hhvm + +before_script: + - composer self-update + - composer install + +script: ./bin/phpunit --coverage-clover=coverage.clover + +after_script: + - wget https://scrutinizer-ci.com/ocular.phar + - php ocular.phar code-coverage:upload --format=php-clover coverage.clover diff --git a/vendor/eluceo/ical/CHANGELOG.md b/vendor/eluceo/ical/CHANGELOG.md new file mode 100644 index 0000000000..84320d9820 --- /dev/null +++ b/vendor/eluceo/ical/CHANGELOG.md @@ -0,0 +1,31 @@ +# Change Log +All notable changes to this project will be documented in this file. + +## [0.10.1] - 2016-05-09 +### Fixed +- Problem with GEO property when importing into Google Calendar [#74](https://github.com/markuspoerschke/iCal/pull/74) + +## [0.10.0] - 2016-04-26 +### Changed +- Use 'escapeValue' to escape the new line character. [#60](https://github.com/markuspoerschke/iCal/pull/60) +- Order components by type when building ical file. [#65](https://github.com/markuspoerschke/iCal/pull/65) + +### Added +- X-ALT-DESC for HTML types with new descriptionHTML field. [#55](https://github.com/markuspoerschke/iCal/pull/55) +- Added a property and setter for calendar color. [#68](https://github.com/markuspoerschke/iCal/pull/68) +- Write also GEO property if geo location is given. [#66](https://github.com/markuspoerschke/iCal/pull/66) + +## [0.9.0] - 2015-11-13 +### Added +- CHANGELOG.md based on [’Keep a CHANGELOG’](https://github.com/olivierlacan/keep-a-changelog) +- Support event properties EXDATE and RECURRENCE-ID [#50](https://github.com/markuspoerschke/iCal/pull/53) + +### Changed +- Allow new lines in event descriptions [#53](https://github.com/markuspoerschke/iCal/pull/53) +- **Breaking Change:** Changed signature of the ```Event::setOrganizer``` method. Now there is is only one parameter that must be an instance of ```Property\Organizer```. +- Updated install section in README.md [#54](https://github.com/markuspoerschke/iCal/pull/53) + +[Unreleased]: https://github.com/markuspoerschke/iCal/compare/0.10.1...HEAD +[0.10.1]: https://github.com/markuspoerschke/iCal/compare/0.10.0...0.10.1 +[0.10.0]: https://github.com/markuspoerschke/iCal/compare/0.9.0...0.10.0 +[0.9.0]: https://github.com/markuspoerschke/iCal/compare/0.8.0...0.9.0 diff --git a/vendor/eluceo/ical/LICENSE b/vendor/eluceo/ical/LICENSE new file mode 100644 index 0000000000..92be8d0616 --- /dev/null +++ b/vendor/eluceo/ical/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2012-2015 Markus Poerschke + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/eluceo/ical/README.md b/vendor/eluceo/ical/README.md new file mode 100644 index 0000000000..521d537deb --- /dev/null +++ b/vendor/eluceo/ical/README.md @@ -0,0 +1,158 @@ +# eluceo — iCal + +[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/markuspoerschke/iCal/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/markuspoerschke/iCal/?branch=master) [![Code Coverage](https://scrutinizer-ci.com/g/markuspoerschke/iCal/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/markuspoerschke/iCal/?branch=master) [![Build Status](https://travis-ci.org/markuspoerschke/iCal.svg?branch=master)](https://travis-ci.org/markuspoerschke/iCal) + +This package offers a abstraction layer for creating iCalendars. The output will +follow [RFC 5545](http://www.ietf.org/rfc/rfc5545.txt) as best as possible. + +The following components are supported at this time: + +* VCALENDAR +* VEVENT +* VALARM +* VTIMEZONE + +## Installation + +You can install this package by using [Composer](http://getcomposer.org), running this command: + +```sh +composer require eluceo/ical +``` +Link to Packagist: https://packagist.org/packages/eluceo/ical + +## Usage + +### Basic Usage + +#### 1. Create a Calendar object + +```PHP +$vCalendar = new \Eluceo\iCal\Component\Calendar('www.example.com'); +``` + +#### 2. Create an Event object + +```PHP +$vEvent = new \Eluceo\iCal\Component\Event(); +``` + +#### 3. Add your information to the Event + +```PHP +$vEvent + ->setDtStart(new \DateTime('2012-12-24')) + ->setDtEnd(new \DateTime('2012-12-24')) + ->setNoTime(true) + ->setSummary('Christmas') +; +``` + +#### 4. Add Event to Calendar + +```PHP +$vCalendar->addComponent($vEvent); +``` + +#### 5. Set HTTP-headers + +```PHP +header('Content-Type: text/calendar; charset=utf-8'); +header('Content-Disposition: attachment; filename="cal.ics"'); +``` + +#### 6. Send output + +```PHP +echo $vCalendar->render(); +``` + +### Timezone support + +This package supports three different types of handling timezones: + +#### 1. UTC (default) + +In the default setting, UTC/GMT will be used as Timezone. The time will be formated as following: + +``` +DTSTART:20121224T180000Z +``` + +#### 2. Use explicit timezone + +You can use an explicit timezone by calling `$vEvent->setUseTimezone(true);`. The timezone of your +`\DateTime` object will be used. In this case the non-standard field "X-WR-TIMEZONE" will be used. +Be awre that this is a simple solution which is not supported by all calendar clients. +The output will be as following: + +``` +DTSTART;TZID=Europe/Berlin:20121224T180000 +``` + +#### 3. Use explicit timezone with definition + +You can use an explicit timezone and define it using `Timezone()` and `TimezoneRule()` (see example5.php). +The timezone of your `\DateTime` object will be used. The output will be as following: + +``` +BEGIN:VTIMEZONE +TZID:Europe/Berlin +X-LIC-LOCATION:Europe/Berlin +BEGIN:DAYLIGHT +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +DTSTART:19810329T030000 +RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=3;BYDAY=-1SU +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +DTSTART:19961027T030000 +RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=10;BYDAY=-1SU +END:STANDARD +END:VTIMEZONE +... +DTSTART;TZID=Europe/Berlin:20121224T180000 +``` + +#### 4. Use locale time + +You can use local time by calling `$vEvent->setUseUtc(false);`. The output will be: + +``` +DTSTART:20121224T180000 +``` + +## Running the tests + +To setup and run tests: + +- go to the root directory of this project +- download composer: `wget https://getcomposer.org/composer.phar` +- install dev dependencies: `php composer.phar install --dev` +- run `./bin/phpunit` + +## License + +This package is released under the __MIT license__. + +Copyright (c) 2012-2015 Markus Poerschke + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/eluceo/ical/UPGRADE.md b/vendor/eluceo/ical/UPGRADE.md new file mode 100644 index 0000000000..951209dd0d --- /dev/null +++ b/vendor/eluceo/ical/UPGRADE.md @@ -0,0 +1,9 @@ +# v0.8.0 -> v0.9.0 + +- The signature of the ```Event::setOrganizer``` method was changed: +Now there is is only one parameter that must be an instance of ```Property\Organizer```. + +# v0.7.0 -> v0.8.0 + +- The signature of the ```Event::setOrganizer``` method was changed: Now there are +two parameters name and email instead of an already formatted string. diff --git a/vendor/eluceo/ical/composer.json b/vendor/eluceo/ical/composer.json new file mode 100644 index 0000000000..b2477c2834 --- /dev/null +++ b/vendor/eluceo/ical/composer.json @@ -0,0 +1,43 @@ +{ + "name": "eluceo/ical", + "description": "The eluceo/iCal package offers a abstraction layer for creating iCalendars. You can easily create iCal files by using PHP object instead of typing your *.ics file by hand. The output will follow RFC 2445 as best as possible.", + "license": "MIT", + "homepage": "https://github.com/markuspoerschke/iCal", + "authors": [ + { + "name": "Markus Poerschke", + "email": "markus@eluceo.de", + "role": "Developer" + }, + { + "name": "Maciej Łebkowski", + "email": "m.lebkowski@gmail.com", + "role": "Contributor" + } + ], + "keywords": [ + "ical", + "php calendar", + "icalendar", + "ics", + "calendar" + ], + "support": { + "issues": "https://github.com/markuspoerschke/iCal/issues", + "source": "https://github.com/markuspoerschke/iCal" + }, + "autoload": { + "psr-0": { + "Eluceo\\iCal": "src/" + } + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.3" + }, + "config": { + "bin-dir": "bin" + } +} diff --git a/vendor/eluceo/ical/examples/example1.php b/vendor/eluceo/ical/examples/example1.php new file mode 100644 index 0000000000..4d1d32c4eb --- /dev/null +++ b/vendor/eluceo/ical/examples/example1.php @@ -0,0 +1,30 @@ +setDtStart(new \DateTime('2012-12-24')); +$vEvent->setDtEnd(new \DateTime('2012-12-24')); +$vEvent->setNoTime(true); +$vEvent->setSummary('Christmas'); + +// Adding Timezone (optional) +$vEvent->setUseTimezone(true); + +// 3. Add event to calendar +$vCalendar->addComponent($vEvent); + +// 4. Set headers +header('Content-Type: text/calendar; charset=utf-8'); +header('Content-Disposition: attachment; filename="cal.ics"'); + +// 5. Output +echo $vCalendar->render(); diff --git a/vendor/eluceo/ical/examples/example2.php b/vendor/eluceo/ical/examples/example2.php new file mode 100644 index 0000000000..bafcf033b7 --- /dev/null +++ b/vendor/eluceo/ical/examples/example2.php @@ -0,0 +1,31 @@ +setDtStart(new \DateTime('2012-12-24')); +$vEvent->setDtEnd(new \DateTime('2012-12-24')); +$vEvent->setNoTime(true); +$vEvent->setSummary('Summary with some german "umlauten" and a backslash \\: Kinder mögen Äpfel pflücken.'); +$vEvent->setCategories(['holidays']); + +// Adding Timezone (optional) +$vEvent->setUseTimezone(true); + +// 3. Add event to calendar +$vCalendar->addComponent($vEvent); + +// 4. Set headers +header('Content-Type: text/calendar; charset=utf-8'); +header('Content-Disposition: attachment; filename="cal.ics"'); + +// 5. Output +echo $vCalendar->render(); diff --git a/vendor/eluceo/ical/examples/example3.php b/vendor/eluceo/ical/examples/example3.php new file mode 100644 index 0000000000..290ff2dcc7 --- /dev/null +++ b/vendor/eluceo/ical/examples/example3.php @@ -0,0 +1,36 @@ +setDtStart(new \DateTime('2012-12-31')); +$vEvent->setDtEnd(new \DateTime('2012-12-31')); +$vEvent->setNoTime(true); +$vEvent->setSummary('New Year’s Eve'); + +// Set recurrence rule +$recurrenceRule = new \Eluceo\iCal\Property\Event\RecurrenceRule(); +$recurrenceRule->setFreq(\Eluceo\iCal\Property\Event\RecurrenceRule::FREQ_YEARLY); +$recurrenceRule->setInterval(1); +$vEvent->setRecurrenceRule($recurrenceRule); + +// Adding Timezone (optional) +$vEvent->setUseTimezone(true); + +// 3. Add event to calendar +$vCalendar->addComponent($vEvent); + +// 4. Set headers +header('Content-Type: text/calendar; charset=utf-8'); +header('Content-Disposition: attachment; filename="cal.ics"'); + +// 5. Output +echo $vCalendar->render(); diff --git a/vendor/eluceo/ical/examples/example4.php b/vendor/eluceo/ical/examples/example4.php new file mode 100644 index 0000000000..7b87144ce6 --- /dev/null +++ b/vendor/eluceo/ical/examples/example4.php @@ -0,0 +1,35 @@ +setDtStart(new \DateTime('2012-11-11 13:00:00')); +$vEvent->setDtEnd(new \DateTime('2012-11-11 14:30:00')); +$vEvent->setSummary('Weekly lunch with Markus'); + +// Set recurrence rule +$recurrenceRule = new \Eluceo\iCal\Property\Event\RecurrenceRule(); +$recurrenceRule->setFreq(\Eluceo\iCal\Property\Event\RecurrenceRule::FREQ_WEEKLY); +$recurrenceRule->setInterval(1); +$vEvent->setRecurrenceRule($recurrenceRule); + +// Adding Timezone (optional) +$vEvent->setUseTimezone(true); + +// 3. Add event to calendar +$vCalendar->addComponent($vEvent); + +// 4. Set headers +header('Content-Type: text/calendar; charset=utf-8'); +header('Content-Disposition: attachment; filename="cal.ics"'); + +// 5. Output +echo $vCalendar->render(); diff --git a/vendor/eluceo/ical/examples/example5.php b/vendor/eluceo/ical/examples/example5.php new file mode 100644 index 0000000000..a7da6e27ca --- /dev/null +++ b/vendor/eluceo/ical/examples/example5.php @@ -0,0 +1,66 @@ +setTzName('CEST'); +$vTimezoneRuleDst->setDtStart(new \DateTime('1981-03-29 02:00:00', $dtz)); +$vTimezoneRuleDst->setTzOffsetFrom('+0100'); +$vTimezoneRuleDst->setTzOffsetTo('+0200'); +$dstRecurrenceRule = new \Eluceo\iCal\Property\Event\RecurrenceRule(); +$dstRecurrenceRule->setFreq(\Eluceo\iCal\Property\Event\RecurrenceRule::FREQ_YEARLY); +$dstRecurrenceRule->setByMonth(3); +$dstRecurrenceRule->setByDay('-1SU'); +$vTimezoneRuleDst->setRecurrenceRule($dstRecurrenceRule); + +// 3. Create timezone rule object for Standard Time +$vTimezoneRuleStd = new \Eluceo\iCal\Component\TimezoneRule(\Eluceo\iCal\Component\TimezoneRule::TYPE_STANDARD); +$vTimezoneRuleStd->setTzName('CET'); +$vTimezoneRuleStd->setDtStart(new \DateTime('1996-10-27 03:00:00', $dtz)); +$vTimezoneRuleStd->setTzOffsetFrom('+0200'); +$vTimezoneRuleStd->setTzOffsetTo('+0100'); +$stdRecurrenceRule = new \Eluceo\iCal\Property\Event\RecurrenceRule(); +$stdRecurrenceRule->setFreq(\Eluceo\iCal\Property\Event\RecurrenceRule::FREQ_YEARLY); +$stdRecurrenceRule->setByMonth(10); +$stdRecurrenceRule->setByDay('-1SU'); +$vTimezoneRuleStd->setRecurrenceRule($stdRecurrenceRule); + +// 4. Create timezone definition and add rules +$vTimezone = new \Eluceo\iCal\Component\Timezone($tz); +$vTimezone->addComponent($vTimezoneRuleDst); +$vTimezone->addComponent($vTimezoneRuleStd); +$vCalendar->setTimezone($vTimezone); + +// 5. Create an event +$vEvent = new \Eluceo\iCal\Component\Event(); +$vEvent->setDtStart(new \DateTime('2012-12-24', $dtz)); +$vEvent->setDtEnd(new \DateTime('2012-12-24', $dtz)); +$vEvent->setSummary('Summary with some german "umlauten" and a backslash \\: Kinder mögen Äpfel pflücken.'); + +// 6. Adding Timezone +$vEvent->setUseTimezone(true); + +// 7. Add event to calendar +$vCalendar->addComponent($vEvent); + +// 8. Set headers +header('Content-Type: text/calendar; charset=utf-8'); +header('Content-Disposition: attachment; filename="cal.ics"'); + +// 9. Output +echo $vCalendar->render(); diff --git a/vendor/eluceo/ical/examples/example6.php b/vendor/eluceo/ical/examples/example6.php new file mode 100644 index 0000000000..104009932b --- /dev/null +++ b/vendor/eluceo/ical/examples/example6.php @@ -0,0 +1,30 @@ +setDtStart(new \DateTime('2012-12-24')); +$vEvent->setDtEnd(new \DateTime('2012-12-24')); +$vEvent->setNoTime(true); +$vEvent->setSummary('Christmas'); + +// add some location information for apple devices +$vEvent->setLocation("Infinite Loop\nCupertino CA 95014", 'Infinite Loop', '37.332095,-122.030743'); + +// 3. Add event to calendar +$vCalendar->addComponent($vEvent); + +// 4. Set headers +header('Content-Type: text/calendar; charset=utf-8'); +header('Content-Disposition: attachment; filename="cal.ics"'); + +// 5. Output +echo $vCalendar->render(); diff --git a/vendor/eluceo/ical/examples/example7.php b/vendor/eluceo/ical/examples/example7.php new file mode 100644 index 0000000000..1f8013e79b --- /dev/null +++ b/vendor/eluceo/ical/examples/example7.php @@ -0,0 +1,33 @@ +setDtStart(new \DateTime('2012-12-24')); +$vEvent->setDtEnd(new \DateTime('2012-12-24')); +$vEvent->setNoTime(true); +$vEvent->setSummary('Christmas'); +$vEvent->setDescription('Happy Christmas!'); +$vEvent->setDescriptionHTML('Happy Christmas!'); + + +// add some location information for apple devices +$vEvent->setLocation("Infinite Loop\nCupertino CA 95014", 'Infinite Loop', '37.332095,-122.030743'); + +// 3. Add event to calendar +$vCalendar->addComponent($vEvent); + +// 4. Set headers +header('Content-Type: text/calendar; charset=utf-8'); +header('Content-Disposition: attachment; filename="cal.ics"'); + +// 5. Output +echo $vCalendar->render(); diff --git a/vendor/eluceo/ical/phpunit.xml.dist b/vendor/eluceo/ical/phpunit.xml.dist new file mode 100644 index 0000000000..13e18c0e41 --- /dev/null +++ b/vendor/eluceo/ical/phpunit.xml.dist @@ -0,0 +1,21 @@ + + + + + + + + + ./tests/Eluceo/iCal/ + + + diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/Component.php b/vendor/eluceo/ical/src/Eluceo/iCal/Component.php new file mode 100644 index 0000000000..76c9b2d946 --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/Component.php @@ -0,0 +1,172 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal; + +use Eluceo\iCal\Util\ComponentUtil; + +/** + * Abstract Calender Component. + */ +abstract class Component +{ + /** + * Array of Components. + * + * @var Component[] + */ + protected $components = array(); + + /** + * The order in which the components will be rendered during build. + * + * Not defined components will be appended at the end. + * + * @var array + */ + private $componentsBuildOrder = array('VTIMEZONE', 'DAYLIGHT', 'STANDARD'); + + /** + * The type of the concrete Component. + * + * @abstract + * + * @return string + */ + abstract public function getType(); + + /** + * Building the PropertyBag. + * + * @abstract + * + * @return PropertyBag + */ + abstract public function buildPropertyBag(); + + /** + * Adds a Component. + * + * If $key is given, the component at $key will be replaced else the component will be append. + * + * @param Component $component The Component that will be added + * @param null $key The key of the Component + */ + public function addComponent(Component $component, $key = null) + { + if (null == $key) { + $this->components[] = $component; + } else { + $this->components[$key] = $component; + } + } + + /** + * Renders an array containing the lines of the iCal file. + * + * @return array + */ + public function build() + { + $lines = array(); + + $lines[] = sprintf('BEGIN:%s', $this->getType()); + + /** @var $property Property */ + foreach ($this->buildPropertyBag() as $property) { + foreach ($property->toLines() as $l) { + $lines[] = $l; + } + } + + $this->buildComponents($lines); + + $lines[] = sprintf('END:%s', $this->getType()); + + $ret = array(); + + foreach ($lines as $line) { + foreach (ComponentUtil::fold($line) as $l) { + $ret[] = $l; + } + } + + return $ret; + } + + /** + * Renders the output. + * + * @return string + */ + public function render() + { + return implode("\r\n", $this->build()); + } + + /** + * Renders the output when treating the class as a string. + * + * @return string + */ + public function __toString() + { + return $this->render(); + } + + /** + * @param $lines + * + * @return array + */ + private function buildComponents(array &$lines) + { + $componentsByType = array(); + + /** @var $component Component */ + foreach ($this->components as $component) { + $type = $component->getType(); + if (!isset($componentsByType[$type])) { + $componentsByType[$type] = array(); + } + $componentsByType[$type][] = $component; + } + + // render ordered components + foreach ($this->componentsBuildOrder as $type) { + if (!isset($componentsByType[$type])) { + continue; + } + foreach ($componentsByType[$type] as $component) { + $this->addComponentLines($lines, $component); + } + unset($componentsByType[$type]); + } + + // render all other + foreach ($componentsByType as $components) { + foreach ($components as $component) { + $this->addComponentLines($lines, $component); + } + } + } + + /** + * @param array $lines + * @param Component $component + */ + private function addComponentLines(array &$lines, Component $component) + { + foreach ($component->build() as $l) { + $lines[] = $l; + } + } +} diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/Component/Alarm.php b/vendor/eluceo/ical/src/Eluceo/iCal/Component/Alarm.php new file mode 100644 index 0000000000..9e0f0c1b41 --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/Component/Alarm.php @@ -0,0 +1,151 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal\Component; + +use Eluceo\iCal\Component; +use Eluceo\iCal\PropertyBag; +use Eluceo\iCal\Property; + +/** + * Implementation of the VALARM component. + */ +class Alarm extends Component +{ + /** + * Alarm ACTION property. + * + * According to RFC 5545: 3.8.6.1. Action + * + * @link http://tools.ietf.org/html/rfc5545#section-3.8.6.1 + */ + const ACTION_AUDIO = 'AUDIO'; + const ACTION_DISPLAY = 'DISPLAY'; + const ACTION_EMAIL = 'EMAIL'; + + protected $action; + protected $repeat; + protected $duration; + protected $description; + protected $attendee; + protected $trigger; + + public function getType() + { + return 'VALARM'; + } + + public function getAction() + { + return $this->action; + } + + public function getRepeat() + { + return $this->repeat; + } + + public function getDuration() + { + return $this->duration; + } + + public function getDescription() + { + return $this->description; + } + + public function getAttendee() + { + return $this->attendee; + } + + public function getTrigger() + { + return $this->trigger; + } + + public function setAction($action) + { + $this->action = $action; + + return $this; + } + + public function setRepeat($repeat) + { + $this->repeat = $repeat; + + return $this; + } + + public function setDuration($duration) + { + $this->duration = $duration; + + return $this; + } + + public function setDescription($description) + { + $this->description = $description; + + return $this; + } + + public function setAttendee($attendee) + { + $this->attendee = $attendee; + + return $this; + } + + public function setTrigger($trigger) + { + $this->trigger = $trigger; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function buildPropertyBag() + { + $propertyBag = new PropertyBag(); + + if (null != $this->trigger) { + $propertyBag->set('TRIGGER', $this->trigger); + } + + if (null != $this->action) { + $propertyBag->set('ACTION', $this->action); + } + + if (null != $this->repeat) { + $propertyBag->set('REPEAT', $this->repeat); + } + + if (null != $this->duration) { + $propertyBag->set('DURATION', $this->duration); + } + + if (null != $this->description) { + $propertyBag->set('DESCRIPTION', $this->description); + } + + if (null != $this->attendee) { + $propertyBag->set('ATTENDEE', $this->attendee); + } + + return $propertyBag; + } +} diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/Component/Calendar.php b/vendor/eluceo/ical/src/Eluceo/iCal/Component/Calendar.php new file mode 100644 index 0000000000..db3dd98dc6 --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/Component/Calendar.php @@ -0,0 +1,323 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal\Component; + +use Eluceo\iCal\Component; +use Eluceo\iCal\PropertyBag; + +class Calendar extends Component +{ + /** + * Methods for calendar components. + * + * According to RFP 5545: 3.7.2. Method + * + * @link http://tools.ietf.org/html/rfc5545#section-3.7.2 + * + * And then according to RFC 2446: 3 APPLICATION PROTOCOL ELEMENTS + * @link https://www.ietf.org/rfc/rfc2446.txt + */ + const METHOD_PUBLISH = 'PUBLISH'; + const METHOD_REQUEST = 'REQUEST'; + const METHOD_REPLY = 'REPLY'; + const METHOD_ADD = 'ADD'; + const METHOD_CANCEL = 'CANCEL'; + const METHOD_REFRESH = 'REFRESH'; + const METHOD_COUNTER = 'COUNTER'; + const METHOD_DECLINECOUNTER = 'DECLINECOUNTER'; + + /** + * This property defines the calendar scale used for the calendar information specified in the iCalendar object. + * + * According to RFC 5545: 3.7.1. Calendar Scale + * + * @link http://tools.ietf.org/html/rfc5545#section-3.7 + */ + const CALSCALE_GREGORIAN = 'GREGORIAN'; + + /** + * The Product Identifier. + * + * According to RFC 2445: 4.7.3 Product Identifier + * + * This property specifies the identifier for the product that created the Calendar object. + * + * @link http://www.ietf.org/rfc/rfc2445.txt + * + * @var string + */ + protected $prodId = null; + protected $method = null; + protected $name = null; + protected $description = null; + protected $timezone = null; + + /** + * This property defines the calendar scale used for the + * calendar information specified in the iCalendar object. + * + * Also identifies the calendar type of a non-Gregorian recurring appointment. + * + * @var string + * + * @see http://tools.ietf.org/html/rfc5545#section-3.7 + * @see http://msdn.microsoft.com/en-us/library/ee237520(v=exchg.80).aspx + */ + protected $calendarScale = null; + + /** + * Specifies whether or not the iCalendar file only contains one appointment. + * + * @var bool + * + * @see http://msdn.microsoft.com/en-us/library/ee203486(v=exchg.80).aspx + */ + protected $forceInspectOrOpen = false; + + /** + * Specifies a globally unique identifier for the calendar. + * + * @var string + * + * @see http://msdn.microsoft.com/en-us/library/ee179588(v=exchg.80).aspx + */ + protected $calId = null; + + /** + * Specifies a suggested iCalendar file download frequency for clients and + * servers with sync capabilities. + * + * @var string + * + * @see http://msdn.microsoft.com/en-us/library/ee178699(v=exchg.80).aspx + */ + protected $publishedTTL = 'P1W'; + + /** + * Specifies a color for the calendar in calendar for Apple/Outlook. + * + * @var string + * + * @see http://msdn.microsoft.com/en-us/library/ee179588(v=exchg.80).aspx + */ + protected $calendarColor = null; + + public function __construct($prodId) + { + if (empty($prodId)) { + throw new \UnexpectedValueException('PRODID cannot be empty'); + } + + $this->prodId = $prodId; + } + + /** + * {@inheritdoc} + */ + public function getType() + { + return 'VCALENDAR'; + } + + /** + * @param $method + * + * @return $this + */ + public function setMethod($method) + { + $this->method = $method; + + return $this; + } + + /** + * @param $name + * + * @return $this + */ + public function setName($name) + { + $this->name = $name; + + return $this; + } + + /** + * @param $description + * + * @return $this + */ + public function setDescription($description) + { + $this->description = $description; + + return $this; + } + + /** + * @param $timezone + * + * @return $this + */ + public function setTimezone($timezone) + { + $this->timezone = $timezone; + + return $this; + } + + /** + * @param $calendarColor + * + * @return $this + */ + public function setCalendarColor($calendarColor) + { + $this->calendarColor = $calendarColor; + + return $this; + } + + /** + * @param $calendarScale + * + * @return $this + */ + public function setCalendarScale($calendarScale) + { + $this->calendarScale = $calendarScale; + + return $this; + } + + /** + * @param bool $forceInspectOrOpen + * + * @return $this + */ + public function setForceInspectOrOpen($forceInspectOrOpen) + { + $this->forceInspectOrOpen = $forceInspectOrOpen; + + return $this; + } + + /** + * @param string $calId + * + * @return $this + */ + public function setCalId($calId) + { + $this->calId = $calId; + + return $this; + } + + /** + * @param string $ttl + * + * @return $this + */ + public function setPublishedTTL($ttl) + { + $this->publishedTTL = $ttl; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function buildPropertyBag() + { + $propertyBag = new PropertyBag(); + $propertyBag->set('VERSION', '2.0'); + $propertyBag->set('PRODID', $this->prodId); + + if ($this->method) { + $propertyBag->set('METHOD', $this->method); + } + + if ($this->calendarColor) { + $propertyBag->set('X-APPLE-CALENDAR-COLOR', $this->calendarColor); + $propertyBag->set('X-OUTLOOK-COLOR', $this->calendarColor); + $propertyBag->set('X-FUNAMBOL-COLOR', $this->calendarColor); + } + + if ($this->calendarScale) { + $propertyBag->set('CALSCALE', $this->calendarScale); + $propertyBag->set('X-MICROSOFT-CALSCALE', $this->calendarScale); + } + + if ($this->name) { + $propertyBag->set('X-WR-CALNAME', $this->name); + } + + if ($this->description) { + $propertyBag->set('X-WR-CALDESC', $this->description); + } + + if ($this->timezone) { + if ($this->timezone instanceof Timezone) { + $propertyBag->set('X-WR-TIMEZONE', $this->timezone->getZoneIdentifier()); + $this->addComponent($this->timezone); + } else { + $propertyBag->set('X-WR-TIMEZONE', $this->timezone); + $this->addComponent(new Timezone($this->timezone)); + } + } + + if ($this->forceInspectOrOpen) { + $propertyBag->set('X-MS-OLK-FORCEINSPECTOROPEN', $this->forceInspectOrOpen); + } + + if ($this->calId) { + $propertyBag->set('X-WR-RELCALID', $this->calId); + } + + if ($this->publishedTTL) { + $propertyBag->set('X-PUBLISHED-TTL', $this->publishedTTL); + } + + return $propertyBag; + } + + /** + * Adds an Event to the Calendar. + * + * Wrapper for addComponent() + * + * @see Eluceo\iCal::addComponent + * @deprecated Please, use public method addComponent() from abstract Component class + * + * @param Event $event + */ + public function addEvent(Event $event) + { + $this->addComponent($event); + } + + /** + * @return null|string + */ + public function getProdId() + { + return $this->prodId; + } + + public function getMethod() + { + return $this->method; + } +} diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/Component/Event.php b/vendor/eluceo/ical/src/Eluceo/iCal/Component/Event.php new file mode 100644 index 0000000000..e93d506c61 --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/Component/Event.php @@ -0,0 +1,783 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal\Component; + +use Eluceo\iCal\Component; +use Eluceo\iCal\Property; +use Eluceo\iCal\Property\DateTimeProperty; +use Eluceo\iCal\Property\Event\Attendees; +use Eluceo\iCal\Property\Event\Organizer; +use Eluceo\iCal\Property\Event\RecurrenceRule; +use Eluceo\iCal\Property\Event\Description; +use Eluceo\iCal\PropertyBag; +use Eluceo\iCal\Property\Event\RecurrenceId; +use Eluceo\iCal\Property\DateTimesProperty; + +/** + * Implementation of the EVENT component. + */ +class Event extends Component +{ + const TIME_TRANSPARENCY_OPAQUE = 'OPAQUE'; + const TIME_TRANSPARENCY_TRANSPARENT = 'TRANSPARENT'; + + const STATUS_TENTATIVE = 'TENTATIVE'; + const STATUS_CONFIRMED = 'CONFIRMED'; + const STATUS_CANCELLED = 'CANCELLED'; + + /** + * @var string + */ + protected $uniqueId; + + /** + * The property indicates the date/time that the instance of + * the iCalendar object was created. + * + * The value MUST be specified in the UTC time format. + * + * @var \DateTime + */ + protected $dtStamp; + + /** + * @var \DateTime + */ + protected $dtStart; + + /** + * Preferentially chosen over the duration if both are set. + * + * @var \DateTime + */ + protected $dtEnd; + + /** + * @var \DateInterval + */ + protected $duration; + + /** + * @var bool + */ + protected $noTime = false; + + /** + * @var string + */ + protected $url; + + /** + * @var string + */ + protected $location; + + /** + * @var string + */ + protected $locationTitle; + + /** + * @var string + */ + protected $locationGeo; + + /** + * @var string + */ + protected $summary; + + /** + * @var Organizer + */ + protected $organizer; + + /** + * @see http://www.ietf.org/rfc/rfc2445.txt 4.8.2.7 Time Transparency + * + * @var string + */ + protected $transparency = self::TIME_TRANSPARENCY_OPAQUE; + + /** + * If set to true the timezone will be added to the event. + * + * @var bool + */ + protected $useTimezone = false; + + /** + * @var int + */ + protected $sequence = 0; + + /** + * @var Attendees + */ + protected $attendees; + + /** + * @var string + */ + protected $description; + + /** + * @var string + */ + protected $descriptionHTML; + + /** + * @var string + */ + protected $status; + + /** + * @var RecurrenceRule + */ + protected $recurrenceRule; + + /** + * This property specifies the date and time that the calendar + * information was created. + * + * The value MUST be specified in the UTC time format. + * + * @var \DateTime + */ + protected $created; + + /** + * The property specifies the date and time that the information + * associated with the calendar component was last revised. + * + * The value MUST be specified in the UTC time format. + * + * @var \DateTime + */ + protected $modified; + + /** + * Indicates if the UTC time should be used or not. + * + * @var bool + */ + protected $useUtc = true; + + /** + * @var bool + */ + protected $cancelled; + + /** + * This property is used to specify categories or subtypes + * of the calendar component. The categories are useful in searching + * for a calendar component of a particular type and category. + * + * @see https://tools.ietf.org/html/rfc5545#section-3.8.1.2 + * + * @var array + */ + protected $categories; + + /** + * https://tools.ietf.org/html/rfc5545#section-3.8.1.3. + * + * @var bool + */ + protected $isPrivate = false; + + /** + * Dates to be excluded from a series of events. + * + * @var \DateTime[] + */ + protected $exDates = array(); + + /** + * @var RecurrenceId + */ + protected $recurrenceId; + + public function __construct($uniqueId = null) + { + if (null == $uniqueId) { + $uniqueId = uniqid(); + } + + $this->uniqueId = $uniqueId; + } + + /** + * {@inheritdoc} + */ + public function getType() + { + return 'VEVENT'; + } + + /** + * {@inheritdoc} + */ + public function buildPropertyBag() + { + $propertyBag = new PropertyBag(); + + // mandatory information + $propertyBag->set('UID', $this->uniqueId); + + $propertyBag->add(new DateTimeProperty('DTSTART', $this->dtStart, $this->noTime, $this->useTimezone, $this->useUtc)); + $propertyBag->set('SEQUENCE', $this->sequence); + $propertyBag->set('TRANSP', $this->transparency); + + if ($this->status) { + $propertyBag->set('STATUS', $this->status); + } + + // An event can have a 'dtend' or 'duration', but not both. + if (null != $this->dtEnd) { + $propertyBag->add(new DateTimeProperty('DTEND', $this->dtEnd, $this->noTime, $this->useTimezone, $this->useUtc)); + } elseif (null != $this->duration) { + $propertyBag->set('DURATION', $this->duration->format('P%dDT%hH%iM%sS')); + } + + // optional information + if (null != $this->url) { + $propertyBag->set('URL', $this->url); + } + + if (null != $this->location) { + $propertyBag->set('LOCATION', $this->location); + + if (null != $this->locationGeo) { + $propertyBag->add( + new Property( + 'X-APPLE-STRUCTURED-LOCATION', + 'geo:' . $this->locationGeo, + array( + 'VALUE' => 'URI', + 'X-ADDRESS' => $this->location, + 'X-APPLE-RADIUS' => 49, + 'X-TITLE' => $this->locationTitle, + ) + ) + ); + $propertyBag->set('GEO', str_replace(',', ';', $this->locationGeo)); + } + } + + if (null != $this->summary) { + $propertyBag->set('SUMMARY', $this->summary); + } + + if (null != $this->attendees) { + $propertyBag->add($this->attendees); + } + + $propertyBag->set('CLASS', $this->isPrivate ? 'PRIVATE' : 'PUBLIC'); + + if (null != $this->description) { + $propertyBag->set('DESCRIPTION', new Description($this->description)); + } + + if (null != $this->descriptionHTML) { + $propertyBag->add( + new Property( + 'X-ALT-DESC', + $this->descriptionHTML, + array( + 'FMTTYPE' => 'text/html', + ) + ) + ); + } + + if (null != $this->recurrenceRule) { + $propertyBag->set('RRULE', $this->recurrenceRule); + } + + if (null != $this->recurrenceId) { + $this->recurrenceId->applyTimeSettings($this->noTime, $this->useTimezone, $this->useUtc); + $propertyBag->add($this->recurrenceId); + } + + if (!empty($this->exDates)) { + $propertyBag->add(new DateTimesProperty('EXDATE', $this->exDates, $this->noTime, $this->useTimezone, $this->useUtc)); + } + + if ($this->cancelled) { + $propertyBag->set('STATUS', 'CANCELLED'); + } + + if (null != $this->organizer) { + $propertyBag->add($this->organizer); + } + + if ($this->noTime) { + $propertyBag->set('X-MICROSOFT-CDO-ALLDAYEVENT', 'TRUE'); + } + + if (null != $this->categories) { + $propertyBag->set('CATEGORIES', $this->categories); + } + + $propertyBag->add( + new DateTimeProperty('DTSTAMP', $this->dtStamp ?: new \DateTime(), false, false, true) + ); + + if ($this->created) { + $propertyBag->add(new DateTimeProperty('CREATED', $this->created, false, false, true)); + } + + if ($this->modified) { + $propertyBag->add(new DateTimeProperty('LAST-MODIFIED', $this->modified, false, false, true)); + } + + return $propertyBag; + } + + /** + * @param $dtEnd + * + * @return $this + */ + public function setDtEnd($dtEnd) + { + $this->dtEnd = $dtEnd; + + return $this; + } + + public function getDtEnd() + { + return $this->dtEnd; + } + + public function setDtStart($dtStart) + { + $this->dtStart = $dtStart; + + return $this; + } + + /** + * @param $dtStamp + * + * @return $this + */ + public function setDtStamp($dtStamp) + { + $this->dtStamp = $dtStamp; + + return $this; + } + + /** + * @param $duration + * + * @return $this + */ + public function setDuration($duration) + { + $this->duration = $duration; + + return $this; + } + + /** + * @param $location + * @param string $title + * @param null $geo + * + * @return $this + */ + public function setLocation($location, $title = '', $geo = null) + { + $this->location = $location; + $this->locationTitle = $title; + $this->locationGeo = $geo; + + return $this; + } + + /** + * @param $noTime + * + * @return $this + */ + public function setNoTime($noTime) + { + $this->noTime = $noTime; + + return $this; + } + + /** + * @param int $sequence + * + * @return $this + */ + public function setSequence($sequence) + { + $this->sequence = $sequence; + + return $this; + } + + /** + * @return int + */ + public function getSequence() + { + return $this->sequence; + } + + /** + * @param Organizer $organizer + * + * @return $this + */ + public function setOrganizer(Organizer $organizer) + { + $this->organizer = $organizer; + + return $this; + } + + /** + * @param $summary + * + * @return $this + */ + public function setSummary($summary) + { + $this->summary = $summary; + + return $this; + } + + /** + * @param $uniqueId + * + * @return $this + */ + public function setUniqueId($uniqueId) + { + $this->uniqueId = $uniqueId; + + return $this; + } + + /** + * @return string + */ + public function getUniqueId() + { + return $this->uniqueId; + } + + /** + * @param $url + * + * @return $this + */ + public function setUrl($url) + { + $this->url = $url; + + return $this; + } + + /** + * @param $useTimezone + * + * @return $this + */ + public function setUseTimezone($useTimezone) + { + $this->useTimezone = $useTimezone; + + return $this; + } + + /** + * @return bool + */ + public function getUseTimezone() + { + return $this->useTimezone; + } + + /** + * @param Attendees $attendees + * + * @return $this + */ + public function setAttendees(Attendees $attendees) + { + $this->attendees = $attendees; + + return $this; + } + + /** + * @param string $attendee + * @param array $params + * + * @return $this + */ + public function addAttendee($attendee, $params = array()) + { + if (!isset($this->attendees)) { + $this->attendees = new Attendees(); + } + $this->attendees->add($attendee, $params); + + return $this; + } + + /** + * @return Attendees + */ + public function getAttendees() + { + return $this->attendees; + } + + /** + * @param $description + * + * @return $this + */ + public function setDescription($description) + { + $this->description = $description; + + return $this; + } + + /** + * @param $descriptionHTML + * + * @return $this + */ + public function setDescriptionHTML($descriptionHTML) + { + $this->descriptionHTML = $descriptionHTML; + + return $this; + } + + /** + * @param bool $useUtc + * + * @return $this + */ + public function setUseUtc($useUtc = true) + { + $this->useUtc = $useUtc; + + return $this; + } + + /** + * @return string + */ + public function getDescription() + { + return $this->description; + } + + /** + * @return string + */ + public function getDescriptionHTML() + { + return $this->descriptionHTML; + } + + /** + * @param $status + * + * @return $this + */ + public function setCancelled($status) + { + $this->cancelled = (bool) $status; + + return $this; + } + + /** + * @param $transparency + * + * @return $this + * + * @throws \InvalidArgumentException + */ + public function setTimeTransparency($transparency) + { + $transparency = strtoupper($transparency); + if ($transparency === self::TIME_TRANSPARENCY_OPAQUE + || $transparency === self::TIME_TRANSPARENCY_TRANSPARENT + ) { + $this->transparency = $transparency; + } else { + throw new \InvalidArgumentException('Invalid value for transparancy'); + } + + return $this; + } + + /** + * @param $status + * + * @return $this + * + * @throws \InvalidArgumentException + */ + public function setStatus($status) + { + $status = strtoupper($status); + if ($status == self::STATUS_CANCELLED + || $status == self::STATUS_CONFIRMED + || $status == self::STATUS_TENTATIVE + ) { + $this->status = $status; + } else { + throw new \InvalidArgumentException('Invalid value for status'); + } + + return $this; + } + + /** + * @param RecurrenceRule $recurrenceRule + * + * @return $this + */ + public function setRecurrenceRule(RecurrenceRule $recurrenceRule) + { + $this->recurrenceRule = $recurrenceRule; + + return $this; + } + + /** + * @return RecurrenceRule + */ + public function getRecurrenceRule() + { + return $this->recurrenceRule; + } + + /** + * @param $dtStamp + * + * @return $this + */ + public function setCreated($dtStamp) + { + $this->created = $dtStamp; + + return $this; + } + + /** + * @param $dtStamp + * + * @return $this + */ + public function setModified($dtStamp) + { + $this->modified = $dtStamp; + + return $this; + } + + /** + * @param $categories + * + * @return $this + */ + public function setCategories($categories) + { + $this->categories = $categories; + + return $this; + } + + /** + * Sets the event privacy. + * + * @param bool $flag + * + * @return $this + */ + public function setIsPrivate($flag) + { + $this->isPrivate = (bool) $flag; + + return $this; + } + + /** + * @param \DateTime $dateTime + * + * @return \Eluceo\iCal\Component\Event + */ + public function addExDate(\DateTime $dateTime) + { + $this->exDates[] = $dateTime; + + return $this; + } + + /** + * @return \DateTime[] + */ + public function getExDates() + { + return $this->exDates; + } + + /** + * @param \DateTime[] + * + * @return \Eluceo\iCal\Component\Event + */ + public function setExDates(array $exDates) + { + $this->exDates = $exDates; + + return $this; + } + + /** + * @return \Eluceo\iCal\Property\Event\RecurrenceId + */ + public function getRecurrenceId() + { + return $this->recurrenceId; + } + + /** + * @param RecurrenceId $recurrenceId + * + * @return \Eluceo\iCal\Component\Event + */ + public function setRecurrenceId(RecurrenceId $recurrenceId) + { + $this->recurrenceId = $recurrenceId; + + return $this; + } +} diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/Component/Timezone.php b/vendor/eluceo/ical/src/Eluceo/iCal/Component/Timezone.php new file mode 100644 index 0000000000..c820d75b4d --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/Component/Timezone.php @@ -0,0 +1,57 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal\Component; + +use Eluceo\iCal\Component; +use Eluceo\iCal\PropertyBag; + +/** + * Implementation of the TIMEZONE component. + */ +class Timezone extends Component +{ + /** + * @var string + */ + protected $timezone; + + public function __construct($timezone) + { + $this->timezone = $timezone; + } + + /** + * {@inheritdoc} + */ + public function getType() + { + return 'VTIMEZONE'; + } + + /** + * {@inheritdoc} + */ + public function buildPropertyBag() + { + $propertyBag = new PropertyBag(); + + $propertyBag->set('TZID', $this->timezone); + $propertyBag->set('X-LIC-LOCATION', $this->timezone); + + return $propertyBag; + } + + public function getZoneIdentifier() + { + return $this->timezone; + } +} diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/Component/TimezoneRule.php b/vendor/eluceo/ical/src/Eluceo/iCal/Component/TimezoneRule.php new file mode 100644 index 0000000000..97da491197 --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/Component/TimezoneRule.php @@ -0,0 +1,215 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal\Component; + +use Eluceo\iCal\Component; +use Eluceo\iCal\PropertyBag; +use Eluceo\iCal\Property\Event\RecurrenceRule; + +/** + * Implementation of Standard Time and Daylight Saving Time observances (or rules) + * which define the TIMEZONE component. + */ +class TimezoneRule extends Component +{ + const TYPE_DAYLIGHT = 'DAYLIGHT'; + const TYPE_STANDARD = 'STANDARD'; + + /** + * @var string + */ + protected $type; + + /** + * @var string + */ + protected $tzOffsetFrom; + + /** + * @var string + */ + protected $tzOffsetTo; + + /** + * @var string + */ + protected $tzName; + + /** + * @var \DateTime + */ + protected $dtStart; + + /** + * @var RecurrenceRule + */ + protected $recurrenceRule; + + /** + * create new Timezone Rule object by giving a rule type identifier. + * + * @param string $ruleType one of DAYLIGHT or STANDARD + * + * @throws \InvalidArgumentException + */ + public function __construct($ruleType) + { + $ruleType = strtoupper($ruleType); + if ($ruleType === self::TYPE_DAYLIGHT || $ruleType === self::TYPE_STANDARD) { + $this->type = $ruleType; + } else { + throw new \InvalidArgumentException('Invalid value for timezone rule type'); + } + } + + /** + * {@inheritdoc} + */ + public function buildPropertyBag() + { + $propertyBag = new PropertyBag(); + + if ($this->getTzName()) { + $propertyBag->set('TZNAME', $this->getTzName()); + } + + if ($this->getTzOffsetFrom()) { + $propertyBag->set('TZOFFSETFROM', $this->getTzOffsetFrom()); + } + + if ($this->getTzOffsetTo()) { + $propertyBag->set('TZOFFSETTO', $this->getTzOffsetTo()); + } + + if ($this->getDtStart()) { + $propertyBag->set('DTSTART', $this->getDtStart()); + } + + if ($this->recurrenceRule) { + $propertyBag->set('RRULE', $this->recurrenceRule); + } + + return $propertyBag; + } + + /** + * @param $offset + * + * @return $this + */ + public function setTzOffsetFrom($offset) + { + $this->tzOffsetFrom = $offset; + + return $this; + } + + /** + * @param $offset + * + * @return $this + */ + public function setTzOffsetTo($offset) + { + $this->tzOffsetTo = $offset; + + return $this; + } + + /** + * @param $name + * + * @return $this + */ + public function setTzName($name) + { + $this->tzName = $name; + + return $this; + } + + /** + * @param \DateTime $dtStart + * + * @return $this + */ + public function setDtStart(\DateTime $dtStart) + { + $this->dtStart = $dtStart; + + return $this; + } + + /** + * @param RecurrenceRule $recurrenceRule + * + * @return $this + */ + public function setRecurrenceRule(RecurrenceRule $recurrenceRule) + { + $this->recurrenceRule = $recurrenceRule; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getType() + { + return $this->type; + } + + /** + * @return string + */ + public function getTzOffsetFrom() + { + return $this->tzOffsetFrom; + } + + /** + * @return string + */ + public function getTzOffsetTo() + { + return $this->tzOffsetTo; + } + + /** + * @return string + */ + public function getTzName() + { + return $this->tzName; + } + + /** + * @return RecurrenceRule + */ + public function getRecurrenceRule() + { + return $this->recurrenceRule; + } + + /** + * @return mixed return string representation of start date or NULL if no date was given + */ + public function getDtStart() + { + if ($this->dtStart) { + return $this->dtStart->format('Ymd\THis'); + } + + return; + } +} diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/ParameterBag.php b/vendor/eluceo/ical/src/Eluceo/iCal/ParameterBag.php new file mode 100644 index 0000000000..9b0c24a126 --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/ParameterBag.php @@ -0,0 +1,108 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal; + +class ParameterBag +{ + /** + * The params. + * + * @var array + */ + protected $params; + + public function __construct($params = array()) + { + $this->params = $params; + } + + /** + * @param string $name + * @param mixed $value + */ + public function setParam($name, $value) + { + $this->params[$name] = $value; + } + + /** + * @param $name + */ + public function getParam($name) + { + if (array_key_exists($name, $this->params)) { + return $this->params[$name]; + } + } + + /** + * Checks if there are any params. + * + * @return bool + */ + public function hasParams() + { + return count($this->params) > 0; + } + + /** + * @return string + */ + public function toString() + { + $line = ''; + foreach ($this->params as $param => $paramValues) { + if (!is_array($paramValues)) { + $paramValues = array($paramValues); + } + foreach ($paramValues as $k => $v) { + $paramValues[$k] = $this->escapeParamValue($v); + } + + if ('' != $line) { + $line .= ';'; + } + + $line .= $param . '=' . implode(',', $paramValues); + } + + return $line; + } + + /** + * Returns an escaped string for a param value. + * + * @param string $value + * + * @return string + */ + public function escapeParamValue($value) + { + $count = 0; + $value = str_replace('\\', '\\\\', $value); + $value = str_replace('"', '\"', $value, $count); + $value = str_replace("\n", '\\n', $value); + if (false !== strpos($value, ';') || false !== strpos($value, ',') || false !== strpos($value, ':') || $count) { + $value = '"' . $value . '"'; + } + + return $value; + } + + /** + * @return string + */ + public function __toString() + { + return $this->toString(); + } +} diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/Property.php b/vendor/eluceo/ical/src/Eluceo/iCal/Property.php new file mode 100644 index 0000000000..1bf2c3f097 --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/Property.php @@ -0,0 +1,148 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal; + +use Eluceo\iCal\Property\ArrayValue; +use Eluceo\iCal\Property\StringValue; +use Eluceo\iCal\Property\ValueInterface; + +/** + * The Property Class represents a property as defined in RFC 2445. + * + * The content of a line (unfolded) will be rendered in this class + */ +class Property +{ + /** + * The value of the Property. + * + * @var ValueInterface + */ + protected $value; + + /** + * The params of the Property. + * + * @var ParameterBag + */ + protected $parameterBag; + + /** + * @var string + */ + protected $name; + + /** + * @param $name + * @param $value + * @param array $params + */ + public function __construct($name, $value, $params = array()) + { + $this->name = $name; + $this->setValue($value); + $this->parameterBag = new ParameterBag($params); + } + + /** + * Renders an unfolded line. + * + * @return string + */ + public function toLine() + { + // Property-name + $line = $this->getName(); + + // Adding params + //@todo added check for $this->parameterBag because doctrine/orm proxies won't execute constructor - ok? + if ($this->parameterBag && $this->parameterBag->hasParams()) { + $line .= ';' . $this->parameterBag->toString(); + } + + // Property value + $line .= ':' . $this->value->getEscapedValue(); + + return $line; + } + + /** + * Get all unfolded lines. + * + * @return array + */ + public function toLines() + { + return array($this->toLine()); + } + + /** + * @param string $name + * @param mixed $value + * + * @return $this + */ + public function setParam($name, $value) + { + $this->parameterBag->setParam($name, $value); + + return $this; + } + + /** + * @param $name + */ + public function getParam($name) + { + return $this->parameterBag->getParam($name); + } + + /** + * @param mixed $value + * + * @return $this + * + * @throws \Exception + */ + public function setValue($value) + { + if (is_scalar($value)) { + $this->value = new StringValue($value); + } elseif (is_array($value)) { + $this->value = new ArrayValue($value); + } else { + if (!$value instanceof ValueInterface) { + throw new \Exception('The value must implement the ValueInterface.'); + } else { + $this->value = $value; + } + } + + return $this; + } + + /** + * @return mixed + */ + public function getValue() + { + return $this->value; + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } +} diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/Property/ArrayValue.php b/vendor/eluceo/ical/src/Eluceo/iCal/Property/ArrayValue.php new file mode 100644 index 0000000000..314787a0d3 --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/Property/ArrayValue.php @@ -0,0 +1,43 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal\Property; + +use Eluceo\iCal\Util\PropertyValueUtil; + +class ArrayValue implements ValueInterface +{ + /** + * The value. + * + * @var array + */ + protected $values; + + public function __construct(array $values) + { + $this->values = $values; + } + + public function setValues(array $values) + { + $this->values = $values; + + return $this; + } + + public function getEscapedValue() + { + return implode(',', array_map(function ($value) { + return PropertyValueUtil::escapeValue((string) $value); + }, $this->values)); + } +} diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/Property/DateTimeProperty.php b/vendor/eluceo/ical/src/Eluceo/iCal/Property/DateTimeProperty.php new file mode 100644 index 0000000000..d0fd495e79 --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/Property/DateTimeProperty.php @@ -0,0 +1,38 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal\Property; + +use Eluceo\iCal\Property; +use Eluceo\iCal\Util\DateUtil; + +class DateTimeProperty extends Property +{ + /** + * @param string $name + * @param \DateTime $dateTime + * @param bool $noTime + * @param bool $useTimezone + * @param bool $useUtc + */ + public function __construct( + $name, + \DateTime $dateTime = null, + $noTime = false, + $useTimezone = false, + $useUtc = false + ) { + $dateString = DateUtil::getDateString($dateTime, $noTime, $useTimezone, $useUtc); + $params = DateUtil::getDefaultParams($dateTime, $noTime, $useTimezone); + + parent::__construct($name, $dateString, $params); + } +} diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/Property/DateTimesProperty.php b/vendor/eluceo/ical/src/Eluceo/iCal/Property/DateTimesProperty.php new file mode 100644 index 0000000000..6242f2858e --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/Property/DateTimesProperty.php @@ -0,0 +1,41 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal\Property; + +use Eluceo\iCal\Property; +use Eluceo\iCal\Util\DateUtil; + +class DateTimesProperty extends Property +{ + /** + * @param string $name + * @param \DateTime[] $dateTimes + * @param bool $noTime + * @param bool $useTimezone + * @param bool $useUtc + */ + public function __construct( + $name, + $dateTimes = array(), + $noTime = false, + $useTimezone = false, + $useUtc = false + ) { + $dates = array(); + foreach ($dateTimes as $dateTime) { + $dates[] = DateUtil::getDateString($dateTime, $noTime, $useTimezone, $useUtc); + } + $params = DateUtil::getDefaultParams($dateTime, $noTime, $useTimezone); + + parent::__construct($name, $dates, $params); + } +} diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/Property/Event/Attendees.php b/vendor/eluceo/ical/src/Eluceo/iCal/Property/Event/Attendees.php new file mode 100644 index 0000000000..dbb36e7dfe --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/Property/Event/Attendees.php @@ -0,0 +1,102 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal\Property\Event; + +use Eluceo\iCal\Property; + +class Attendees extends Property +{ + /** @var Property[] */ + protected $attendees = array(); + + const PROPERTY_NAME = 'ATTENDEES'; + + public function __construct() + { + // Overwrites constructor functionality of Property + } + + /** + * @param $value + * @param array $params + * + * @return $this + */ + public function add($value, $params = array()) + { + $this->attendees[] = new Property('ATTENDEE', $value, $params); + + return $this; + } + + /** + * @param Property[] $value + * + * @return $this + */ + public function setValue($value) + { + $this->attendees = $value; + + return $this; + } + + /** + * @return Property[] + */ + public function getValue() + { + return $this->attendees; + } + + /** + * {@inheritdoc} + */ + public function toLines() + { + $lines = array(); + foreach ($this->attendees as $attendee) { + $lines[] = $attendee->toLine(); + } + + return $lines; + } + + /** + * @param string $name + * @param mixed $value + * + * @throws \BadMethodCallException + */ + public function setParam($name, $value) + { + throw new \BadMethodCallException('Cannot call setParam on Attendees Property'); + } + + /** + * @param $name + * + * @throws \BadMethodCallException + */ + public function getParam($name) + { + throw new \BadMethodCallException('Cannot call getParam on Attendees Property'); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return self::PROPERTY_NAME; + } +} diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/Property/Event/Description.php b/vendor/eluceo/ical/src/Eluceo/iCal/Property/Event/Description.php new file mode 100644 index 0000000000..b0dbb3c1e4 --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/Property/Event/Description.php @@ -0,0 +1,66 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal\Property\Event; + +use Eluceo\iCal\Property\ValueInterface; +use Eluceo\iCal\Util\PropertyValueUtil; + +/** + * Class Description + * Alows new line charectars to be in the description. + */ +class Description implements ValueInterface +{ + /** + * The value. + * + * @var string + */ + protected $value; + + public function __construct($value) + { + $this->value = $value; + } + + /** + * Return the value of the Property as an escaped string. + * + * Escape values as per RFC 2445. See http://www.kanzaki.com/docs/ical/text.html + * + * @return string + */ + public function getEscapedValue() + { + return PropertyValueUtil::escapeValue((string) $this->value); + } + + /** + * @param string $value + * + * @return $this + */ + public function setValue($value) + { + $this->value = $value; + + return $this; + } + + /** + * @return string + */ + public function getValue() + { + return $this->value; + } +} diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/Property/Event/Organizer.php b/vendor/eluceo/ical/src/Eluceo/iCal/Property/Event/Organizer.php new file mode 100644 index 0000000000..a91a75d281 --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/Property/Event/Organizer.php @@ -0,0 +1,39 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal\Property\Event; + +use Eluceo\iCal\Property; + +/** + * Class Organizer. + */ +class Organizer extends Property +{ + const PROPERTY_NAME = 'ORGANIZER'; + + /** + * @param string $value + * @param array $params + */ + public function __construct($value, $params = array()) + { + parent::__construct(self::PROPERTY_NAME, $value, $params); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return self::PROPERTY_NAME; + } +} diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/Property/Event/RecurrenceId.php b/vendor/eluceo/ical/src/Eluceo/iCal/Property/Event/RecurrenceId.php new file mode 100644 index 0000000000..2a684dda9e --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/Property/Event/RecurrenceId.php @@ -0,0 +1,130 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal\Property\Event; + +use Eluceo\iCal\ParameterBag; +use Eluceo\iCal\Property; +use Eluceo\iCal\Util\DateUtil; +use Eluceo\iCal\Property\ValueInterface; + +/** + * Implementation of Recurrence Id. + * + * @see http://www.ietf.org/rfc/rfc2445.txt 4.8.4.4 Recurrence ID + */ +class RecurrenceId extends Property +{ + const PROPERTY_NAME = 'RECURRENCE-ID'; + + /** + * The effective range of recurrence instances from the instance + * specified by the recurrence identifier specified by the property. + */ + const RANGE_THISANDPRIOR = 'THISANDPRIOR'; + const RANGE_THISANDFUTURE = 'THISANDFUTURE'; + + /** + * The dateTime to identify a particular instance of a recurring event which is getting modified. + * + * @var \DateTime + */ + protected $dateTime; + + /** + * Specify the effective range of recurrence instances from the instance. + * + * @var string + */ + protected $range; + + public function __construct(\DateTime $dateTime = null) + { + $this->parameterBag = new ParameterBag(); + if (isset($dateTime)) { + $this->dateTime = $dateTime; + } + } + + public function applyTimeSettings($noTime = false, $useTimezone = false, $useUtc = false) + { + $params = DateUtil::getDefaultParams($this->dateTime, $noTime, $useTimezone, $useUtc); + foreach ($params as $name => $value) { + $this->parameterBag->setParam($name, $value); + } + + if ($this->range) { + $this->parameterBag->setParam('RANGE', $this->range); + } + + $this->setValue(DateUtil::getDateString($this->dateTime, $noTime, $useTimezone, $useUtc)); + } + + /** + * @return DateTime + */ + public function getDatetime() + { + return $this->dateTime; + } + + /** + * @param \DateTime $dateTime + * + * @return \Eluceo\iCal\Property\Event\RecurrenceId + */ + public function setDatetime(\DateTime $dateTime) + { + $this->dateTime = $dateTime; + + return $this; + } + + /** + * @return string + */ + public function getRange() + { + return $this->range; + } + + /** + * @param string $range + * + * @return \Eluceo\iCal\Property\Event\RecurrenceId + */ + public function setRange($range) + { + $this->range = $range; + } + + /** + * Get all unfolded lines. + * + * @return array + */ + public function toLines() + { + if (!$this->value instanceof ValueInterface) { + throw new \Exception('The value must implement the ValueInterface. Call RecurrenceId::applyTimeSettings() before adding RecurrenceId.'); + } else { + return parent::toLines(); + } + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return self::PROPERTY_NAME; + } +} diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/Property/Event/RecurrenceRule.php b/vendor/eluceo/ical/src/Eluceo/iCal/Property/Event/RecurrenceRule.php new file mode 100644 index 0000000000..32533449ca --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/Property/Event/RecurrenceRule.php @@ -0,0 +1,444 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal\Property\Event; + +use Eluceo\iCal\Property\ValueInterface; +use Eluceo\iCal\ParameterBag; +use InvalidArgumentException; + +/** + * Implementation of Recurrence Rule. + * + * @see http://www.ietf.org/rfc/rfc2445.txt 3.3.10. Recurrence Rule + */ +class RecurrenceRule implements ValueInterface +{ + const FREQ_YEARLY = 'YEARLY'; + const FREQ_MONTHLY = 'MONTHLY'; + const FREQ_WEEKLY = 'WEEKLY'; + const FREQ_DAILY = 'DAILY'; + + const WEEKDAY_SUNDAY = 'SU'; + const WEEKDAY_MONDAY = 'MO'; + const WEEKDAY_TUESDAY = 'TU'; + const WEEKDAY_WEDNESDAY = 'WE'; + const WEEKDAY_THURSDAY = 'TH'; + const WEEKDAY_FRIDAY = 'FR'; + const WEEKDAY_SATURDAY = 'SA'; + + /** + * The frequency of an Event. + * + * @var string + */ + protected $freq = self::FREQ_YEARLY; + + /** + * @var null|int + */ + protected $interval = 1; + + /** + * @var null|int + */ + protected $count = null; + + /** + * @var null|\DateTime + */ + protected $until = null; + + /** + * @var null|string + */ + protected $wkst; + + /** + * @var null|string + */ + protected $byMonth; + + /** + * @var null|string + */ + protected $byWeekNo; + + /** + * @var null|string + */ + protected $byYearDay; + + /** + * @var null|string + */ + protected $byMonthDay; + + /** + * @var null|string + */ + protected $byDay; + + /** + * @var null|string + */ + protected $byHour; + + /** + * @var null|string + */ + protected $byMinute; + + /** + * @var null|string + */ + protected $bySecond; + + /** + * Return the value of the Property as an escaped string. + * + * Escape values as per RFC 2445. See http://www.kanzaki.com/docs/ical/text.html + * + * @return string + */ + public function getEscapedValue() + { + return $this->buildParameterBag()->toString(); + } + + /** + * @return ParameterBag + */ + protected function buildParameterBag() + { + $parameterBag = new ParameterBag(); + + $parameterBag->setParam('FREQ', $this->freq); + + if (null !== $this->interval) { + $parameterBag->setParam('INTERVAL', $this->interval); + } + + if (null !== $this->count) { + $parameterBag->setParam('COUNT', $this->count); + } + + if (null != $this->until) { + $parameterBag->setParam('UNTIL', $this->until->format('Ymd\THis\Z')); + } + + if (null !== $this->wkst) { + $parameterBag->setParam('WKST', $this->wkst); + } + + if (null !== $this->byMonth) { + $parameterBag->setParam('BYMONTH', $this->byMonth); + } + + if (null !== $this->byWeekNo) { + $parameterBag->setParam('BYWEEKNO', $this->byWeekNo); + } + + if (null !== $this->byYearDay) { + $parameterBag->setParam('BYYEARDAY', $this->byYearDay); + } + + if (null !== $this->byMonthDay) { + $parameterBag->setParam('BYMONTHDAY', $this->byMonthDay); + } + + if (null !== $this->byDay) { + $parameterBag->setParam('BYDAY', $this->byDay); + } + + if (null !== $this->byHour) { + $parameterBag->setParam('BYHOUR', $this->byHour); + } + + if (null !== $this->byMinute) { + $parameterBag->setParam('BYMINUTE', $this->byMinute); + } + + if (null !== $this->bySecond) { + $parameterBag->setParam('BYSECOND', $this->bySecond); + } + + return $parameterBag; + } + + /** + * @param int|null $count + * + * @return $this + */ + public function setCount($count) + { + $this->count = $count; + + return $this; + } + + /** + * @return int|null + */ + public function getCount() + { + return $this->count; + } + + /** + * @param \DateTime|null $until + * + * @return $this + */ + public function setUntil(\DateTime $until = null) + { + $this->until = $until; + + return $this; + } + + /** + * @return \DateTime|null + */ + public function getUntil() + { + return $this->until; + } + + /** + * The FREQ rule part identifies the type of recurrence rule. This + * rule part MUST be specified in the recurrence rule. Valid values + * include. + * + * SECONDLY, to specify repeating events based on an interval of a second or more; + * MINUTELY, to specify repeating events based on an interval of a minute or more; + * HOURLY, to specify repeating events based on an interval of an hour or more; + * DAILY, to specify repeating events based on an interval of a day or more; + * WEEKLY, to specify repeating events based on an interval of a week or more; + * MONTHLY, to specify repeating events based on an interval of a month or more; + * YEARLY, to specify repeating events based on an interval of a year or more. + * + * @param string $freq + * + * @return $this + * + * @throws \InvalidArgumentException + */ + public function setFreq($freq) + { + if (self::FREQ_YEARLY === $freq || self::FREQ_MONTHLY === $freq + || self::FREQ_WEEKLY === $freq + || self::FREQ_DAILY === $freq + ) { + $this->freq = $freq; + } else { + throw new \InvalidArgumentException("The Frequency {$freq} is not supported."); + } + + return $this; + } + + /** + * @return string + */ + public function getFreq() + { + return $this->freq; + } + + /** + * The INTERVAL rule part contains a positive integer representing at + * which intervals the recurrence rule repeats. + * + * @param int|null $interval + * + * @return $this + */ + public function setInterval($interval) + { + $this->interval = $interval; + + return $this; + } + + /** + * @return int|null + */ + public function getInterval() + { + return $this->interval; + } + + /** + * The WKST rule part specifies the day on which the workweek starts. + * Valid values are MO, TU, WE, TH, FR, SA, and SU. + * + * @param string $value + * + * @return $this + */ + public function setWkst($value) + { + $this->wkst = $value; + + return $this; + } + + /** + * The BYMONTH rule part specifies a COMMA-separated list of months of the year. + * Valid values are 1 to 12. + * + * @param int $month + * + * @throws InvalidArgumentException + * + * @return $this + */ + public function setByMonth($month) + { + if (!is_integer($month) || $month < 0 || $month > 12) { + throw new InvalidArgumentException('Invalid value for BYMONTH'); + } + + $this->byMonth = $month; + + return $this; + } + + /** + * The BYWEEKNO rule part specifies a COMMA-separated list of ordinals specifying weeks of the year. + * Valid values are 1 to 53 or -53 to -1. + * + * @param int $value + * + * @return $this + */ + public function setByWeekNo($value) + { + $this->byWeekNo = $value; + + return $this; + } + + /** + * The BYYEARDAY rule part specifies a COMMA-separated list of days of the year. + * Valid values are 1 to 366 or -366 to -1. + * + * @param int $day + * + * @return $this + */ + public function setByYearDay($day) + { + $this->byYearDay = $day; + + return $this; + } + + /** + * The BYMONTHDAY rule part specifies a COMMA-separated list of days of the month. + * Valid values are 1 to 31 or -31 to -1. + * + * @param int $day + * + * @return $this + */ + public function setByMonthDay($day) + { + $this->byMonthDay = $day; + + return $this; + } + + /** + * The BYDAY rule part specifies a COMMA-separated list of days of the week;. + * + * SU indicates Sunday; MO indicates Monday; TU indicates Tuesday; + * WE indicates Wednesday; TH indicates Thursday; FR indicates Friday; and SA indicates Saturday. + * + * Each BYDAY value can also be preceded by a positive (+n) or negative (-n) integer. + * If present, this indicates the nth occurrence of a specific day within the MONTHLY or YEARLY "RRULE". + * + * @param string $day + * + * @return $this + */ + public function setByDay($day) + { + $this->byDay = $day; + + return $this; + } + + /** + * The BYHOUR rule part specifies a COMMA-separated list of hours of the day. + * Valid values are 0 to 23. + * + * @param int $value + * + * @return $this + * + * @throws \InvalidArgumentException + */ + public function setByHour($value) + { + if (!is_integer($value) || $value < 0 || $value > 23) { + throw new \InvalidArgumentException('Invalid value for BYHOUR'); + } + + $this->byHour = $value; + + return $this; + } + + /** + * The BYMINUTE rule part specifies a COMMA-separated list of minutes within an hour. + * Valid values are 0 to 59. + * + * @param int $value + * + * @return $this + * + * @throws \InvalidArgumentException + */ + public function setByMinute($value) + { + if (!is_integer($value) || $value < 0 || $value > 59) { + throw new \InvalidArgumentException('Invalid value for BYMINUTE'); + } + + $this->byMinute = $value; + + return $this; + } + + /** + * The BYSECOND rule part specifies a COMMA-separated list of seconds within a minute. + * Valid values are 0 to 60. + * + * @param int $value + * + * @return $this + * + * @throws \InvalidArgumentException + */ + public function setBySecond($value) + { + if (!is_integer($value) || $value < 0 || $value > 60) { + throw new \InvalidArgumentException('Invalid value for BYSECOND'); + } + + $this->bySecond = $value; + + return $this; + } +} diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/Property/StringValue.php b/vendor/eluceo/ical/src/Eluceo/iCal/Property/StringValue.php new file mode 100644 index 0000000000..4995723b5a --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/Property/StringValue.php @@ -0,0 +1,61 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal\Property; + +use Eluceo\iCal\Util\PropertyValueUtil; + +class StringValue implements ValueInterface +{ + /** + * The value. + * + * @var string + */ + protected $value; + + public function __construct($value) + { + $this->value = $value; + } + + /** + * Return the value of the Property as an escaped string. + * + * Escape values as per RFC 2445. See http://www.kanzaki.com/docs/ical/text.html + * + * @return string + */ + public function getEscapedValue() + { + return PropertyValueUtil::escapeValue((string) $this->value); + } + + /** + * @param string $value + * + * @return $this + */ + public function setValue($value) + { + $this->value = $value; + + return $this; + } + + /** + * @return string + */ + public function getValue() + { + return $this->value; + } +} diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/Property/ValueInterface.php b/vendor/eluceo/ical/src/Eluceo/iCal/Property/ValueInterface.php new file mode 100644 index 0000000000..cc3d885c68 --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/Property/ValueInterface.php @@ -0,0 +1,24 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal\Property; + +interface ValueInterface +{ + /** + * Return the value of the Property as an escaped string. + * + * Escape values as per RFC 2445. See http://www.kanzaki.com/docs/ical/text.html + * + * @return string + */ + public function getEscapedValue(); +} diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/PropertyBag.php b/vendor/eluceo/ical/src/Eluceo/iCal/PropertyBag.php new file mode 100644 index 0000000000..7032360be4 --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/PropertyBag.php @@ -0,0 +1,79 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal; + +class PropertyBag implements \IteratorAggregate +{ + /** + * @var array + */ + protected $elements = array(); + + /** + * Creates a new Property with $name, $value and $params. + * + * @param $name + * @param $value + * @param array $params + * + * @return $this + */ + public function set($name, $value, $params = array()) + { + $property = new Property($name, $value, $params); + $this->elements[] = $property; + + return $this; + } + + /** + * @param string $name + * + * @return null|Property + */ + public function get($name) + { + // Searching Property in elements-array + /** @var $property Property */ + foreach ($this->elements as $property) { + if ($property->getName() == $name) { + return $property; + } + } + } + + /** + * Adds a Property. If Property already exists an Exception will be thrown. + * + * @param Property $property + * + * @return $this + * + * @throws \Exception + */ + public function add(Property $property) + { + // Property already exists? + if (null !== $this->get($property->getName())) { + throw new \Exception("Property with name '{$property->getName()}' already exists"); + } + + $this->elements[] = $property; + + return $this; + } + + public function getIterator() + { + return new \ArrayObject($this->elements); + } +} diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/Util/ComponentUtil.php b/vendor/eluceo/ical/src/Eluceo/iCal/Util/ComponentUtil.php new file mode 100644 index 0000000000..ca6a2be0e5 --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/Util/ComponentUtil.php @@ -0,0 +1,48 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal\Util; + +class ComponentUtil +{ + /** + * Folds a single line. + * + * According to RFC 2445, all lines longer than 75 characters will be folded + * + * @link http://www.ietf.org/rfc/rfc2445.txt + * + * @param $string + * + * @return array + */ + public static function fold($string) + { + $lines = array(); + $array = preg_split('/(? 75) { + $line = ' ' . $char; + ++$lineNo; + } else { + $line .= $char; + } + $lines[$lineNo] = $line; + } + + return $lines; + } +} diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/Util/DateUtil.php b/vendor/eluceo/ical/src/Eluceo/iCal/Util/DateUtil.php new file mode 100644 index 0000000000..3bd3367fdc --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/Util/DateUtil.php @@ -0,0 +1,69 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal\Util; + +class DateUtil +{ + public static function getDefaultParams(\DateTime $dateTime = null, $noTime = false, $useTimezone = false) + { + $params = array(); + + if ($useTimezone) { + $timeZone = $dateTime->getTimezone()->getName(); + $params['TZID'] = $timeZone; + } + + if ($noTime) { + $params['VALUE'] = 'DATE'; + } + + return $params; + } + + /** + * Returns a formatted date string. + * + * @param \DateTime|null $dateTime The DateTime object + * @param bool $noTime Indicates if the time will be added + * @param bool $useTimezone + * @param bool $useUtc + * + * @return mixed + */ + public static function getDateString(\DateTime $dateTime = null, $noTime = false, $useTimezone = false, $useUtc = false) + { + if (empty($dateTime)) { + $dateTime = new \DateTime(); + } + + return $dateTime->format(self::getDateFormat($noTime, $useTimezone, $useUtc)); + } + + /** + * Returns the date format that can be passed to DateTime::format(). + * + * @param bool $noTime Indicates if the time will be added + * @param bool $useTimezone + * @param bool $useUtc + * + * @return string + */ + public static function getDateFormat($noTime = false, $useTimezone = false, $useUtc = false) + { + // Do not use UTC time (Z) if timezone support is enabled. + if ($useTimezone || !$useUtc) { + return $noTime ? 'Ymd' : 'Ymd\THis'; + } + + return $noTime ? 'Ymd' : 'Ymd\THis\Z'; + } +} diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/Util/PropertyValueUtil.php b/vendor/eluceo/ical/src/Eluceo/iCal/Util/PropertyValueUtil.php new file mode 100644 index 0000000000..4053858916 --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/Util/PropertyValueUtil.php @@ -0,0 +1,40 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal\Util; + +class PropertyValueUtil +{ + public static function escapeValue($value) + { + $value = self::escapeValueAllowNewLine($value); + $value = str_replace("\n", '\\n', $value); + + return $value; + } + + public static function escapeValueAllowNewLine($value) + { + $value = str_replace('\\', '\\\\', $value); + $value = str_replace('"', '\\"', $value); + $value = str_replace(',', '\\,', $value); + $value = str_replace(';', '\\;', $value); + $value = str_replace(array( + "\x00", "\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07", + "\x08", "\x09", /* \n*/ "\x0B", "\x0C", "\x0D", "\x0E", "\x0F", + "\x10", "\x11", "\x12", "\x13", "\x14", "\x15", "\x16", "\x17", + "\x18", "\x19", "\x1A", "\x1B", "\x1C", "\x1D", "\x1E", "\x1F", + "\x7F", + ), '', $value); + + return $value; + } +} diff --git a/vendor/eluceo/ical/tests/Eluceo/iCal/Component/CalendarIntegrationTest.php b/vendor/eluceo/ical/tests/Eluceo/iCal/Component/CalendarIntegrationTest.php new file mode 100644 index 0000000000..eb869af8cf --- /dev/null +++ b/vendor/eluceo/ical/tests/Eluceo/iCal/Component/CalendarIntegrationTest.php @@ -0,0 +1,64 @@ +setDtStart(new \DateTime('2012-12-31', $timeZone)); + $vEvent->setDtEnd(new \DateTime('2012-12-31', $timeZone)); + $vEvent->setNoTime(true); + $vEvent->setIsPrivate(true); + $vEvent->setSummary('New Year’s Eve'); + + // Set recurrence rule + $recurrenceRule = new \Eluceo\iCal\Property\Event\RecurrenceRule(); + $recurrenceRule->setFreq(\Eluceo\iCal\Property\Event\RecurrenceRule::FREQ_YEARLY); + $recurrenceRule->setInterval(1); + $vEvent->setRecurrenceRule($recurrenceRule); + + // Adding Timezone (optional) + $vEvent->setUseTimezone(true); + + // 3. Add event to calendar + $vCalendar->addComponent($vEvent); + + $lines = array( + '/BEGIN:VCALENDAR/', + '/VERSION:2\.0/', + '/PRODID:www\.example\.com/', + '/X-PUBLISHED-TTL:P1W/', + '/BEGIN:VEVENT/', + '/UID:123456/', + '/DTSTART;TZID=Europe\/Berlin;VALUE=DATE:20121231/', + '/SEQUENCE:0/', + '/TRANSP:OPAQUE/', + '/DTEND;TZID=Europe\/Berlin;VALUE=DATE:20121231/', + '/SUMMARY:New Year’s Eve/', + '/CLASS:PRIVATE/', + '/RRULE:FREQ=YEARLY;INTERVAL=1/', + '/X-MICROSOFT-CDO-ALLDAYEVENT:TRUE/', + '/DTSTAMP:20\d{6}T\d{6}Z/', + '/END:VEVENT/', + '/END:VCALENDAR/', + ); + + foreach (explode("\n", $vCalendar->render()) as $key => $line) + { + $this->assertTrue(isset($lines[$key]), 'Too many lines... ' . $line); + + $this->assertRegExp($lines[$key], $line); + } + } +} diff --git a/vendor/eluceo/ical/tests/Eluceo/iCal/ComponentTest.php b/vendor/eluceo/ical/tests/Eluceo/iCal/ComponentTest.php new file mode 100644 index 0000000000..5cac7c5dfa --- /dev/null +++ b/vendor/eluceo/ical/tests/Eluceo/iCal/ComponentTest.php @@ -0,0 +1,45 @@ +setDtStart(new \DateTime('2014-12-24')); + $vEvent->setDtEnd(new \DateTime('2014-12-24')); + $vEvent->setDescription($input); + + $vAlarm = new \Eluceo\iCal\Component\Alarm; + $vAlarm->setAction(\Eluceo\iCal\Component\Alarm::ACTION_DISPLAY); + $vAlarm->setDescription($input); + $vAlarm->setTrigger('PT0S', true); + $vEvent->addComponent($vAlarm); + + $vCalendar->addComponent($vEvent); + + $output = $vCalendar->render(); + $output = preg_replace('/\r\n /u', '', $output); + $this->assertContains($input, $output); + } + + public function testDescriptionWithNewLines() + { + $input = "new string \n new line \n new line \n new string"; + + $vCalendar = new \Eluceo\iCal\Component\Calendar('www.example.com'); + $vEvent = new \Eluceo\iCal\Component\Event(); + $vEvent->setDtStart(new \DateTime('2014-12-24')); + $vEvent->setDtEnd(new \DateTime('2014-12-24')); + $vEvent->setDescription($input); + + $vCalendar->addComponent($vEvent); + + $output = $vCalendar->render(); + $this->assertContains(str_replace("\n", "\\n", $input), $output); + } +} diff --git a/vendor/eluceo/ical/tests/Eluceo/iCal/ParameterBagTest.php b/vendor/eluceo/ical/tests/Eluceo/iCal/ParameterBagTest.php new file mode 100644 index 0000000000..0fb6e84cfd --- /dev/null +++ b/vendor/eluceo/ical/tests/Eluceo/iCal/ParameterBagTest.php @@ -0,0 +1,35 @@ +assertEquals( + 'test string', + $propertyObject->escapeParamValue('test string'), + 'No escaping necessary' + ); + + $this->assertEquals( + '"Containing \\"double-quotes\\""', + $propertyObject->escapeParamValue('Containing "double-quotes"'), + 'Text contains double quotes' + ); + + $this->assertEquals( + '"Containing forbidden chars like a ;"', + $propertyObject->escapeParamValue('Containing forbidden chars like a ;'), + 'Text with semicolon' + ); + + $this->assertEquals( + '"Containing forbidden chars like a :"', + $propertyObject->escapeParamValue('Containing forbidden chars like a :'), + 'Text with colon' + ); + } +} diff --git a/vendor/eluceo/ical/tests/Eluceo/iCal/Property/ArrayValueTest.php b/vendor/eluceo/ical/tests/Eluceo/iCal/Property/ArrayValueTest.php new file mode 100644 index 0000000000..1d1b3331b9 --- /dev/null +++ b/vendor/eluceo/ical/tests/Eluceo/iCal/Property/ArrayValueTest.php @@ -0,0 +1,26 @@ +assertEquals($expectedOutput, $arrayValue->getEscapedValue()); + } + + public function arrayValuesProvider() + { + return array( + array(array(), ''), + array(array('Lorem'), 'Lorem'), + array(array('Lorem', 'Ipsum'), 'Lorem,Ipsum'), + array(array('Lorem', '"doublequotes"'), 'Lorem,\"doublequotes\"'), + ); + } +} diff --git a/vendor/eluceo/ical/tests/Eluceo/iCal/Property/Event/DescriptionTest.php b/vendor/eluceo/ical/tests/Eluceo/iCal/Property/Event/DescriptionTest.php new file mode 100644 index 0000000000..0ad16bccf2 --- /dev/null +++ b/vendor/eluceo/ical/tests/Eluceo/iCal/Property/Event/DescriptionTest.php @@ -0,0 +1,17 @@ +assertEquals( + str_replace("\n", "\\n", $testString), + $description->getEscapedValue() + ); + } +} diff --git a/vendor/eluceo/ical/tests/Eluceo/iCal/Property/Event/OrganizerTest.php b/vendor/eluceo/ical/tests/Eluceo/iCal/Property/Event/OrganizerTest.php new file mode 100644 index 0000000000..71acdce97b --- /dev/null +++ b/vendor/eluceo/ical/tests/Eluceo/iCal/Property/Event/OrganizerTest.php @@ -0,0 +1,63 @@ + + */ + +namespace Eluceo\iCal\Property\Event; + +/** + * OrganizerTest + */ +class OrganizerTest extends \PHPUnit_Framework_TestCase +{ + public function testOrganizerValueOnly() + { + $value = "MAILTO:name.lastname@example.com"; + $expected = "ORGANIZER:$value"; + + $vCalendar = $this->createCalendarWithOrganizer( + new \Eluceo\iCal\Property\Event\Organizer($value) + ); + + foreach (explode("\n", $vCalendar->render()) as $line) + { + if (preg_match('/^ORGANIZER[:;](.*)$/', $line)) { + $this->assertEquals($expected, trim($line)); + } + } + } + + public function testOrganizerValueAndParameter() + { + $value = "MAILTO:name.lastname@example.com"; + $param = "Name LastName"; + $expected = "ORGANIZER;CN=$param:$value"; + + $vCalendar = $this->createCalendarWithOrganizer( + new \Eluceo\iCal\Property\Event\Organizer($value, array('CN' => $param)) + ); + + foreach (explode("\n", $vCalendar->render()) as $line) + { + if (preg_match('/^ORGANIZER[:;](.*)$/', $line)) { + $this->assertEquals($expected, trim($line)); + } + } + + } + + /** + * @param Organizer $vOrganizer + * @return \Eluceo\iCal\Component\Calendar + */ + private function createCalendarWithOrganizer(\Eluceo\iCal\Property\Event\Organizer $vOrganizer) + { + $vCalendar = new \Eluceo\iCal\Component\Calendar('www.example.com'); + $vEvent = new \Eluceo\iCal\Component\Event('123456'); + $vEvent->setOrganizer($vOrganizer); + $vCalendar->addComponent($vEvent); + return $vCalendar; + } +} diff --git a/vendor/eluceo/ical/tests/Eluceo/iCal/Property/Event/RecurrenceRuleTest.php b/vendor/eluceo/ical/tests/Eluceo/iCal/Property/Event/RecurrenceRuleTest.php new file mode 100644 index 0000000000..a44b25800f --- /dev/null +++ b/vendor/eluceo/ical/tests/Eluceo/iCal/Property/Event/RecurrenceRuleTest.php @@ -0,0 +1,21 @@ +setFreq(RecurrenceRule::FREQ_DAILY); + $rule->setInterval(null); + $rule->setUntil(new \DateTime('1997-12-24')); + $this->assertEquals( + 'FREQ=DAILY;UNTIL=19971224T000000Z', + $rule->getEscapedValue() + ); + } +} diff --git a/vendor/eluceo/ical/tests/Eluceo/iCal/Property/StringValueTest.php b/vendor/eluceo/ical/tests/Eluceo/iCal/Property/StringValueTest.php new file mode 100644 index 0000000000..afa70df10d --- /dev/null +++ b/vendor/eluceo/ical/tests/Eluceo/iCal/Property/StringValueTest.php @@ -0,0 +1,63 @@ +assertEquals( + 'LOREM', + $stringValue->getEscapedValue(), + 'No escaping necessary' + ); + } + + public function testValueContainsBackslash() + { + $stringValue = new StringValue('text contains backslash: \\'); + + $this->assertEquals( + 'text contains backslash: \\\\', + $stringValue->getEscapedValue(), + 'Text contains backslash' + ); + } + + public function testEscapingDoubleQuotes() + { + $stringValue = new StringValue('text with "doublequotes" will be escaped'); + + $this->assertEquals( + 'text with \\"doublequotes\\" will be escaped', + $stringValue->getEscapedValue(), + 'Escaping double quotes' + ); + } + + public function testEscapingSemicolonAndComma() + { + $stringValue = new StringValue('text with , and ; will also be escaped'); + + $this->assertEquals( + 'text with \\, and \\; will also be escaped', + $stringValue->getEscapedValue(), + 'Escaping ; and ,' + ); + } + + public function testNewLineEscaping() + { + $stringValue = new StringValue("Text with new\n line"); + + $this->assertEquals( + 'Text with new\\n line', + $stringValue->getEscapedValue(), + 'Escape new line to text' + ); + } +} diff --git a/vendor/eluceo/ical/tests/Eluceo/iCal/PropertyBagTest.php b/vendor/eluceo/ical/tests/Eluceo/iCal/PropertyBagTest.php new file mode 100644 index 0000000000..a7f8d8cea0 --- /dev/null +++ b/vendor/eluceo/ical/tests/Eluceo/iCal/PropertyBagTest.php @@ -0,0 +1,18 @@ +setExpectedException('\\Exception', "Property with name 'propName' already exists"); + + $propertyBag = new PropertyBag(); + $propertyBag->add(new Property('propName', '')); + $propertyBag->add(new Property('propName', '')); + } +} diff --git a/vendor/eluceo/ical/tests/Eluceo/iCal/PropertyTest.php b/vendor/eluceo/ical/tests/Eluceo/iCal/PropertyTest.php new file mode 100644 index 0000000000..b30f5a3a20 --- /dev/null +++ b/vendor/eluceo/ical/tests/Eluceo/iCal/PropertyTest.php @@ -0,0 +1,42 @@ +assertEquals( + 'DTSTAMP:20131020T153112', + $property->toLine() + ); + } + + public function testPropertyWithValueAndParams() + { + $property = new Property('DTSTAMP', '20131020T153112', array('TZID' => 'Europe/Berlin')); + $this->assertEquals( + 'DTSTAMP;TZID=Europe/Berlin:20131020T153112', + $property->toLine() + ); + } + + public function testPropertyWithEscapedSingleValue() + { + $property = new Property('SOMEPROP', 'Escape me!"'); + $this->assertEquals( + 'SOMEPROP:Escape me!\\"', + $property->toLine() + ); + } + + public function testPropertyWithEscapedValues() + { + $property = new Property('SOMEPROP', 'Escape me!"', array('TEST' => 'Lorem "test" ipsum')); + $this->assertEquals( + 'SOMEPROP;TEST="Lorem \\"test\\" ipsum":Escape me!\\"', + $property->toLine() + ); + } +} diff --git a/vendor/erusev/parsedown/.travis.yml b/vendor/erusev/parsedown/.travis.yml new file mode 100644 index 0000000000..5df49dcb3c --- /dev/null +++ b/vendor/erusev/parsedown/.travis.yml @@ -0,0 +1,16 @@ +language: php + +php: + - 7.0 + - 5.6 + - 5.5 + - 5.4 + - 5.3 + - hhvm + - hhvm-nightly + +matrix: + fast_finish: true + allow_failures: + - php: 7.0 + - php: hhvm-nightly diff --git a/vendor/erusev/parsedown/LICENSE.txt b/vendor/erusev/parsedown/LICENSE.txt new file mode 100644 index 0000000000..baca86f5b8 --- /dev/null +++ b/vendor/erusev/parsedown/LICENSE.txt @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013 Emanuil Rusev, erusev.com + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/vendor/erusev/parsedown/Parsedown.php b/vendor/erusev/parsedown/Parsedown.php new file mode 100755 index 0000000000..c8c92a3924 --- /dev/null +++ b/vendor/erusev/parsedown/Parsedown.php @@ -0,0 +1,1528 @@ +DefinitionData = array(); + + # standardize line breaks + $text = str_replace(array("\r\n", "\r"), "\n", $text); + + # remove surrounding line breaks + $text = trim($text, "\n"); + + # split text into lines + $lines = explode("\n", $text); + + # iterate through lines to identify blocks + $markup = $this->lines($lines); + + # trim line breaks + $markup = trim($markup, "\n"); + + return $markup; + } + + # + # Setters + # + + function setBreaksEnabled($breaksEnabled) + { + $this->breaksEnabled = $breaksEnabled; + + return $this; + } + + protected $breaksEnabled; + + function setMarkupEscaped($markupEscaped) + { + $this->markupEscaped = $markupEscaped; + + return $this; + } + + protected $markupEscaped; + + function setUrlsLinked($urlsLinked) + { + $this->urlsLinked = $urlsLinked; + + return $this; + } + + protected $urlsLinked = true; + + # + # Lines + # + + protected $BlockTypes = array( + '#' => array('Header'), + '*' => array('Rule', 'List'), + '+' => array('List'), + '-' => array('SetextHeader', 'Table', 'Rule', 'List'), + '0' => array('List'), + '1' => array('List'), + '2' => array('List'), + '3' => array('List'), + '4' => array('List'), + '5' => array('List'), + '6' => array('List'), + '7' => array('List'), + '8' => array('List'), + '9' => array('List'), + ':' => array('Table'), + '<' => array('Comment', 'Markup'), + '=' => array('SetextHeader'), + '>' => array('Quote'), + '[' => array('Reference'), + '_' => array('Rule'), + '`' => array('FencedCode'), + '|' => array('Table'), + '~' => array('FencedCode'), + ); + + # ~ + + protected $unmarkedBlockTypes = array( + 'Code', + ); + + # + # Blocks + # + + private function lines(array $lines) + { + $CurrentBlock = null; + + foreach ($lines as $line) + { + if (chop($line) === '') + { + if (isset($CurrentBlock)) + { + $CurrentBlock['interrupted'] = true; + } + + continue; + } + + if (strpos($line, "\t") !== false) + { + $parts = explode("\t", $line); + + $line = $parts[0]; + + unset($parts[0]); + + foreach ($parts as $part) + { + $shortage = 4 - mb_strlen($line, 'utf-8') % 4; + + $line .= str_repeat(' ', $shortage); + $line .= $part; + } + } + + $indent = 0; + + while (isset($line[$indent]) and $line[$indent] === ' ') + { + $indent ++; + } + + $text = $indent > 0 ? substr($line, $indent) : $line; + + # ~ + + $Line = array('body' => $line, 'indent' => $indent, 'text' => $text); + + # ~ + + if (isset($CurrentBlock['continuable'])) + { + $Block = $this->{'block'.$CurrentBlock['type'].'Continue'}($Line, $CurrentBlock); + + if (isset($Block)) + { + $CurrentBlock = $Block; + + continue; + } + else + { + if (method_exists($this, 'block'.$CurrentBlock['type'].'Complete')) + { + $CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock); + } + } + } + + # ~ + + $marker = $text[0]; + + # ~ + + $blockTypes = $this->unmarkedBlockTypes; + + if (isset($this->BlockTypes[$marker])) + { + foreach ($this->BlockTypes[$marker] as $blockType) + { + $blockTypes []= $blockType; + } + } + + # + # ~ + + foreach ($blockTypes as $blockType) + { + $Block = $this->{'block'.$blockType}($Line, $CurrentBlock); + + if (isset($Block)) + { + $Block['type'] = $blockType; + + if ( ! isset($Block['identified'])) + { + $Blocks []= $CurrentBlock; + + $Block['identified'] = true; + } + + if (method_exists($this, 'block'.$blockType.'Continue')) + { + $Block['continuable'] = true; + } + + $CurrentBlock = $Block; + + continue 2; + } + } + + # ~ + + if (isset($CurrentBlock) and ! isset($CurrentBlock['type']) and ! isset($CurrentBlock['interrupted'])) + { + $CurrentBlock['element']['text'] .= "\n".$text; + } + else + { + $Blocks []= $CurrentBlock; + + $CurrentBlock = $this->paragraph($Line); + + $CurrentBlock['identified'] = true; + } + } + + # ~ + + if (isset($CurrentBlock['continuable']) and method_exists($this, 'block'.$CurrentBlock['type'].'Complete')) + { + $CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock); + } + + # ~ + + $Blocks []= $CurrentBlock; + + unset($Blocks[0]); + + # ~ + + $markup = ''; + + foreach ($Blocks as $Block) + { + if (isset($Block['hidden'])) + { + continue; + } + + $markup .= "\n"; + $markup .= isset($Block['markup']) ? $Block['markup'] : $this->element($Block['element']); + } + + $markup .= "\n"; + + # ~ + + return $markup; + } + + # + # Code + + protected function blockCode($Line, $Block = null) + { + if (isset($Block) and ! isset($Block['type']) and ! isset($Block['interrupted'])) + { + return; + } + + if ($Line['indent'] >= 4) + { + $text = substr($Line['body'], 4); + + $Block = array( + 'element' => array( + 'name' => 'pre', + 'handler' => 'element', + 'text' => array( + 'name' => 'code', + 'text' => $text, + ), + ), + ); + + return $Block; + } + } + + protected function blockCodeContinue($Line, $Block) + { + if ($Line['indent'] >= 4) + { + if (isset($Block['interrupted'])) + { + $Block['element']['text']['text'] .= "\n"; + + unset($Block['interrupted']); + } + + $Block['element']['text']['text'] .= "\n"; + + $text = substr($Line['body'], 4); + + $Block['element']['text']['text'] .= $text; + + return $Block; + } + } + + protected function blockCodeComplete($Block) + { + $text = $Block['element']['text']['text']; + + $text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8'); + + $Block['element']['text']['text'] = $text; + + return $Block; + } + + # + # Comment + + protected function blockComment($Line) + { + if ($this->markupEscaped) + { + return; + } + + if (isset($Line['text'][3]) and $Line['text'][3] === '-' and $Line['text'][2] === '-' and $Line['text'][1] === '!') + { + $Block = array( + 'markup' => $Line['body'], + ); + + if (preg_match('/-->$/', $Line['text'])) + { + $Block['closed'] = true; + } + + return $Block; + } + } + + protected function blockCommentContinue($Line, array $Block) + { + if (isset($Block['closed'])) + { + return; + } + + $Block['markup'] .= "\n" . $Line['body']; + + if (preg_match('/-->$/', $Line['text'])) + { + $Block['closed'] = true; + } + + return $Block; + } + + # + # Fenced Code + + protected function blockFencedCode($Line) + { + if (preg_match('/^['.$Line['text'][0].']{3,}[ ]*([\w-]+)?[ ]*$/', $Line['text'], $matches)) + { + $Element = array( + 'name' => 'code', + 'text' => '', + ); + + if (isset($matches[1])) + { + $class = 'language-'.$matches[1]; + + $Element['attributes'] = array( + 'class' => $class, + ); + } + + $Block = array( + 'char' => $Line['text'][0], + 'element' => array( + 'name' => 'pre', + 'handler' => 'element', + 'text' => $Element, + ), + ); + + return $Block; + } + } + + protected function blockFencedCodeContinue($Line, $Block) + { + if (isset($Block['complete'])) + { + return; + } + + if (isset($Block['interrupted'])) + { + $Block['element']['text']['text'] .= "\n"; + + unset($Block['interrupted']); + } + + if (preg_match('/^'.$Block['char'].'{3,}[ ]*$/', $Line['text'])) + { + $Block['element']['text']['text'] = substr($Block['element']['text']['text'], 1); + + $Block['complete'] = true; + + return $Block; + } + + $Block['element']['text']['text'] .= "\n".$Line['body'];; + + return $Block; + } + + protected function blockFencedCodeComplete($Block) + { + $text = $Block['element']['text']['text']; + + $text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8'); + + $Block['element']['text']['text'] = $text; + + return $Block; + } + + # + # Header + + protected function blockHeader($Line) + { + if (isset($Line['text'][1])) + { + $level = 1; + + while (isset($Line['text'][$level]) and $Line['text'][$level] === '#') + { + $level ++; + } + + if ($level > 6) + { + return; + } + + $text = trim($Line['text'], '# '); + + $Block = array( + 'element' => array( + 'name' => 'h' . min(6, $level), + 'text' => $text, + 'handler' => 'line', + ), + ); + + return $Block; + } + } + + # + # List + + protected function blockList($Line) + { + list($name, $pattern) = $Line['text'][0] <= '-' ? array('ul', '[*+-]') : array('ol', '[0-9]+[.]'); + + if (preg_match('/^('.$pattern.'[ ]+)(.*)/', $Line['text'], $matches)) + { + $Block = array( + 'indent' => $Line['indent'], + 'pattern' => $pattern, + 'element' => array( + 'name' => $name, + 'handler' => 'elements', + ), + ); + + $Block['li'] = array( + 'name' => 'li', + 'handler' => 'li', + 'text' => array( + $matches[2], + ), + ); + + $Block['element']['text'] []= & $Block['li']; + + return $Block; + } + } + + protected function blockListContinue($Line, array $Block) + { + if ($Block['indent'] === $Line['indent'] and preg_match('/^'.$Block['pattern'].'(?:[ ]+(.*)|$)/', $Line['text'], $matches)) + { + if (isset($Block['interrupted'])) + { + $Block['li']['text'] []= ''; + + unset($Block['interrupted']); + } + + unset($Block['li']); + + $text = isset($matches[1]) ? $matches[1] : ''; + + $Block['li'] = array( + 'name' => 'li', + 'handler' => 'li', + 'text' => array( + $text, + ), + ); + + $Block['element']['text'] []= & $Block['li']; + + return $Block; + } + + if ($Line['text'][0] === '[' and $this->blockReference($Line)) + { + return $Block; + } + + if ( ! isset($Block['interrupted'])) + { + $text = preg_replace('/^[ ]{0,4}/', '', $Line['body']); + + $Block['li']['text'] []= $text; + + return $Block; + } + + if ($Line['indent'] > 0) + { + $Block['li']['text'] []= ''; + + $text = preg_replace('/^[ ]{0,4}/', '', $Line['body']); + + $Block['li']['text'] []= $text; + + unset($Block['interrupted']); + + return $Block; + } + } + + # + # Quote + + protected function blockQuote($Line) + { + if (preg_match('/^>[ ]?(.*)/', $Line['text'], $matches)) + { + $Block = array( + 'element' => array( + 'name' => 'blockquote', + 'handler' => 'lines', + 'text' => (array) $matches[1], + ), + ); + + return $Block; + } + } + + protected function blockQuoteContinue($Line, array $Block) + { + if ($Line['text'][0] === '>' and preg_match('/^>[ ]?(.*)/', $Line['text'], $matches)) + { + if (isset($Block['interrupted'])) + { + $Block['element']['text'] []= ''; + + unset($Block['interrupted']); + } + + $Block['element']['text'] []= $matches[1]; + + return $Block; + } + + if ( ! isset($Block['interrupted'])) + { + $Block['element']['text'] []= $Line['text']; + + return $Block; + } + } + + # + # Rule + + protected function blockRule($Line) + { + if (preg_match('/^(['.$Line['text'][0].'])([ ]*\1){2,}[ ]*$/', $Line['text'])) + { + $Block = array( + 'element' => array( + 'name' => 'hr' + ), + ); + + return $Block; + } + } + + # + # Setext + + protected function blockSetextHeader($Line, array $Block = null) + { + if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted'])) + { + return; + } + + if (chop($Line['text'], $Line['text'][0]) === '') + { + $Block['element']['name'] = $Line['text'][0] === '=' ? 'h1' : 'h2'; + + return $Block; + } + } + + # + # Markup + + protected function blockMarkup($Line) + { + if ($this->markupEscaped) + { + return; + } + + if (preg_match('/^<(\w*)(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*(\/)?>/', $Line['text'], $matches)) + { + $element = strtolower($matches[1]); + + if (in_array($element, $this->textLevelElements)) + { + return; + } + + $Block = array( + 'name' => $matches[1], + 'depth' => 0, + 'markup' => $Line['text'], + ); + + $length = strlen($matches[0]); + + $remainder = substr($Line['text'], $length); + + if (trim($remainder) === '') + { + if (isset($matches[2]) or in_array($matches[1], $this->voidElements)) + { + $Block['closed'] = true; + + $Block['void'] = true; + } + } + else + { + if (isset($matches[2]) or in_array($matches[1], $this->voidElements)) + { + return; + } + + if (preg_match('/<\/'.$matches[1].'>[ ]*$/i', $remainder)) + { + $Block['closed'] = true; + } + } + + return $Block; + } + } + + protected function blockMarkupContinue($Line, array $Block) + { + if (isset($Block['closed'])) + { + return; + } + + if (preg_match('/^<'.$Block['name'].'(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*>/i', $Line['text'])) # open + { + $Block['depth'] ++; + } + + if (preg_match('/(.*?)<\/'.$Block['name'].'>[ ]*$/i', $Line['text'], $matches)) # close + { + if ($Block['depth'] > 0) + { + $Block['depth'] --; + } + else + { + $Block['closed'] = true; + } + } + + if (isset($Block['interrupted'])) + { + $Block['markup'] .= "\n"; + + unset($Block['interrupted']); + } + + $Block['markup'] .= "\n".$Line['body']; + + return $Block; + } + + # + # Reference + + protected function blockReference($Line) + { + if (preg_match('/^\[(.+?)\]:[ ]*?(?:[ ]+["\'(](.+)["\')])?[ ]*$/', $Line['text'], $matches)) + { + $id = strtolower($matches[1]); + + $Data = array( + 'url' => $matches[2], + 'title' => null, + ); + + if (isset($matches[3])) + { + $Data['title'] = $matches[3]; + } + + $this->DefinitionData['Reference'][$id] = $Data; + + $Block = array( + 'hidden' => true, + ); + + return $Block; + } + } + + # + # Table + + protected function blockTable($Line, array $Block = null) + { + if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted'])) + { + return; + } + + if (strpos($Block['element']['text'], '|') !== false and chop($Line['text'], ' -:|') === '') + { + $alignments = array(); + + $divider = $Line['text']; + + $divider = trim($divider); + $divider = trim($divider, '|'); + + $dividerCells = explode('|', $divider); + + foreach ($dividerCells as $dividerCell) + { + $dividerCell = trim($dividerCell); + + if ($dividerCell === '') + { + continue; + } + + $alignment = null; + + if ($dividerCell[0] === ':') + { + $alignment = 'left'; + } + + if (substr($dividerCell, - 1) === ':') + { + $alignment = $alignment === 'left' ? 'center' : 'right'; + } + + $alignments []= $alignment; + } + + # ~ + + $HeaderElements = array(); + + $header = $Block['element']['text']; + + $header = trim($header); + $header = trim($header, '|'); + + $headerCells = explode('|', $header); + + foreach ($headerCells as $index => $headerCell) + { + $headerCell = trim($headerCell); + + $HeaderElement = array( + 'name' => 'th', + 'text' => $headerCell, + 'handler' => 'line', + ); + + if (isset($alignments[$index])) + { + $alignment = $alignments[$index]; + + $HeaderElement['attributes'] = array( + 'style' => 'text-align: '.$alignment.';', + ); + } + + $HeaderElements []= $HeaderElement; + } + + # ~ + + $Block = array( + 'alignments' => $alignments, + 'identified' => true, + 'element' => array( + 'name' => 'table', + 'handler' => 'elements', + ), + ); + + $Block['element']['text'] []= array( + 'name' => 'thead', + 'handler' => 'elements', + ); + + $Block['element']['text'] []= array( + 'name' => 'tbody', + 'handler' => 'elements', + 'text' => array(), + ); + + $Block['element']['text'][0]['text'] []= array( + 'name' => 'tr', + 'handler' => 'elements', + 'text' => $HeaderElements, + ); + + return $Block; + } + } + + protected function blockTableContinue($Line, array $Block) + { + if (isset($Block['interrupted'])) + { + return; + } + + if ($Line['text'][0] === '|' or strpos($Line['text'], '|')) + { + $Elements = array(); + + $row = $Line['text']; + + $row = trim($row); + $row = trim($row, '|'); + + preg_match_all('/(?:(\\\\[|])|[^|`]|`[^`]+`|`)+/', $row, $matches); + + foreach ($matches[0] as $index => $cell) + { + $cell = trim($cell); + + $Element = array( + 'name' => 'td', + 'handler' => 'line', + 'text' => $cell, + ); + + if (isset($Block['alignments'][$index])) + { + $Element['attributes'] = array( + 'style' => 'text-align: '.$Block['alignments'][$index].';', + ); + } + + $Elements []= $Element; + } + + $Element = array( + 'name' => 'tr', + 'handler' => 'elements', + 'text' => $Elements, + ); + + $Block['element']['text'][1]['text'] []= $Element; + + return $Block; + } + } + + # + # ~ + # + + protected function paragraph($Line) + { + $Block = array( + 'element' => array( + 'name' => 'p', + 'text' => $Line['text'], + 'handler' => 'line', + ), + ); + + return $Block; + } + + # + # Inline Elements + # + + protected $InlineTypes = array( + '"' => array('SpecialCharacter'), + '!' => array('Image'), + '&' => array('SpecialCharacter'), + '*' => array('Emphasis'), + ':' => array('Url'), + '<' => array('UrlTag', 'EmailTag', 'Markup', 'SpecialCharacter'), + '>' => array('SpecialCharacter'), + '[' => array('Link'), + '_' => array('Emphasis'), + '`' => array('Code'), + '~' => array('Strikethrough'), + '\\' => array('EscapeSequence'), + ); + + # ~ + + protected $inlineMarkerList = '!"*_&[:<>`~\\'; + + # + # ~ + # + + public function line($text) + { + $markup = ''; + + # $excerpt is based on the first occurrence of a marker + + while ($excerpt = strpbrk($text, $this->inlineMarkerList)) + { + $marker = $excerpt[0]; + + $markerPosition = strpos($text, $marker); + + $Excerpt = array('text' => $excerpt, 'context' => $text); + + foreach ($this->InlineTypes[$marker] as $inlineType) + { + $Inline = $this->{'inline'.$inlineType}($Excerpt); + + if ( ! isset($Inline)) + { + continue; + } + + # makes sure that the inline belongs to "our" marker + + if (isset($Inline['position']) and $Inline['position'] > $markerPosition) + { + continue; + } + + # sets a default inline position + + if ( ! isset($Inline['position'])) + { + $Inline['position'] = $markerPosition; + } + + # the text that comes before the inline + $unmarkedText = substr($text, 0, $Inline['position']); + + # compile the unmarked text + $markup .= $this->unmarkedText($unmarkedText); + + # compile the inline + $markup .= isset($Inline['markup']) ? $Inline['markup'] : $this->element($Inline['element']); + + # remove the examined text + $text = substr($text, $Inline['position'] + $Inline['extent']); + + continue 2; + } + + # the marker does not belong to an inline + + $unmarkedText = substr($text, 0, $markerPosition + 1); + + $markup .= $this->unmarkedText($unmarkedText); + + $text = substr($text, $markerPosition + 1); + } + + $markup .= $this->unmarkedText($text); + + return $markup; + } + + # + # ~ + # + + protected function inlineCode($Excerpt) + { + $marker = $Excerpt['text'][0]; + + if (preg_match('/^('.$marker.'+)[ ]*(.+?)[ ]*(? strlen($matches[0]), + 'element' => array( + 'name' => 'code', + 'text' => $text, + ), + ); + } + } + + protected function inlineEmailTag($Excerpt) + { + if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<((mailto:)?\S+?@\S+?)>/i', $Excerpt['text'], $matches)) + { + $url = $matches[1]; + + if ( ! isset($matches[2])) + { + $url = 'mailto:' . $url; + } + + return array( + 'extent' => strlen($matches[0]), + 'element' => array( + 'name' => 'a', + 'text' => $matches[1], + 'attributes' => array( + 'href' => $url, + ), + ), + ); + } + } + + protected function inlineEmphasis($Excerpt) + { + if ( ! isset($Excerpt['text'][1])) + { + return; + } + + $marker = $Excerpt['text'][0]; + + if ($Excerpt['text'][1] === $marker and preg_match($this->StrongRegex[$marker], $Excerpt['text'], $matches)) + { + $emphasis = 'strong'; + } + elseif (preg_match($this->EmRegex[$marker], $Excerpt['text'], $matches)) + { + $emphasis = 'em'; + } + else + { + return; + } + + return array( + 'extent' => strlen($matches[0]), + 'element' => array( + 'name' => $emphasis, + 'handler' => 'line', + 'text' => $matches[1], + ), + ); + } + + protected function inlineEscapeSequence($Excerpt) + { + if (isset($Excerpt['text'][1]) and in_array($Excerpt['text'][1], $this->specialCharacters)) + { + return array( + 'markup' => $Excerpt['text'][1], + 'extent' => 2, + ); + } + } + + protected function inlineImage($Excerpt) + { + if ( ! isset($Excerpt['text'][1]) or $Excerpt['text'][1] !== '[') + { + return; + } + + $Excerpt['text']= substr($Excerpt['text'], 1); + + $Link = $this->inlineLink($Excerpt); + + if ($Link === null) + { + return; + } + + $Inline = array( + 'extent' => $Link['extent'] + 1, + 'element' => array( + 'name' => 'img', + 'attributes' => array( + 'src' => $Link['element']['attributes']['href'], + 'alt' => $Link['element']['text'], + ), + ), + ); + + $Inline['element']['attributes'] += $Link['element']['attributes']; + + unset($Inline['element']['attributes']['href']); + + return $Inline; + } + + protected function inlineLink($Excerpt) + { + $Element = array( + 'name' => 'a', + 'handler' => 'line', + 'text' => null, + 'attributes' => array( + 'href' => null, + 'title' => null, + ), + ); + + $extent = 0; + + $remainder = $Excerpt['text']; + + if (preg_match('/\[((?:[^][]|(?R))*)\]/', $remainder, $matches)) + { + $Element['text'] = $matches[1]; + + $extent += strlen($matches[0]); + + $remainder = substr($remainder, $extent); + } + else + { + return; + } + + if (preg_match('/^[(]((?:[^ ()]|[(][^ )]+[)])+)(?:[ ]+("[^"]*"|\'[^\']*\'))?[)]/', $remainder, $matches)) + { + $Element['attributes']['href'] = $matches[1]; + + if (isset($matches[2])) + { + $Element['attributes']['title'] = substr($matches[2], 1, - 1); + } + + $extent += strlen($matches[0]); + } + else + { + if (preg_match('/^\s*\[(.*?)\]/', $remainder, $matches)) + { + $definition = strlen($matches[1]) ? $matches[1] : $Element['text']; + $definition = strtolower($definition); + + $extent += strlen($matches[0]); + } + else + { + $definition = strtolower($Element['text']); + } + + if ( ! isset($this->DefinitionData['Reference'][$definition])) + { + return; + } + + $Definition = $this->DefinitionData['Reference'][$definition]; + + $Element['attributes']['href'] = $Definition['url']; + $Element['attributes']['title'] = $Definition['title']; + } + + $Element['attributes']['href'] = str_replace(array('&', '<'), array('&', '<'), $Element['attributes']['href']); + + return array( + 'extent' => $extent, + 'element' => $Element, + ); + } + + protected function inlineMarkup($Excerpt) + { + if ($this->markupEscaped or strpos($Excerpt['text'], '>') === false) + { + return; + } + + if ($Excerpt['text'][1] === '/' and preg_match('/^<\/\w*[ ]*>/s', $Excerpt['text'], $matches)) + { + return array( + 'markup' => $matches[0], + 'extent' => strlen($matches[0]), + ); + } + + if ($Excerpt['text'][1] === '!' and preg_match('/^/s', $Excerpt['text'], $matches)) + { + return array( + 'markup' => $matches[0], + 'extent' => strlen($matches[0]), + ); + } + + if ($Excerpt['text'][1] !== ' ' and preg_match('/^<\w*(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*\/?>/s', $Excerpt['text'], $matches)) + { + return array( + 'markup' => $matches[0], + 'extent' => strlen($matches[0]), + ); + } + } + + protected function inlineSpecialCharacter($Excerpt) + { + if ($Excerpt['text'][0] === '&' and ! preg_match('/^&#?\w+;/', $Excerpt['text'])) + { + return array( + 'markup' => '&', + 'extent' => 1, + ); + } + + $SpecialCharacter = array('>' => 'gt', '<' => 'lt', '"' => 'quot'); + + if (isset($SpecialCharacter[$Excerpt['text'][0]])) + { + return array( + 'markup' => '&'.$SpecialCharacter[$Excerpt['text'][0]].';', + 'extent' => 1, + ); + } + } + + protected function inlineStrikethrough($Excerpt) + { + if ( ! isset($Excerpt['text'][1])) + { + return; + } + + if ($Excerpt['text'][1] === '~' and preg_match('/^~~(?=\S)(.+?)(?<=\S)~~/', $Excerpt['text'], $matches)) + { + return array( + 'extent' => strlen($matches[0]), + 'element' => array( + 'name' => 'del', + 'text' => $matches[1], + 'handler' => 'line', + ), + ); + } + } + + protected function inlineUrl($Excerpt) + { + if ($this->urlsLinked !== true or ! isset($Excerpt['text'][2]) or $Excerpt['text'][2] !== '/') + { + return; + } + + if (preg_match('/\bhttps?:[\/]{2}[^\s<]+\b\/*/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE)) + { + $Inline = array( + 'extent' => strlen($matches[0][0]), + 'position' => $matches[0][1], + 'element' => array( + 'name' => 'a', + 'text' => $matches[0][0], + 'attributes' => array( + 'href' => $matches[0][0], + ), + ), + ); + + return $Inline; + } + } + + protected function inlineUrlTag($Excerpt) + { + if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(\w+:\/{2}[^ >]+)>/i', $Excerpt['text'], $matches)) + { + $url = str_replace(array('&', '<'), array('&', '<'), $matches[1]); + + return array( + 'extent' => strlen($matches[0]), + 'element' => array( + 'name' => 'a', + 'text' => $url, + 'attributes' => array( + 'href' => $url, + ), + ), + ); + } + } + + # ~ + + protected function unmarkedText($text) + { + if ($this->breaksEnabled) + { + $text = preg_replace('/[ ]*\n/', "
\n", $text); + } + else + { + $text = preg_replace('/(?:[ ][ ]+|[ ]*\\\\)\n/', "
\n", $text); + $text = str_replace(" \n", "\n", $text); + } + + return $text; + } + + # + # Handlers + # + + protected function element(array $Element) + { + $markup = '<'.$Element['name']; + + if (isset($Element['attributes'])) + { + foreach ($Element['attributes'] as $name => $value) + { + if ($value === null) + { + continue; + } + + $markup .= ' '.$name.'="'.$value.'"'; + } + } + + if (isset($Element['text'])) + { + $markup .= '>'; + + if (isset($Element['handler'])) + { + $markup .= $this->{$Element['handler']}($Element['text']); + } + else + { + $markup .= $Element['text']; + } + + $markup .= ''; + } + else + { + $markup .= ' />'; + } + + return $markup; + } + + protected function elements(array $Elements) + { + $markup = ''; + + foreach ($Elements as $Element) + { + $markup .= "\n" . $this->element($Element); + } + + $markup .= "\n"; + + return $markup; + } + + # ~ + + protected function li($lines) + { + $markup = $this->lines($lines); + + $trimmedMarkup = trim($markup); + + if ( ! in_array('', $lines) and substr($trimmedMarkup, 0, 3) === '

') + { + $markup = $trimmedMarkup; + $markup = substr($markup, 3); + + $position = strpos($markup, "

"); + + $markup = substr_replace($markup, '', $position, 4); + } + + return $markup; + } + + # + # Deprecated Methods + # + + function parse($text) + { + $markup = $this->text($text); + + return $markup; + } + + # + # Static Methods + # + + static function instance($name = 'default') + { + if (isset(self::$instances[$name])) + { + return self::$instances[$name]; + } + + $instance = new static(); + + self::$instances[$name] = $instance; + + return $instance; + } + + private static $instances = array(); + + # + # Fields + # + + protected $DefinitionData; + + # + # Read-Only + + protected $specialCharacters = array( + '\\', '`', '*', '_', '{', '}', '[', ']', '(', ')', '>', '#', '+', '-', '.', '!', '|', + ); + + protected $StrongRegex = array( + '*' => '/^[*]{2}((?:\\\\\*|[^*]|[*][^*]*[*])+?)[*]{2}(?![*])/s', + '_' => '/^__((?:\\\\_|[^_]|_[^_]*_)+?)__(?!_)/us', + ); + + protected $EmRegex = array( + '*' => '/^[*]((?:\\\\\*|[^*]|[*][*][^*]+?[*][*])+?)[*](?![*])/s', + '_' => '/^_((?:\\\\_|[^_]|__[^_]*__)+?)_(?!_)\b/us', + ); + + protected $regexHtmlAttribute = '[a-zA-Z_:][\w:.-]*(?:\s*=\s*(?:[^"\'=<>`\s]+|"[^"]*"|\'[^\']*\'))?'; + + protected $voidElements = array( + 'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', + ); + + protected $textLevelElements = array( + 'a', 'br', 'bdo', 'abbr', 'blink', 'nextid', 'acronym', 'basefont', + 'b', 'em', 'big', 'cite', 'small', 'spacer', 'listing', + 'i', 'rp', 'del', 'code', 'strike', 'marquee', + 'q', 'rt', 'ins', 'font', 'strong', + 's', 'tt', 'sub', 'mark', + 'u', 'xm', 'sup', 'nobr', + 'var', 'ruby', + 'wbr', 'span', + 'time', + ); +} diff --git a/vendor/erusev/parsedown/README.md b/vendor/erusev/parsedown/README.md new file mode 100644 index 0000000000..6f9f649856 --- /dev/null +++ b/vendor/erusev/parsedown/README.md @@ -0,0 +1,57 @@ +## Parsedown + +[![Build Status](https://img.shields.io/travis/erusev/parsedown/master.svg?style=flat-square)](https://travis-ci.org/erusev/parsedown) + + +Better Markdown Parser in PHP + +[Demo](http://parsedown.org/demo) | +[Benchmarks](http://parsedown.org/speed) | +[Tests](http://parsedown.org/tests/) | +[Documentation](https://github.com/erusev/parsedown/wiki/) + +### Features + +* Super Fast +* [GitHub flavored](https://help.github.com/articles/github-flavored-markdown) +* Extensible +* Tested in 5.3 to 5.6 +* [Markdown Extra extension](https://github.com/erusev/parsedown-extra) + +### Installation + +Include `Parsedown.php` or install [the composer package](https://packagist.org/packages/erusev/parsedown). + +### Example + +``` php +$Parsedown = new Parsedown(); + +echo $Parsedown->text('Hello _Parsedown_!'); # prints:

Hello Parsedown!

+``` + +More examples in [the wiki](https://github.com/erusev/parsedown/wiki/) and in [this video tutorial](http://youtu.be/wYZBY8DEikI). + +### Questions + +**How does Parsedown work?** + +It tries to read Markdown like a human. First, it looks at the lines. It’s interested in how the lines start. This helps it recognise blocks. It knows, for example, that if a line start with a `-` then it perhaps belong to a list. Once it recognises the blocks, it continues to the content. As it reads, it watches out for special characters. This helps it recognise inline elements (or inlines). + +We call this approach "line based". We believe that Parsedown is the first Markdown parser to use it. Since the release of Parsedown, other developers have used the same approach to develop other Markdown parsers in PHP and in other languages. + +**Is it compliant with CommonMark?** + +It passes most of the CommonMark tests. Most of the tests that don't pass deal with cases that are quite uncommon. Still, as CommonMark matures, compliance should improve. + +**Who uses it?** + +[phpDocumentor](http://www.phpdoc.org/), [October CMS](http://octobercms.com/), [Bolt CMS](http://bolt.cm/), [Kirby CMS](http://getkirby.com/), [Grav CMS](http://getgrav.org/), [Statamic CMS](http://www.statamic.com/), [RaspberryPi.org](http://www.raspberrypi.org/) and [more](https://www.versioneye.com/php/erusev:parsedown/references). + +**How can I help?** + +Use it, star it, share it and if you feel generous, [donate](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=528P3NZQMP8N2). + +--- + +You might also like [Caret](http://caret.io) - our Markdown editor for the desktop. diff --git a/vendor/erusev/parsedown/composer.json b/vendor/erusev/parsedown/composer.json new file mode 100644 index 0000000000..1439b824dd --- /dev/null +++ b/vendor/erusev/parsedown/composer.json @@ -0,0 +1,18 @@ +{ + "name": "erusev/parsedown", + "description": "Parser for Markdown.", + "keywords": ["markdown", "parser"], + "homepage": "http://parsedown.org", + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "Emanuil Rusev", + "email": "hello@erusev.com", + "homepage": "http://erusev.com" + } + ], + "autoload": { + "psr-0": {"Parsedown": ""} + } +} \ No newline at end of file diff --git a/vendor/erusev/parsedown/phpunit.xml.dist b/vendor/erusev/parsedown/phpunit.xml.dist new file mode 100644 index 0000000000..b2d5e9d4fe --- /dev/null +++ b/vendor/erusev/parsedown/phpunit.xml.dist @@ -0,0 +1,8 @@ + + + + + test/ParsedownTest.php + + + \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/CommonMarkTest.php b/vendor/erusev/parsedown/test/CommonMarkTest.php new file mode 100644 index 0000000000..9b8d116209 --- /dev/null +++ b/vendor/erusev/parsedown/test/CommonMarkTest.php @@ -0,0 +1,74 @@ +setUrlsLinked(false); + + $actualHtml = $Parsedown->text($markdown); + $actualHtml = $this->normalizeMarkup($actualHtml); + + $this->assertEquals($expectedHtml, $actualHtml); + } + + function data() + { + $spec = file_get_contents(self::SPEC_URL); + $spec = strstr($spec, '', true); + + $tests = array(); + $currentSection = ''; + + preg_replace_callback( + '/^\.\n([\s\S]*?)^\.\n([\s\S]*?)^\.$|^#{1,6} *(.*)$/m', + function($matches) use ( & $tests, & $currentSection, & $testCount) { + if (isset($matches[3]) and $matches[3]) { + $currentSection = $matches[3]; + } else { + $testCount++; + $markdown = $matches[1]; + $markdown = preg_replace('/→/', "\t", $markdown); + $expectedHtml = $matches[2]; + $expectedHtml = $this->normalizeMarkup($expectedHtml); + $tests []= array( + $currentSection, # section + $markdown, # markdown + $expectedHtml, # html + ); + } + }, + $spec + ); + + return $tests; + } + + private function normalizeMarkup($markup) + { + $markup = preg_replace("/\n+/", "\n", $markup); + $markup = preg_replace('/^\s+/m', '', $markup); + $markup = preg_replace('/^((?:<[\w]+>)+)\n/m', '$1', $markup); + $markup = preg_replace('/\n((?:<\/[\w]+>)+)$/m', '$1', $markup); + $markup = trim($markup); + + return $markup; + } +} diff --git a/vendor/erusev/parsedown/test/ParsedownTest.php b/vendor/erusev/parsedown/test/ParsedownTest.php new file mode 100644 index 0000000000..c922ab1f2b --- /dev/null +++ b/vendor/erusev/parsedown/test/ParsedownTest.php @@ -0,0 +1,159 @@ +dirs = $this->initDirs(); + $this->Parsedown = $this->initParsedown(); + + parent::__construct($name, $data, $dataName); + } + + private $dirs, $Parsedown; + + /** + * @return array + */ + protected function initDirs() + { + $dirs []= dirname(__FILE__).'/data/'; + + return $dirs; + } + + /** + * @return Parsedown + */ + protected function initParsedown() + { + $Parsedown = new Parsedown(); + + return $Parsedown; + } + + /** + * @dataProvider data + * @param $test + * @param $dir + */ + function test_($test, $dir) + { + $markdown = file_get_contents($dir . $test . '.md'); + + $expectedMarkup = file_get_contents($dir . $test . '.html'); + + $expectedMarkup = str_replace("\r\n", "\n", $expectedMarkup); + $expectedMarkup = str_replace("\r", "\n", $expectedMarkup); + + $actualMarkup = $this->Parsedown->text($markdown); + + $this->assertEquals($expectedMarkup, $actualMarkup); + } + + function data() + { + $data = array(); + + foreach ($this->dirs as $dir) + { + $Folder = new DirectoryIterator($dir); + + foreach ($Folder as $File) + { + /** @var $File DirectoryIterator */ + + if ( ! $File->isFile()) + { + continue; + } + + $filename = $File->getFilename(); + + $extension = pathinfo($filename, PATHINFO_EXTENSION); + + if ($extension !== 'md') + { + continue; + } + + $basename = $File->getBasename('.md'); + + if (file_exists($dir . $basename . '.html')) + { + $data []= array($basename, $dir); + } + } + } + + return $data; + } + + public function test_no_markup() + { + $markdownWithHtml = <<_content_ + +sparse: + +
+
+_content_ +
+
+ +paragraph + + + +comment + + +MARKDOWN_WITH_MARKUP; + + $expectedHtml = <<<div>content</div>

+

sparse:

+

<div> +<div class="inner"> +content +</div> +</div>

+

paragraph

+

<style type="text/css"> +p { +color: red; +} +</style>

+

comment

+

<!-- html comment -->

+EXPECTED_HTML; + $parsedownWithNoMarkup = new Parsedown(); + $parsedownWithNoMarkup->setMarkupEscaped(true); + $this->assertEquals($expectedHtml, $parsedownWithNoMarkup->text($markdownWithHtml)); + } + + public function testLateStaticBinding() + { + include 'test/TestParsedown.php'; + + $parsedown = Parsedown::instance(); + $this->assertInstanceOf('Parsedown', $parsedown); + + // After instance is already called on Parsedown + // subsequent calls with the same arguments return the same instance + $sameParsedown = TestParsedown::instance(); + $this->assertInstanceOf('Parsedown', $sameParsedown); + $this->assertSame($parsedown, $sameParsedown); + + $testParsedown = TestParsedown::instance('test late static binding'); + $this->assertInstanceOf('TestParsedown', $testParsedown); + + $sameInstanceAgain = TestParsedown::instance('test late static binding'); + $this->assertSame($testParsedown, $sameInstanceAgain); + } +} diff --git a/vendor/erusev/parsedown/test/TestParsedown.php b/vendor/erusev/parsedown/test/TestParsedown.php new file mode 100644 index 0000000000..7024dfbccd --- /dev/null +++ b/vendor/erusev/parsedown/test/TestParsedown.php @@ -0,0 +1,5 @@ + + + +header 1 +header 2 + + + + +cell 1.1 +cell 1.2 + + +cell 2.1 +cell 2.2 + + + \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/aesthetic_table.md b/vendor/erusev/parsedown/test/data/aesthetic_table.md new file mode 100644 index 0000000000..5245e6c9dd --- /dev/null +++ b/vendor/erusev/parsedown/test/data/aesthetic_table.md @@ -0,0 +1,4 @@ +| header 1 | header 2 | +| -------- | -------- | +| cell 1.1 | cell 1.2 | +| cell 2.1 | cell 2.2 | \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/aligned_table.html b/vendor/erusev/parsedown/test/data/aligned_table.html new file mode 100644 index 0000000000..c4acfcb68c --- /dev/null +++ b/vendor/erusev/parsedown/test/data/aligned_table.html @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + +
header 1header 2header 2
cell 1.1cell 1.2cell 1.3
cell 2.1cell 2.2cell 2.3
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/aligned_table.md b/vendor/erusev/parsedown/test/data/aligned_table.md new file mode 100644 index 0000000000..69a45f90fe --- /dev/null +++ b/vendor/erusev/parsedown/test/data/aligned_table.md @@ -0,0 +1,4 @@ +| header 1 | header 2 | header 2 | +| :------- | :------: | -------: | +| cell 1.1 | cell 1.2 | cell 1.3 | +| cell 2.1 | cell 2.2 | cell 2.3 | \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/atx_heading.html b/vendor/erusev/parsedown/test/data/atx_heading.html new file mode 100644 index 0000000000..751f8739f8 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/atx_heading.html @@ -0,0 +1,9 @@ +

h1

+

h2

+

h3

+

h4

+
h5
+
h6
+

####### not a heading

+

closed h1

+

#

\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/atx_heading.md b/vendor/erusev/parsedown/test/data/atx_heading.md new file mode 100644 index 0000000000..ad97b44ca1 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/atx_heading.md @@ -0,0 +1,17 @@ +# h1 + +## h2 + +### h3 + +#### h4 + +##### h5 + +###### h6 + +####### not a heading + +# closed h1 # + +# \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/automatic_link.html b/vendor/erusev/parsedown/test/data/automatic_link.html new file mode 100644 index 0000000000..50a94ba0fb --- /dev/null +++ b/vendor/erusev/parsedown/test/data/automatic_link.html @@ -0,0 +1 @@ +

http://example.com

\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/automatic_link.md b/vendor/erusev/parsedown/test/data/automatic_link.md new file mode 100644 index 0000000000..08d3bf46a8 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/automatic_link.md @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/block-level_html.html b/vendor/erusev/parsedown/test/data/block-level_html.html new file mode 100644 index 0000000000..6443a4a6a5 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/block-level_html.html @@ -0,0 +1,12 @@ +
_content_
+

paragraph

+
+
+ _content_ +
+
+ +
+ home
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/block-level_html.md b/vendor/erusev/parsedown/test/data/block-level_html.md new file mode 100644 index 0000000000..17cbc22d31 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/block-level_html.md @@ -0,0 +1,16 @@ +
_content_
+ +paragraph + +
+
+ _content_ +
+
+ + + +
+ home
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/code_block.html b/vendor/erusev/parsedown/test/data/code_block.html new file mode 100644 index 0000000000..889b02d993 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/code_block.html @@ -0,0 +1,8 @@ +
<?php
+
+$message = 'Hello World!';
+echo $message;
+
+
> not a quote
+- not a list item
+[not a reference]: http://foo.com
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/code_block.md b/vendor/erusev/parsedown/test/data/code_block.md new file mode 100644 index 0000000000..2cfc953cc6 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/code_block.md @@ -0,0 +1,10 @@ + not a quote + - not a list item + [not a reference]: http://foo.com \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/code_span.html b/vendor/erusev/parsedown/test/data/code_span.html new file mode 100644 index 0000000000..5c4c231e38 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/code_span.html @@ -0,0 +1,6 @@ +

a code span

+

this is also a codespan trailing text

+

and look at this one!

+

single backtick in a code span: `

+

backtick-delimited string in a code span: `foo`

+

sth `` sth

\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/code_span.md b/vendor/erusev/parsedown/test/data/code_span.md new file mode 100644 index 0000000000..c2f1a74420 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/code_span.md @@ -0,0 +1,11 @@ +a `code span` + +`this is also a codespan` trailing text + +`and look at this one!` + +single backtick in a code span: `` ` `` + +backtick-delimited string in a code span: `` `foo` `` + +`sth `` sth` \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/compound_blockquote.html b/vendor/erusev/parsedown/test/data/compound_blockquote.html new file mode 100644 index 0000000000..37afb57a48 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/compound_blockquote.html @@ -0,0 +1,9 @@ +
+

header

+

paragraph

+
    +
  • li
  • +
+
+

paragraph

+
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/compound_blockquote.md b/vendor/erusev/parsedown/test/data/compound_blockquote.md new file mode 100644 index 0000000000..80c4aed16c --- /dev/null +++ b/vendor/erusev/parsedown/test/data/compound_blockquote.md @@ -0,0 +1,10 @@ +> header +> ------ +> +> paragraph +> +> - li +> +> --- +> +> paragraph \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/compound_emphasis.html b/vendor/erusev/parsedown/test/data/compound_emphasis.html new file mode 100644 index 0000000000..178dd54bae --- /dev/null +++ b/vendor/erusev/parsedown/test/data/compound_emphasis.html @@ -0,0 +1,2 @@ +

code code

+

codecodecode

\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/compound_emphasis.md b/vendor/erusev/parsedown/test/data/compound_emphasis.md new file mode 100644 index 0000000000..6fe07f2600 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/compound_emphasis.md @@ -0,0 +1,4 @@ +_`code`_ __`code`__ + +*`code`**`code`**`code`* + diff --git a/vendor/erusev/parsedown/test/data/compound_list.html b/vendor/erusev/parsedown/test/data/compound_list.html new file mode 100644 index 0000000000..f5593c1420 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/compound_list.html @@ -0,0 +1,12 @@ +
    +
  • +

    paragraph

    +

    paragraph

    +
  • +
  • +

    paragraph

    +
    +

    quote

    +
    +
  • +
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/compound_list.md b/vendor/erusev/parsedown/test/data/compound_list.md new file mode 100644 index 0000000000..ed7f0c60f3 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/compound_list.md @@ -0,0 +1,7 @@ +- paragraph + + paragraph + +- paragraph + + > quote \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/deeply_nested_list.html b/vendor/erusev/parsedown/test/data/deeply_nested_list.html new file mode 100644 index 0000000000..d2c7e5acc8 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/deeply_nested_list.html @@ -0,0 +1,12 @@ +
    +
  • li +
      +
    • li +
        +
      • li
      • +
      • li
      • +
    • +
    • li
    • +
  • +
  • li
  • +
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/deeply_nested_list.md b/vendor/erusev/parsedown/test/data/deeply_nested_list.md new file mode 100644 index 0000000000..76b7552d85 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/deeply_nested_list.md @@ -0,0 +1,6 @@ +- li + - li + - li + - li + - li +- li \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/em_strong.html b/vendor/erusev/parsedown/test/data/em_strong.html new file mode 100644 index 0000000000..323d60aec7 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/em_strong.html @@ -0,0 +1,8 @@ +

em strong

+

em strong strong

+

strong em strong

+

strong em strong strong

+

em strong

+

em strong strong

+

strong em strong

+

strong em strong strong

\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/em_strong.md b/vendor/erusev/parsedown/test/data/em_strong.md new file mode 100644 index 0000000000..9abeb3fd4a --- /dev/null +++ b/vendor/erusev/parsedown/test/data/em_strong.md @@ -0,0 +1,15 @@ +___em strong___ + +___em strong_ strong__ + +__strong _em strong___ + +__strong _em strong_ strong__ + +***em strong*** + +***em strong* strong** + +**strong *em strong*** + +**strong *em strong* strong** \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/email.html b/vendor/erusev/parsedown/test/data/email.html new file mode 100644 index 0000000000..c40759c969 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/email.html @@ -0,0 +1 @@ +

my email is me@example.com

\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/email.md b/vendor/erusev/parsedown/test/data/email.md new file mode 100644 index 0000000000..26b7b6cc56 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/email.md @@ -0,0 +1 @@ +my email is \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/emphasis.html b/vendor/erusev/parsedown/test/data/emphasis.html new file mode 100644 index 0000000000..60ff4bd8b3 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/emphasis.html @@ -0,0 +1,8 @@ +

underscore, asterisk, one two, three four, a, b

+

strong and em and strong and em

+

line +line +line

+

this_is_not_an_emphasis

+

an empty emphasis __ ** is not an emphasis

+

*mixed *double and single asterisk** spans

\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/emphasis.md b/vendor/erusev/parsedown/test/data/emphasis.md new file mode 100644 index 0000000000..85b9d2299f --- /dev/null +++ b/vendor/erusev/parsedown/test/data/emphasis.md @@ -0,0 +1,13 @@ +_underscore_, *asterisk*, _one two_, *three four*, _a_, *b* + +**strong** and *em* and **strong** and *em* + +_line +line +line_ + +this_is_not_an_emphasis + +an empty emphasis __ ** is not an emphasis + +*mixed **double and* single asterisk** spans \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/escaping.html b/vendor/erusev/parsedown/test/data/escaping.html new file mode 100644 index 0000000000..ab1c41fdc1 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/escaping.html @@ -0,0 +1,6 @@ +

escaped *emphasis*.

+

escaped \*emphasis\* in a code span

+
escaped \*emphasis\* in a code block
+

\ ` * _ { } [ ] ( ) > # + - . !

+

one_two one_two

+

one*two one*two

\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/escaping.md b/vendor/erusev/parsedown/test/data/escaping.md new file mode 100644 index 0000000000..9f174e98c2 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/escaping.md @@ -0,0 +1,11 @@ +escaped \*emphasis\*. + +`escaped \*emphasis\* in a code span` + + escaped \*emphasis\* in a code block + +\\ \` \* \_ \{ \} \[ \] \( \) \> \# \+ \- \. \! + +_one\_two_ __one\_two__ + +*one\*two* **one\*two** \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/fenced_code_block.html b/vendor/erusev/parsedown/test/data/fenced_code_block.html new file mode 100644 index 0000000000..8bdabba962 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/fenced_code_block.html @@ -0,0 +1,6 @@ +
<?php
+
+$message = 'fenced code block';
+echo $message;
+
tilde
+
echo 'language identifier';
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/fenced_code_block.md b/vendor/erusev/parsedown/test/data/fenced_code_block.md new file mode 100644 index 0000000000..cbed8ebb56 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/fenced_code_block.md @@ -0,0 +1,14 @@ +``` + +
+
+
+
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/horizontal_rule.md b/vendor/erusev/parsedown/test/data/horizontal_rule.md new file mode 100644 index 0000000000..bf461a925e --- /dev/null +++ b/vendor/erusev/parsedown/test/data/horizontal_rule.md @@ -0,0 +1,9 @@ +--- + +- - - + + - - - + +*** + +___ \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/html_comment.html b/vendor/erusev/parsedown/test/data/html_comment.html new file mode 100644 index 0000000000..566dc3add5 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/html_comment.html @@ -0,0 +1,5 @@ + +

paragraph

+ +

paragraph

\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/html_comment.md b/vendor/erusev/parsedown/test/data/html_comment.md new file mode 100644 index 0000000000..6ddfdb441e --- /dev/null +++ b/vendor/erusev/parsedown/test/data/html_comment.md @@ -0,0 +1,8 @@ + + +paragraph + + + +paragraph \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/html_entity.html b/vendor/erusev/parsedown/test/data/html_entity.html new file mode 100644 index 0000000000..4d23e3cd4b --- /dev/null +++ b/vendor/erusev/parsedown/test/data/html_entity.html @@ -0,0 +1 @@ +

& © {

\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/html_entity.md b/vendor/erusev/parsedown/test/data/html_entity.md new file mode 100644 index 0000000000..ff545ea5ca --- /dev/null +++ b/vendor/erusev/parsedown/test/data/html_entity.md @@ -0,0 +1 @@ +& © { \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/image_reference.html b/vendor/erusev/parsedown/test/data/image_reference.html new file mode 100644 index 0000000000..67fbd2c864 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/image_reference.html @@ -0,0 +1,2 @@ +

Markdown Logo

+

![missing reference]

\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/image_reference.md b/vendor/erusev/parsedown/test/data/image_reference.md new file mode 100644 index 0000000000..1e11d9479e --- /dev/null +++ b/vendor/erusev/parsedown/test/data/image_reference.md @@ -0,0 +1,5 @@ +![Markdown Logo][image] + +[image]: /md.png + +![missing reference] \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/image_title.html b/vendor/erusev/parsedown/test/data/image_title.html new file mode 100644 index 0000000000..957c9505cf --- /dev/null +++ b/vendor/erusev/parsedown/test/data/image_title.html @@ -0,0 +1,2 @@ +

alt

+

blank title

\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/image_title.md b/vendor/erusev/parsedown/test/data/image_title.md new file mode 100644 index 0000000000..7ce2849a51 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/image_title.md @@ -0,0 +1,3 @@ +![alt](/md.png "title") + +![blank title](/md.png "") \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/implicit_reference.html b/vendor/erusev/parsedown/test/data/implicit_reference.html new file mode 100644 index 0000000000..24b51c1b00 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/implicit_reference.html @@ -0,0 +1,4 @@ +

an implicit reference link

+

an implicit reference link with an empty link definition

+

an implicit reference link followed by another

+

an explicit reference link with a title

\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/implicit_reference.md b/vendor/erusev/parsedown/test/data/implicit_reference.md new file mode 100644 index 0000000000..f850df964c --- /dev/null +++ b/vendor/erusev/parsedown/test/data/implicit_reference.md @@ -0,0 +1,13 @@ +an [implicit] reference link + +[implicit]: http://example.com + +an [implicit][] reference link with an empty link definition + +an [implicit][] reference link followed by [another][] + +[another]: http://cnn.com + +an [explicit][example] reference link with a title + +[example]: http://example.com "Example" \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/inline_link.html b/vendor/erusev/parsedown/test/data/inline_link.html new file mode 100644 index 0000000000..5ad564aa3d --- /dev/null +++ b/vendor/erusev/parsedown/test/data/inline_link.html @@ -0,0 +1,6 @@ +

link

+

link with parentheses in URL

+

(link) in parentheses

+

link

+

MD Logo

+

MD Logo and text

\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/inline_link.md b/vendor/erusev/parsedown/test/data/inline_link.md new file mode 100644 index 0000000000..6bac0b35e6 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/inline_link.md @@ -0,0 +1,11 @@ +[link](http://example.com) + +[link](/url-(parentheses)) with parentheses in URL + +([link](/index.php)) in parentheses + +[`link`](http://example.com) + +[![MD Logo](http://parsedown.org/md.png)](http://example.com) + +[![MD Logo](http://parsedown.org/md.png) and text](http://example.com) \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/inline_link_title.html b/vendor/erusev/parsedown/test/data/inline_link_title.html new file mode 100644 index 0000000000..ecdfd03daa --- /dev/null +++ b/vendor/erusev/parsedown/test/data/inline_link_title.html @@ -0,0 +1,6 @@ +

single quotes

+

double quotes

+

single quotes blank

+

double quotes blank

+

space

+

parentheses

\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/inline_link_title.md b/vendor/erusev/parsedown/test/data/inline_link_title.md new file mode 100644 index 0000000000..6e1c5af9bc --- /dev/null +++ b/vendor/erusev/parsedown/test/data/inline_link_title.md @@ -0,0 +1,11 @@ +[single quotes](http://example.com 'Title') + +[double quotes](http://example.com "Title") + +[single quotes blank](http://example.com '') + +[double quotes blank](http://example.com "") + +[space](http://example.com "2 Words") + +[parentheses](http://example.com/url-(parentheses) "Title") \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/inline_title.html b/vendor/erusev/parsedown/test/data/inline_title.html new file mode 100644 index 0000000000..bbab93b6c5 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/inline_title.html @@ -0,0 +1 @@ +

single quotes and double quotes

\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/inline_title.md b/vendor/erusev/parsedown/test/data/inline_title.md new file mode 100644 index 0000000000..cb09344a1b --- /dev/null +++ b/vendor/erusev/parsedown/test/data/inline_title.md @@ -0,0 +1 @@ +[single quotes](http://example.com 'Example') and [double quotes](http://example.com "Example") \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/lazy_blockquote.html b/vendor/erusev/parsedown/test/data/lazy_blockquote.html new file mode 100644 index 0000000000..0a2a2aaf9a --- /dev/null +++ b/vendor/erusev/parsedown/test/data/lazy_blockquote.html @@ -0,0 +1,6 @@ +
+

quote +the rest of it

+

another paragraph +the rest of it

+
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/lazy_blockquote.md b/vendor/erusev/parsedown/test/data/lazy_blockquote.md new file mode 100644 index 0000000000..48f645f947 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/lazy_blockquote.md @@ -0,0 +1,5 @@ +> quote +the rest of it + +> another paragraph +the rest of it \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/lazy_list.html b/vendor/erusev/parsedown/test/data/lazy_list.html new file mode 100644 index 0000000000..1a51992495 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/lazy_list.html @@ -0,0 +1,4 @@ +
    +
  • li +the rest of it
  • +
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/lazy_list.md b/vendor/erusev/parsedown/test/data/lazy_list.md new file mode 100644 index 0000000000..62ad9d719b --- /dev/null +++ b/vendor/erusev/parsedown/test/data/lazy_list.md @@ -0,0 +1,2 @@ +- li +the rest of it \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/line_break.html b/vendor/erusev/parsedown/test/data/line_break.html new file mode 100644 index 0000000000..5f37d854c0 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/line_break.html @@ -0,0 +1,2 @@ +

line
+line

\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/line_break.md b/vendor/erusev/parsedown/test/data/line_break.md new file mode 100644 index 0000000000..04dff43e04 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/line_break.md @@ -0,0 +1,2 @@ +line +line \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/multiline_list_paragraph.html b/vendor/erusev/parsedown/test/data/multiline_list_paragraph.html new file mode 100644 index 0000000000..3247bd2279 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/multiline_list_paragraph.html @@ -0,0 +1,7 @@ +
    +
  • +

    li

    +

    line +line

    +
  • +
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/multiline_list_paragraph.md b/vendor/erusev/parsedown/test/data/multiline_list_paragraph.md new file mode 100644 index 0000000000..f5b42729fe --- /dev/null +++ b/vendor/erusev/parsedown/test/data/multiline_list_paragraph.md @@ -0,0 +1,4 @@ +- li + + line + line \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/nested_block-level_html.html b/vendor/erusev/parsedown/test/data/nested_block-level_html.html new file mode 100644 index 0000000000..bfbef54d76 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/nested_block-level_html.html @@ -0,0 +1,10 @@ +
+_parent_ +
+_child_ +
+
+_adopted child_
+
+
+

outside

\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/nested_block-level_html.md b/vendor/erusev/parsedown/test/data/nested_block-level_html.md new file mode 100644 index 0000000000..5e01e10978 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/nested_block-level_html.md @@ -0,0 +1,11 @@ +
+_parent_ +
+_child_ +
+
+_adopted child_
+
+
+ +_outside_ \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/ordered_list.html b/vendor/erusev/parsedown/test/data/ordered_list.html new file mode 100644 index 0000000000..b6c5216ca0 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/ordered_list.html @@ -0,0 +1,13 @@ +
    +
  1. one
  2. +
  3. two
  4. +
+

repeating numbers:

+
    +
  1. one
  2. +
  3. two
  4. +
+

large numbers:

+
    +
  1. one
  2. +
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/ordered_list.md b/vendor/erusev/parsedown/test/data/ordered_list.md new file mode 100644 index 0000000000..b307032cfe --- /dev/null +++ b/vendor/erusev/parsedown/test/data/ordered_list.md @@ -0,0 +1,11 @@ +1. one +2. two + +repeating numbers: + +1. one +1. two + +large numbers: + +123. one \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/paragraph_list.html b/vendor/erusev/parsedown/test/data/paragraph_list.html new file mode 100644 index 0000000000..ced1c43ee3 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/paragraph_list.html @@ -0,0 +1,12 @@ +

paragraph

+
    +
  • li
  • +
  • li
  • +
+

paragraph

+
    +
  • +

    li

    +
  • +
  • li
  • +
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/paragraph_list.md b/vendor/erusev/parsedown/test/data/paragraph_list.md new file mode 100644 index 0000000000..b973908ced --- /dev/null +++ b/vendor/erusev/parsedown/test/data/paragraph_list.md @@ -0,0 +1,9 @@ +paragraph +- li +- li + +paragraph + + * li + + * li \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/reference_title.html b/vendor/erusev/parsedown/test/data/reference_title.html new file mode 100644 index 0000000000..8f2be944c6 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/reference_title.html @@ -0,0 +1,2 @@ +

double quotes and single quotes and parentheses

+

[invalid title]: http://example.com example title

\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/reference_title.md b/vendor/erusev/parsedown/test/data/reference_title.md new file mode 100644 index 0000000000..43cb21708c --- /dev/null +++ b/vendor/erusev/parsedown/test/data/reference_title.md @@ -0,0 +1,6 @@ +[double quotes] and [single quotes] and [parentheses] + +[double quotes]: http://example.com "example title" +[single quotes]: http://example.com 'example title' +[parentheses]: http://example.com (example title) +[invalid title]: http://example.com example title \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/self-closing_html.html b/vendor/erusev/parsedown/test/data/self-closing_html.html new file mode 100644 index 0000000000..4d072b43ff --- /dev/null +++ b/vendor/erusev/parsedown/test/data/self-closing_html.html @@ -0,0 +1,12 @@ +
+

paragraph

+
+

paragraph

+
+

paragraph

+
+

paragraph

+
+

paragraph

+
+

paragraph

\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/self-closing_html.md b/vendor/erusev/parsedown/test/data/self-closing_html.md new file mode 100644 index 0000000000..acb20327d8 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/self-closing_html.md @@ -0,0 +1,12 @@ +
+paragraph +
+paragraph +
+paragraph +
+paragraph +
+paragraph +
+paragraph \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/separated_nested_list.html b/vendor/erusev/parsedown/test/data/separated_nested_list.html new file mode 100644 index 0000000000..80a5cae264 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/separated_nested_list.html @@ -0,0 +1,9 @@ +
    +
  • +

    li

    +
      +
    • li
    • +
    • li
    • +
    +
  • +
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/separated_nested_list.md b/vendor/erusev/parsedown/test/data/separated_nested_list.md new file mode 100644 index 0000000000..d7cd1af792 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/separated_nested_list.md @@ -0,0 +1,4 @@ +- li + + - li + - li \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/setext_header.html b/vendor/erusev/parsedown/test/data/setext_header.html new file mode 100644 index 0000000000..60aac08151 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/setext_header.html @@ -0,0 +1,5 @@ +

h1

+

h2

+

single character

+

not a header

+
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/setext_header.md b/vendor/erusev/parsedown/test/data/setext_header.md new file mode 100644 index 0000000000..c43b52c367 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/setext_header.md @@ -0,0 +1,12 @@ +h1 +== + +h2 +-- + +single character +- + +not a header + +------------ \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/simple_blockquote.html b/vendor/erusev/parsedown/test/data/simple_blockquote.html new file mode 100644 index 0000000000..8225d57cc2 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/simple_blockquote.html @@ -0,0 +1,11 @@ +
+

quote

+
+

indented:

+
+

quote

+
+

no space after >:

+
+

quote

+
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/simple_blockquote.md b/vendor/erusev/parsedown/test/data/simple_blockquote.md new file mode 100644 index 0000000000..22b6b11a95 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/simple_blockquote.md @@ -0,0 +1,7 @@ +> quote + +indented: + > quote + +no space after `>`: +>quote \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/simple_table.html b/vendor/erusev/parsedown/test/data/simple_table.html new file mode 100644 index 0000000000..237d7efb33 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/simple_table.html @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + +
header 1header 2
cell 1.1cell 1.2
cell 2.1cell 2.2
+
+ + + + + + + + + + + + + + + + + +
header 1header 2
cell 1.1cell 1.2
cell 2.1cell 2.2
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/simple_table.md b/vendor/erusev/parsedown/test/data/simple_table.md new file mode 100644 index 0000000000..466d140e31 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/simple_table.md @@ -0,0 +1,11 @@ +header 1 | header 2 +-------- | -------- +cell 1.1 | cell 1.2 +cell 2.1 | cell 2.2 + +--- + +header 1 | header 2 +:------- | -------- +cell 1.1 | cell 1.2 +cell 2.1 | cell 2.2 \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/span-level_html.html b/vendor/erusev/parsedown/test/data/span-level_html.html new file mode 100644 index 0000000000..f852a25a23 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/span-level_html.html @@ -0,0 +1,5 @@ +

an important link

+

broken
+line

+

inline tag at the beginning

+

http://example.com

\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/span-level_html.md b/vendor/erusev/parsedown/test/data/span-level_html.md new file mode 100644 index 0000000000..f221965553 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/span-level_html.md @@ -0,0 +1,8 @@ +an important link + +broken
+line + +inline tag at the beginning + +http://example.com \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/sparse_dense_list.html b/vendor/erusev/parsedown/test/data/sparse_dense_list.html new file mode 100644 index 0000000000..095bc73969 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/sparse_dense_list.html @@ -0,0 +1,7 @@ +
    +
  • +

    li

    +
  • +
  • li
  • +
  • li
  • +
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/sparse_dense_list.md b/vendor/erusev/parsedown/test/data/sparse_dense_list.md new file mode 100644 index 0000000000..576842277d --- /dev/null +++ b/vendor/erusev/parsedown/test/data/sparse_dense_list.md @@ -0,0 +1,4 @@ +- li + +- li +- li \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/sparse_html.html b/vendor/erusev/parsedown/test/data/sparse_html.html new file mode 100644 index 0000000000..9e896274c8 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/sparse_html.html @@ -0,0 +1,8 @@ +
+line 1 + +line 2 +line 3 + +line 4 +
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/sparse_html.md b/vendor/erusev/parsedown/test/data/sparse_html.md new file mode 100644 index 0000000000..9e896274c8 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/sparse_html.md @@ -0,0 +1,8 @@ +
+line 1 + +line 2 +line 3 + +line 4 +
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/sparse_list.html b/vendor/erusev/parsedown/test/data/sparse_list.html new file mode 100644 index 0000000000..452b2b86dd --- /dev/null +++ b/vendor/erusev/parsedown/test/data/sparse_list.html @@ -0,0 +1,15 @@ +
    +
  • +

    li

    +
  • +
  • li
  • +
+
+
    +
  • +

    li

    +
      +
    • indented li
    • +
    +
  • +
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/sparse_list.md b/vendor/erusev/parsedown/test/data/sparse_list.md new file mode 100644 index 0000000000..362a35f57c --- /dev/null +++ b/vendor/erusev/parsedown/test/data/sparse_list.md @@ -0,0 +1,9 @@ +- li + +- li + +--- + +- li + + - indented li \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/special_characters.html b/vendor/erusev/parsedown/test/data/special_characters.html new file mode 100644 index 0000000000..3b652c338f --- /dev/null +++ b/vendor/erusev/parsedown/test/data/special_characters.html @@ -0,0 +1,6 @@ +

AT&T has an ampersand in their name

+

this & that

+

4 < 5 and 6 > 5

+

http://example.com/autolink?a=1&b=2

+

inline link

+

reference link

\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/special_characters.md b/vendor/erusev/parsedown/test/data/special_characters.md new file mode 100644 index 0000000000..111b03b636 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/special_characters.md @@ -0,0 +1,13 @@ +AT&T has an ampersand in their name + +this & that + +4 < 5 and 6 > 5 + + + +[inline link](/script?a=1&b=2) + +[reference link][1] + +[1]: http://example.com/?a=1&b=2 \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/strikethrough.html b/vendor/erusev/parsedown/test/data/strikethrough.html new file mode 100644 index 0000000000..2a9da98213 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/strikethrough.html @@ -0,0 +1,3 @@ +

strikethrough

+

here's one followed by another one

+

~~ this ~~ is not one neither is ~this~

\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/strikethrough.md b/vendor/erusev/parsedown/test/data/strikethrough.md new file mode 100644 index 0000000000..d169144d29 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/strikethrough.md @@ -0,0 +1,5 @@ +~~strikethrough~~ + +here's ~~one~~ followed by ~~another one~~ + +~~ this ~~ is not one neither is ~this~ \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/strong_em.html b/vendor/erusev/parsedown/test/data/strong_em.html new file mode 100644 index 0000000000..b709c9914b --- /dev/null +++ b/vendor/erusev/parsedown/test/data/strong_em.html @@ -0,0 +1,6 @@ +

em strong em

+

strong em em

+

em strong em em

+

em strong em

+

strong em em

+

em strong em em

\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/strong_em.md b/vendor/erusev/parsedown/test/data/strong_em.md new file mode 100644 index 0000000000..f2aa3c7827 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/strong_em.md @@ -0,0 +1,11 @@ +*em **strong em*** + +***strong em** em* + +*em **strong em** em* + +_em __strong em___ + +___strong em__ em_ + +_em __strong em__ em_ \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/tab-indented_code_block.html b/vendor/erusev/parsedown/test/data/tab-indented_code_block.html new file mode 100644 index 0000000000..7c140de730 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/tab-indented_code_block.html @@ -0,0 +1,6 @@ +
<?php
+
+$message = 'Hello World!';
+echo $message;
+
+echo "following a blank line";
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/tab-indented_code_block.md b/vendor/erusev/parsedown/test/data/tab-indented_code_block.md new file mode 100644 index 0000000000..a405a1609a --- /dev/null +++ b/vendor/erusev/parsedown/test/data/tab-indented_code_block.md @@ -0,0 +1,6 @@ + + + +header 1 +header 2 + + + + +cell 1.1 +cell 1.2 + + +| 2.1 +| 2.2 + + +\| 2.1 +link + + + \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/table_inline_markdown.md b/vendor/erusev/parsedown/test/data/table_inline_markdown.md new file mode 100644 index 0000000000..2f3c6200f2 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/table_inline_markdown.md @@ -0,0 +1,5 @@ +| _header_ 1 | header 2 | +| ------------ | ------------ | +| _cell_ 1.1 | ~~cell~~ 1.2 | +| `|` 2.1 | \| 2.2 | +| `\|` 2.1 | [link](/) | \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/text_reference.html b/vendor/erusev/parsedown/test/data/text_reference.html new file mode 100644 index 0000000000..11e4d37ffa --- /dev/null +++ b/vendor/erusev/parsedown/test/data/text_reference.html @@ -0,0 +1,8 @@ +

reference link

+

one with a semantic name

+

[one][404] with no definition

+

multiline +one defined on 2 lines

+

one with a mixed case label and an upper case definition

+

one with the a label on the next line

+

link

\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/text_reference.md b/vendor/erusev/parsedown/test/data/text_reference.md new file mode 100644 index 0000000000..1a66a5cf6b --- /dev/null +++ b/vendor/erusev/parsedown/test/data/text_reference.md @@ -0,0 +1,21 @@ +[reference link][1] + +[1]: http://example.com + +[one][website] with a semantic name + +[website]: http://example.com + +[one][404] with no definition + +[multiline +one][website] defined on 2 lines + +[one][Label] with a mixed case label and an upper case definition + +[LABEL]: http://example.com + +[one] +[1] with the a label on the next line + +[`link`][website] \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/unordered_list.html b/vendor/erusev/parsedown/test/data/unordered_list.html new file mode 100644 index 0000000000..cd95567b7b --- /dev/null +++ b/vendor/erusev/parsedown/test/data/unordered_list.html @@ -0,0 +1,10 @@ +
    +
  • li
  • +
  • li
  • +
+

mixed markers:

+
    +
  • li
  • +
  • li
  • +
  • li
  • +
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/unordered_list.md b/vendor/erusev/parsedown/test/data/unordered_list.md new file mode 100644 index 0000000000..cf62c99f21 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/unordered_list.md @@ -0,0 +1,8 @@ +- li +- li + +mixed markers: + +* li ++ li +- li \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/untidy_table.html b/vendor/erusev/parsedown/test/data/untidy_table.html new file mode 100644 index 0000000000..88e1c2bd44 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/untidy_table.html @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + +
header 1header 2
cell 1.1cell 1.2
cell 2.1cell 2.2
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/untidy_table.md b/vendor/erusev/parsedown/test/data/untidy_table.md new file mode 100644 index 0000000000..8524eb1847 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/untidy_table.md @@ -0,0 +1,4 @@ +| header 1 | header 2 | +| ------------- | ----------- | +| cell 1.1 | cell 1.2 | +| cell 2.1 | cell 2.2 | \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/url_autolinking.html b/vendor/erusev/parsedown/test/data/url_autolinking.html new file mode 100644 index 0000000000..58ca94c6bd --- /dev/null +++ b/vendor/erusev/parsedown/test/data/url_autolinking.html @@ -0,0 +1,3 @@ +

an autolink http://example.com

+

inside of brackets [http://example.com], inside of braces {http://example.com}, inside of parentheses (http://example.com)

+

trailing slash http://example.com/ and http://example.com/path/

\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/url_autolinking.md b/vendor/erusev/parsedown/test/data/url_autolinking.md new file mode 100644 index 0000000000..840f354042 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/url_autolinking.md @@ -0,0 +1,5 @@ +an autolink http://example.com + +inside of brackets [http://example.com], inside of braces {http://example.com}, inside of parentheses (http://example.com) + +trailing slash http://example.com/ and http://example.com/path/ \ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/whitespace.html b/vendor/erusev/parsedown/test/data/whitespace.html new file mode 100644 index 0000000000..f2dd7a0020 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/whitespace.html @@ -0,0 +1 @@ +
code
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/whitespace.md b/vendor/erusev/parsedown/test/data/whitespace.md new file mode 100644 index 0000000000..4cf926a8ad --- /dev/null +++ b/vendor/erusev/parsedown/test/data/whitespace.md @@ -0,0 +1,5 @@ + + + code + + \ No newline at end of file diff --git a/vendor/fguillot/json-rpc/LICENSE b/vendor/fguillot/json-rpc/LICENSE new file mode 100644 index 0000000000..6a362bc163 --- /dev/null +++ b/vendor/fguillot/json-rpc/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Frederic Guillot + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Client.php b/vendor/fguillot/json-rpc/src/JsonRPC/Client.php new file mode 100644 index 0000000000..fed1ce30b3 --- /dev/null +++ b/vendor/fguillot/json-rpc/src/JsonRPC/Client.php @@ -0,0 +1,194 @@ +httpClient = $httpClient ?: new HttpClient($url); + $this->returnException = $returnException; + } + + /** + * Arguments passed are always positional + * + * @access public + * @return $this + */ + public function withPositionalArguments() + { + $this->isNamedArguments = false; + return $this; + } + + /** + * Get HTTP Client + * + * @access public + * @return HttpClient + */ + public function getHttpClient() + { + return $this->httpClient; + } + + /** + * Set username and password + * + * @access public + * @param string $username + * @param string $password + * @return $this + */ + public function authentication($username, $password) + { + $this->httpClient + ->withUsername($username) + ->withPassword($password); + + return $this; + } + + /** + * Automatic mapping of procedures + * + * @access public + * @param string $method Procedure name + * @param array $params Procedure arguments + * @return mixed + */ + public function __call($method, array $params) + { + if ($this->isNamedArguments && count($params) === 1 && is_array($params[0])) { + $params = $params[0]; + } + + return $this->execute($method, $params); + } + + /** + * Start a batch request + * + * @access public + * @return Client + */ + public function batch() + { + $this->isBatch = true; + $this->batch = array(); + return $this; + } + + /** + * Send a batch request + * + * @access public + * @return array + */ + public function send() + { + $this->isBatch = false; + return $this->sendPayload('['.implode(', ', $this->batch).']'); + } + + /** + * Execute a procedure + * + * @access public + * @param string $procedure Procedure name + * @param array $params Procedure arguments + * @param array $reqattrs + * @return mixed + */ + public function execute($procedure, array $params = array(), array $reqattrs = array()) + { + $payload = RequestBuilder::create() + ->withProcedure($procedure) + ->withParams($params) + ->withRequestAttributes($reqattrs) + ->build(); + + if ($this->isBatch) { + $this->batch[] = $payload; + return $this; + } + + return $this->sendPayload($payload); + } + + /** + * Send payload + * + * @access private + * @throws Exception + * @param string $payload + * @return Exception|Client + */ + private function sendPayload($payload) + { + return ResponseParser::create() + ->withReturnException($this->returnException) + ->withPayload($this->httpClient->execute($payload)) + ->parse(); + } +} diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Exception/AccessDeniedException.php b/vendor/fguillot/json-rpc/src/JsonRPC/Exception/AccessDeniedException.php new file mode 100644 index 0000000000..8cb9bc2bc4 --- /dev/null +++ b/vendor/fguillot/json-rpc/src/JsonRPC/Exception/AccessDeniedException.php @@ -0,0 +1,15 @@ +setData($data); + } + + /** + * Attach additional information + * + * @access public + * @param mixed $data [optional] A value that contains additional information about the error. + * @return \JsonRPC\Exception\ResponseException + */ + public function setData($data = null) + { + $this->data = $data; + return $this; + } + + /** + * Get additional information + * + * @access public + * @return mixed|null + */ + public function getData() + { + return $this->data; + } +} diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Exception/ServerErrorException.php b/vendor/fguillot/json-rpc/src/JsonRPC/Exception/ServerErrorException.php new file mode 100644 index 0000000000..ab3ea584db --- /dev/null +++ b/vendor/fguillot/json-rpc/src/JsonRPC/Exception/ServerErrorException.php @@ -0,0 +1,15 @@ +', + 'Content-Type: application/json', + 'Accept: application/json', + 'Connection: close', + ); + + /** + * Username for authentication + * + * @access private + * @var string + */ + private $username; + + /** + * Password for authentication + * + * @access private + * @var string + */ + private $password; + + /** + * Enable debug output to the php error log + * + * @access private + * @var boolean + */ + private $debug = false; + + /** + * Cookies + * + * @access private + * @var array + */ + private $cookies = array(); + + /** + * SSL certificates verification + * + * @access private + * @var boolean + */ + private $verifySslCertificate = true; + + /** + * Callback called before the doing the request + * + * @access private + * @var Closure + */ + private $beforeRequest; + + /** + * HttpClient constructor + * + * @access public + * @param string $url + */ + public function __construct($url = '') + { + $this->url = $url; + } + + /** + * Set URL + * + * @access public + * @param string $url + * @return $this + */ + public function withUrl($url) + { + $this->url = $url; + return $this; + } + + /** + * Set username + * + * @access public + * @param string $username + * @return $this + */ + public function withUsername($username) + { + $this->username = $username; + return $this; + } + + /** + * Set password + * + * @access public + * @param string $password + * @return $this + */ + public function withPassword($password) + { + $this->password = $password; + return $this; + } + + /** + * Set timeout + * + * @access public + * @param integer $timeout + * @return $this + */ + public function withTimeout($timeout) + { + $this->timeout = $timeout; + return $this; + } + + /** + * Set timeout + * + * @access public + * @param array $headers + * @return $this + */ + public function withHeaders(array $headers) + { + $this->headers = array_merge($this->headers, $headers); + return $this; + } + + /** + * Set cookies + * + * @access public + * @param array $cookies + * @param boolean $replace + */ + public function withCookies(array $cookies, $replace = false) + { + if ($replace) { + $this->cookies = $cookies; + } else { + $this->cookies = array_merge($this->cookies, $cookies); + } + } + + /** + * Enable debug mode + * + * @access public + * @return $this + */ + public function withDebug() + { + $this->debug = true; + return $this; + } + + /** + * Disable SSL verification + * + * @access public + * @return $this + */ + public function withoutSslVerification() + { + $this->verifySslCertificate = false; + return $this; + } + + /** + * Assign a callback before the request + * + * @access public + * @param Closure $closure + * @return $this + */ + public function withBeforeRequestCallback(Closure $closure) + { + $this->beforeRequest = $closure; + return $this; + } + + /** + * Get cookies + * + * @access public + * @return array + */ + public function getCookies() + { + return $this->cookies; + } + + /** + * Do the HTTP request + * + * @access public + * @throws ConnectionFailureException + * @param string $payload + * @return array + */ + public function execute($payload) + { + if (is_callable($this->beforeRequest)) { + call_user_func_array($this->beforeRequest, array($this, $payload)); + } + + $stream = fopen(trim($this->url), 'r', false, $this->buildContext($payload)); + + if (! is_resource($stream)) { + throw new ConnectionFailureException('Unable to establish a connection'); + } + + $metadata = stream_get_meta_data($stream); + $headers = $metadata['wrapper_data']; + $response = json_decode(stream_get_contents($stream), true); + + if ($this->debug) { + error_log('==> Request: '.PHP_EOL.(is_string($payload) ? $payload : json_encode($payload, JSON_PRETTY_PRINT))); + error_log('==> Headers: '.PHP_EOL.var_export($headers, true)); + error_log('==> Response: '.PHP_EOL.json_encode($response, JSON_PRETTY_PRINT)); + } + + $this->handleExceptions($headers); + $this->parseCookies($headers); + + return $response; + } + + /** + * Prepare stream context + * + * @access private + * @param string $payload + * @return resource + */ + private function buildContext($payload) + { + $headers = $this->headers; + + if (! empty($this->username) && ! empty($this->password)) { + $headers[] = 'Authorization: Basic '.base64_encode($this->username.':'.$this->password); + } + + if (! empty($this->cookies)){ + $cookies = array(); + + foreach ($this->cookies as $key => $value) { + $cookies[] = $key.'='.$value; + } + + $headers[] = 'Cookie: '.implode('; ', $cookies); + } + + return stream_context_create(array( + 'http' => array( + 'method' => 'POST', + 'protocol_version' => 1.1, + 'timeout' => $this->timeout, + 'max_redirects' => 2, + 'header' => implode("\r\n", $headers), + 'content' => $payload, + 'ignore_errors' => true, + ), + 'ssl' => array( + 'verify_peer' => $this->verifySslCertificate, + 'verify_peer_name' => $this->verifySslCertificate, + ) + )); + } + + /** + * Parse cookies from response + * + * @access private + * @param array $headers + */ + private function parseCookies(array $headers) + { + foreach ($headers as $header) { + $pos = stripos($header, 'Set-Cookie:'); + + if ($pos !== false) { + $cookies = explode(';', substr($header, $pos + 11)); + + foreach ($cookies as $cookie) { + $item = explode('=', $cookie); + + if (count($item) === 2) { + $name = trim($item[0]); + $value = $item[1]; + $this->cookies[$name] = $value; + } + } + } + } + } + + /** + * Throw an exception according the HTTP response + * + * @access public + * @param array $headers + * @throws AccessDeniedException + * @throws ServerErrorException + */ + public function handleExceptions(array $headers) + { + $exceptions = array( + '401' => '\JsonRPC\Exception\AccessDeniedException', + '403' => '\JsonRPC\Exception\AccessDeniedException', + '404' => '\JsonRPC\Exception\ConnectionFailureException', + '500' => '\JsonRPC\Exception\ServerErrorException', + ); + + foreach ($headers as $header) { + foreach ($exceptions as $code => $exception) { + if (strpos($header, 'HTTP/1.0 '.$code) !== false || strpos($header, 'HTTP/1.1 '.$code) !== false) { + throw new $exception('Response: '.$header); + } + } + } + } +} diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/MiddlewareHandler.php b/vendor/fguillot/json-rpc/src/JsonRPC/MiddlewareHandler.php new file mode 100644 index 0000000000..61d5a2d28a --- /dev/null +++ b/vendor/fguillot/json-rpc/src/JsonRPC/MiddlewareHandler.php @@ -0,0 +1,114 @@ +username = $username; + } + + return $this; + } + + /** + * Set password + * + * @access public + * @param string $password + * @return $this + */ + public function withPassword($password) + { + if (! empty($password)) { + $this->password = $password; + } + + return $this; + } + + /** + * Set procedure name + * + * @access public + * @param string $procedureName + * @return $this + */ + public function withProcedure($procedureName) + { + $this->procedureName = $procedureName; + return $this; + } + + /** + * Add a new middleware + * + * @access public + * @param MiddlewareInterface $middleware + * @return MiddlewareHandler + */ + public function withMiddleware(MiddlewareInterface $middleware) + { + $this->middleware[] = $middleware; + return $this; + } + + /** + * Execute all middleware + * + * @access public + */ + public function execute() + { + foreach ($this->middleware as $middleware) { + $middleware->execute($this->username, $this->password, $this->procedureName); + } + } +} diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/MiddlewareInterface.php b/vendor/fguillot/json-rpc/src/JsonRPC/MiddlewareInterface.php new file mode 100644 index 0000000000..ab55261dc4 --- /dev/null +++ b/vendor/fguillot/json-rpc/src/JsonRPC/MiddlewareInterface.php @@ -0,0 +1,27 @@ +callbacks[$procedure] = $callback; + return $this; + } + + /** + * Bind a procedure to a class + * + * @access public + * @param string $procedure Procedure name + * @param mixed $class Class name or instance + * @param string $method Procedure name + * @return $this + */ + public function withClassAndMethod($procedure, $class, $method = '') + { + if ($method === '') { + $method = $procedure; + } + + $this->classes[$procedure] = array($class, $method); + return $this; + } + + /** + * Bind a class instance + * + * @access public + * @param mixed $instance + * @return $this + */ + public function withObject($instance) + { + $this->instances[] = $instance; + return $this; + } + + /** + * Set a before method to call + * + * @access public + * @param string $methodName + * @return $this + */ + public function withBeforeMethod($methodName) + { + $this->beforeMethodName = $methodName; + return $this; + } + + /** + * Execute the procedure + * + * @access public + * @param string $procedure Procedure name + * @param array $params Procedure params + * @return mixed + */ + public function executeProcedure($procedure, array $params = array()) + { + if (isset($this->callbacks[$procedure])) { + return $this->executeCallback($this->callbacks[$procedure], $params); + } elseif (isset($this->classes[$procedure]) && method_exists($this->classes[$procedure][0], $this->classes[$procedure][1])) { + return $this->executeMethod($this->classes[$procedure][0], $this->classes[$procedure][1], $params); + } + + foreach ($this->instances as $instance) { + if (method_exists($instance, $procedure)) { + return $this->executeMethod($instance, $procedure, $params); + } + } + + throw new BadFunctionCallException('Unable to find the procedure'); + } + + /** + * Execute a callback + * + * @access public + * @param Closure $callback Callback + * @param array $params Procedure params + * @return mixed + */ + public function executeCallback(Closure $callback, $params) + { + $reflection = new ReflectionFunction($callback); + + $arguments = $this->getArguments( + $params, + $reflection->getParameters(), + $reflection->getNumberOfRequiredParameters(), + $reflection->getNumberOfParameters() + ); + + return $reflection->invokeArgs($arguments); + } + + /** + * Execute a method + * + * @access public + * @param mixed $class Class name or instance + * @param string $method Method name + * @param array $params Procedure params + * @return mixed + */ + public function executeMethod($class, $method, $params) + { + $instance = is_string($class) ? new $class : $class; + $reflection = new ReflectionMethod($class, $method); + + $this->executeBeforeMethod($instance, $method); + + $arguments = $this->getArguments( + $params, + $reflection->getParameters(), + $reflection->getNumberOfRequiredParameters(), + $reflection->getNumberOfParameters() + ); + + return $reflection->invokeArgs($instance, $arguments); + } + + /** + * Execute before method if defined + * + * @access public + * @param mixed $object + * @param string $method + */ + public function executeBeforeMethod($object, $method) + { + if ($this->beforeMethodName !== '' && method_exists($object, $this->beforeMethodName)) { + call_user_func_array(array($object, $this->beforeMethodName), array($method)); + } + } + + /** + * Get procedure arguments + * + * @access public + * @param array $requestParams Incoming arguments + * @param array $methodParams Procedure arguments + * @param integer $nbRequiredParams Number of required parameters + * @param integer $nbMaxParams Maximum number of parameters + * @return array + */ + public function getArguments(array $requestParams, array $methodParams, $nbRequiredParams, $nbMaxParams) + { + $nbParams = count($requestParams); + + if ($nbParams < $nbRequiredParams) { + throw new InvalidArgumentException('Wrong number of arguments'); + } + + if ($nbParams > $nbMaxParams) { + throw new InvalidArgumentException('Too many arguments'); + } + + if ($this->isPositionalArguments($requestParams)) { + return $requestParams; + } + + return $this->getNamedArguments($requestParams, $methodParams); + } + + /** + * Return true if we have positional parameters + * + * @access public + * @param array $request_params Incoming arguments + * @return bool + */ + public function isPositionalArguments(array $request_params) + { + return array_keys($request_params) === range(0, count($request_params) - 1); + } + + /** + * Get named arguments + * + * @access public + * @param array $requestParams Incoming arguments + * @param array $methodParams Procedure arguments + * @return array + */ + public function getNamedArguments(array $requestParams, array $methodParams) + { + $params = array(); + + foreach ($methodParams as $p) { + $name = $p->getName(); + + if (isset($requestParams[$name])) { + $params[$name] = $requestParams[$name]; + } elseif ($p->isDefaultValueAvailable()) { + $params[$name] = $p->getDefaultValue(); + } else { + throw new InvalidArgumentException('Missing argument: '.$name); + } + } + + return $params; + } +} diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Request/BatchRequestParser.php b/vendor/fguillot/json-rpc/src/JsonRPC/Request/BatchRequestParser.php new file mode 100644 index 0000000000..c0fc776ebb --- /dev/null +++ b/vendor/fguillot/json-rpc/src/JsonRPC/Request/BatchRequestParser.php @@ -0,0 +1,55 @@ +payload as $payload) { + $responses[] = RequestParser::create() + ->withPayload($payload) + ->withProcedureHandler($this->procedureHandler) + ->withMiddlewareHandler($this->middlewareHandler) + ->withLocalException($this->localExceptions) + ->parse(); + } + + $responses = array_filter($responses); + return empty($responses) ? '' : '['.implode(',', $responses).']'; + } + + /** + * Return true if we have a batch request + * + * ex : [ + * 0 => '...', + * 1 => '...', + * 2 => '...', + * 3 => '...', + * ] + * + * @static + * @access public + * @param array $payload + * @return bool + */ + public static function isBatchRequest(array $payload) + { + return array_keys($payload) === range(0, count($payload) - 1); + } +} diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Request/RequestBuilder.php b/vendor/fguillot/json-rpc/src/JsonRPC/Request/RequestBuilder.php new file mode 100644 index 0000000000..145d21c114 --- /dev/null +++ b/vendor/fguillot/json-rpc/src/JsonRPC/Request/RequestBuilder.php @@ -0,0 +1,129 @@ +id = $id; + return $this; + } + + /** + * Set method + * + * @access public + * @param string $procedure + * @return RequestBuilder + */ + public function withProcedure($procedure) + { + $this->procedure = $procedure; + return $this; + } + + /** + * Set parameters + * + * @access public + * @param array $params + * @return RequestBuilder + */ + public function withParams(array $params) + { + $this->params = $params; + return $this; + } + + /** + * Set additional request attributes + * + * @access public + * @param array $reqattrs + * @return RequestBuilder + */ + public function withRequestAttributes(array $reqattrs) + { + $this->reqattrs = $reqattrs; + return $this; + } + + /** + * Build the payload + * + * @access public + * @return string + */ + public function build() + { + $payload = array_merge_recursive($this->reqattrs, array( + 'jsonrpc' => '2.0', + 'method' => $this->procedure, + 'id' => $this->id ?: mt_rand(), + )); + + if (! empty($this->params)) { + $payload['params'] = $this->params; + } + + return json_encode($payload); + } +} diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Request/RequestParser.php b/vendor/fguillot/json-rpc/src/JsonRPC/Request/RequestParser.php new file mode 100644 index 0000000000..ea1b7d43c6 --- /dev/null +++ b/vendor/fguillot/json-rpc/src/JsonRPC/Request/RequestParser.php @@ -0,0 +1,200 @@ +payload = $payload; + return $this; + } + + /** + * Exception classes that should not be relayed to the client + * + * @access public + * @param mixed $exception + * @return $this + */ + public function withLocalException($exception) + { + if (is_array($exception)) { + $this->localExceptions = array_merge($this->localExceptions, $exception); + } else { + $this->localExceptions[] = $exception; + } + + return $this; + } + + /** + * Set procedure handler + * + * @access public + * @param ProcedureHandler $procedureHandler + * @return $this + */ + public function withProcedureHandler(ProcedureHandler $procedureHandler) + { + $this->procedureHandler = $procedureHandler; + return $this; + } + + /** + * Set middleware handler + * + * @access public + * @param MiddlewareHandler $middlewareHandler + * @return $this + */ + public function withMiddlewareHandler(MiddlewareHandler $middlewareHandler) + { + $this->middlewareHandler = $middlewareHandler; + return $this; + } + + /** + * Parse incoming request + * + * @access public + * @return string + * @throws AccessDeniedException + * @throws AuthenticationFailureException + */ + public function parse() + { + try { + + JsonFormatValidator::validate($this->payload); + RpcFormatValidator::validate($this->payload); + + $this->middlewareHandler + ->withProcedure($this->payload['method']) + ->execute(); + + $result = $this->procedureHandler->executeProcedure( + $this->payload['method'], + empty($this->payload['params']) ? array() : $this->payload['params'] + ); + + if (! $this->isNotification()) { + return ResponseBuilder::create() + ->withId($this->payload['id']) + ->withResult($result) + ->build(); + } + } catch (Exception $e) { + return $this->handleExceptions($e); + } + + return ''; + } + + /** + * Handle exceptions + * + * @access protected + * @param Exception $e + * @return string + * @throws Exception + */ + protected function handleExceptions(Exception $e) + { + foreach ($this->localExceptions as $exception) { + if ($e instanceof $exception) { + throw $e; + } + } + + if ($e instanceof InvalidJsonRpcFormatException || ! $this->isNotification()) { + return ResponseBuilder::create() + ->withId(isset($this->payload['id']) ? $this->payload['id'] : null) + ->withException($e) + ->build(); + } + + return ''; + } + + /** + * Return true if the message is a notification + * + * @access protected + * @return bool + */ + protected function isNotification() + { + return is_array($this->payload) && !isset($this->payload['id']); + } +} diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Response/ResponseBuilder.php b/vendor/fguillot/json-rpc/src/JsonRPC/Response/ResponseBuilder.php new file mode 100644 index 0000000000..c1caff92f8 --- /dev/null +++ b/vendor/fguillot/json-rpc/src/JsonRPC/Response/ResponseBuilder.php @@ -0,0 +1,324 @@ + 'application/json', + ); + + /** + * HTTP status + * + * @access private + * @var string + */ + private $status; + + /** + * Exception + * + * @access private + * @var ResponseException + */ + private $exception; + + /** + * Get new object instance + * + * @static + * @access public + * @return ResponseBuilder + */ + public static function create() + { + return new static(); + } + + /** + * Set id + * + * @access public + * @param mixed $id + * @return $this + */ + public function withId($id) + { + $this->id = $id; + return $this; + } + + /** + * Set result + * + * @access public + * @param mixed $result + * @return $this + */ + public function withResult($result) + { + $this->result = $result; + return $this; + } + + /** + * Set error + * + * @access public + * @param integer $code + * @param string $message + * @param string $data + * @return $this + */ + public function withError($code, $message, $data = '') + { + $this->errorCode = $code; + $this->errorMessage = $message; + $this->errorData = $data; + return $this; + } + + /** + * Set exception + * + * @access public + * @param Exception $exception + * @return $this + */ + public function withException(Exception $exception) + { + $this->exception = $exception; + return $this; + } + + /** + * Add HTTP header + * + * @access public + * @param string $name + * @param string $value + * @return $this + */ + public function withHeader($name, $value) + { + $this->headers[$name] = $value; + return $this; + } + + /** + * Add HTTP Status + * + * @access public + * @param string $status + * @return $this + */ + public function withStatus($status) + { + $this->status = $status; + return $this; + } + + /** + * Get status + * + * @access public + * @return string + */ + public function getStatus() + { + return $this->status; + } + + /** + * Get headers + * + * @access public + * @return string[] + */ + public function getHeaders() + { + return $this->headers; + } + + /** + * Build response + * + * @access public + * @return string + */ + public function build() + { + $encodedResponse = json_encode($this->buildResponse()); + JsonEncodingValidator::validate(); + + return $encodedResponse; + } + + /** + * Send HTTP headers + * + * @access public + * @return $this + */ + public function sendHeaders() + { + if (! empty($this->status)) { + header($this->status); + } + + foreach ($this->headers as $name => $value) { + header($name.': '.$value); + } + + return $this; + } + + /** + * Build response payload + * + * @access private + * @return array + */ + private function buildResponse() + { + $response = array('jsonrpc' => '2.0'); + $this->handleExceptions(); + + if (! empty($this->errorMessage)) { + $response['error'] = $this->buildErrorResponse(); + } else { + $response['result'] = $this->result; + } + + $response['id'] = $this->id; + return $response; + } + + /** + * Build response error payload + * + * @access private + * @return array + */ + private function buildErrorResponse() + { + $response = array( + 'code' => $this->errorCode, + 'message' => $this->errorMessage, + ); + + if (! empty($this->errorData)) { + $response['data'] = $this->errorData; + } + + return $response; + } + + /** + * Transform exceptions to JSON-RPC errors + * + * @access private + */ + private function handleExceptions() + { + if ($this->exception instanceof InvalidJsonFormatException) { + $this->errorCode = -32700; + $this->errorMessage = 'Parse error'; + $this->id = null; + } elseif ($this->exception instanceof InvalidJsonRpcFormatException) { + $this->errorCode = -32600; + $this->errorMessage = 'Invalid Request'; + $this->id = null; + } elseif ($this->exception instanceof BadFunctionCallException) { + $this->errorCode = -32601; + $this->errorMessage = 'Method not found'; + } elseif ($this->exception instanceof InvalidArgumentException) { + $this->errorCode = -32602; + $this->errorMessage = 'Invalid params'; + } elseif ($this->exception instanceof ResponseEncodingFailureException) { + $this->errorCode = -32603; + $this->errorMessage = 'Internal error'; + $this->errorData = $this->exception->getMessage(); + } elseif ($this->exception instanceof AuthenticationFailureException) { + $this->errorCode = 401; + $this->errorMessage = 'Unauthorized'; + $this->status = 'HTTP/1.0 401 Unauthorized'; + $this->withHeader('WWW-Authenticate', 'Basic realm="JsonRPC"'); + } elseif ($this->exception instanceof AccessDeniedException) { + $this->errorCode = 403; + $this->errorMessage = 'Forbidden'; + $this->status = 'HTTP/1.0 403 Forbidden'; + } elseif ($this->exception instanceof ResponseException) { + $this->errorCode = $this->exception->getCode(); + $this->errorMessage = $this->exception->getMessage(); + $this->errorData = $this->exception->getData(); + } elseif ($this->exception instanceof Exception) { + $this->errorCode = $this->exception->getCode(); + $this->errorMessage = $this->exception->getMessage(); + } + } +} diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Response/ResponseParser.php b/vendor/fguillot/json-rpc/src/JsonRPC/Response/ResponseParser.php new file mode 100644 index 0000000000..02d449bae5 --- /dev/null +++ b/vendor/fguillot/json-rpc/src/JsonRPC/Response/ResponseParser.php @@ -0,0 +1,154 @@ +returnException = $returnException; + return $this; + } + + /** + * Set payload + * + * @access public + * @param mixed $payload + * @return $this + */ + public function withPayload($payload) + { + $this->payload = $payload; + return $this; + } + + /** + * Parse response + * + * @return array|Exception|null + * @throws InvalidJsonFormatException + * @throws BadFunctionCallException + * @throws InvalidJsonRpcFormatException + * @throws InvalidArgumentException + * @throws Exception + * @throws ResponseException + */ + public function parse() + { + JsonFormatValidator::validate($this->payload); + + if ($this->isBatchResponse()) { + $results = array(); + + foreach ($this->payload as $response) { + $results[] = self::create() + ->withReturnException($this->returnException) + ->withPayload($response) + ->parse(); + } + + return $results; + } + + if (isset($this->payload['error']['code'])) { + try { + $this->handleExceptions(); + } catch (Exception $e) { + if ($this->returnException) { + return $e; + } + throw $e; + } + } + + return isset($this->payload['result']) ? $this->payload['result'] : null; + } + + /** + * Handle exceptions + * + * @access private + * @throws InvalidJsonFormatException + * @throws InvalidJsonRpcFormatException + * @throws ResponseException + */ + private function handleExceptions() + { + switch ($this->payload['error']['code']) { + case -32700: + throw new InvalidJsonFormatException('Parse error: '.$this->payload['error']['message']); + case -32600: + throw new InvalidJsonRpcFormatException('Invalid Request: '.$this->payload['error']['message']); + case -32601: + throw new BadFunctionCallException('Procedure not found: '.$this->payload['error']['message']); + case -32602: + throw new InvalidArgumentException('Invalid arguments: '.$this->payload['error']['message']); + default: + throw new ResponseException( + $this->payload['error']['message'], + $this->payload['error']['code'], + null, + isset($this->payload['error']['data']) ? $this->payload['error']['data'] : null + ); + } + } + + /** + * Return true if we have a batch response + * + * @access private + * @return boolean + */ + private function isBatchResponse() + { + return array_keys($this->payload) === range(0, count($this->payload) - 1); + } +} diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Server.php b/vendor/fguillot/json-rpc/src/JsonRPC/Server.php new file mode 100644 index 0000000000..1ed075a456 --- /dev/null +++ b/vendor/fguillot/json-rpc/src/JsonRPC/Server.php @@ -0,0 +1,386 @@ +payload = json_decode($request, true); + } else { + $this->payload = json_decode(file_get_contents('php://input'), true); + } + + $this->serverVariable = $server ?: $_SERVER; + $this->responseBuilder = $responseBuilder ?: ResponseBuilder::create(); + $this->requestParser = $requestParser ?: RequestParser::create(); + $this->batchRequestParser = $batchRequestParser ?: BatchRequestParser::create(); + $this->procedureHandler = $procedureHandler ?: new ProcedureHandler(); + $this->middlewareHandler = $middlewareHandler ?: new MiddlewareHandler(); + } + + /** + * Define alternative authentication header + * + * @access public + * @param string $header Header name + * @return $this + */ + public function setAuthenticationHeader($header) + { + if (! empty($header)) { + $header = 'HTTP_'.str_replace('-', '_', strtoupper($header)); + $value = $this->getServerVariable($header); + + if (! empty($value)) { + list($this->username, $this->password) = explode(':', base64_decode($value)); + } + } + + return $this; + } + + /** + * Get ProcedureHandler + * + * @access public + * @return ProcedureHandler + */ + public function getProcedureHandler() + { + return $this->procedureHandler; + } + + /** + * Get MiddlewareHandler + * + * @access public + * @return MiddlewareHandler + */ + public function getMiddlewareHandler() + { + return $this->middlewareHandler; + } + + /** + * Get username + * + * @access public + * @return string + */ + public function getUsername() + { + return $this->username ?: $this->getServerVariable('PHP_AUTH_USER'); + } + + /** + * Get password + * + * @access public + * @return string + */ + public function getPassword() + { + return $this->password ?: $this->getServerVariable('PHP_AUTH_PW'); + } + + /** + * IP based client restrictions + * + * @access public + * @param array $hosts List of hosts + * @return $this + */ + public function allowHosts(array $hosts) + { + $this->hosts = $hosts; + return $this; + } + + /** + * HTTP Basic authentication + * + * @access public + * @param array $users Dictionary of username/password + * @return $this + */ + public function authentication(array $users) + { + $this->users = $users; + return $this; + } + + /** + * Register a new procedure + * + * @access public + * @deprecated Use $server->getProcedureHandler()->withCallback($procedure, $callback) + * @param string $procedure Procedure name + * @param closure $callback Callback + * @return $this + */ + public function register($procedure, Closure $callback) + { + $this->procedureHandler->withCallback($procedure, $callback); + return $this; + } + + /** + * Bind a procedure to a class + * + * @access public + * @deprecated Use $server->getProcedureHandler()->withClassAndMethod($procedure, $class, $method); + * @param string $procedure Procedure name + * @param mixed $class Class name or instance + * @param string $method Procedure name + * @return $this + */ + public function bind($procedure, $class, $method = '') + { + $this->procedureHandler->withClassAndMethod($procedure, $class, $method); + return $this; + } + + /** + * Bind a class instance + * + * @access public + * @deprecated Use $server->getProcedureHandler()->withObject($instance); + * @param mixed $instance Instance name + * @return $this + */ + public function attach($instance) + { + $this->procedureHandler->withObject($instance); + return $this; + } + + /** + * Exception classes that should not be relayed to the client + * + * @access public + * @param Exception|string $exception + * @return $this + */ + public function withLocalException($exception) + { + $this->localExceptions[] = $exception; + return $this; + } + + /** + * Parse incoming requests + * + * @access public + * @return string + */ + public function execute() + { + try { + JsonFormatValidator::validate($this->payload); + HostValidator::validate($this->hosts, $this->getServerVariable('REMOTE_ADDR')); + UserValidator::validate($this->users, $this->getUsername(), $this->getPassword()); + + $this->middlewareHandler + ->withUsername($this->getUsername()) + ->withPassword($this->getPassword()) + ; + + $response = $this->parseRequest(); + + } catch (Exception $e) { + $response = $this->handleExceptions($e); + } + + $this->responseBuilder->sendHeaders(); + return $response; + } + + /** + * Handle exceptions + * + * @access protected + * @param Exception $e + * @return string + * @throws Exception + */ + protected function handleExceptions(Exception $e) + { + foreach ($this->localExceptions as $exception) { + if ($e instanceof $exception) { + throw $e; + } + } + + return $this->responseBuilder->withException($e)->build(); + } + + /** + * Parse incoming request + * + * @access protected + * @return string + */ + protected function parseRequest() + { + if (BatchRequestParser::isBatchRequest($this->payload)) { + return $this->batchRequestParser + ->withPayload($this->payload) + ->withProcedureHandler($this->procedureHandler) + ->withMiddlewareHandler($this->middlewareHandler) + ->withLocalException($this->localExceptions) + ->parse(); + } + + return $this->requestParser + ->withPayload($this->payload) + ->withProcedureHandler($this->procedureHandler) + ->withMiddlewareHandler($this->middlewareHandler) + ->withLocalException($this->localExceptions) + ->parse(); + } + + /** + * Check existence and get value of server variable + * + * @access protected + * @param string $variable + * @return string|null + */ + protected function getServerVariable($variable) + { + return isset($this->serverVariable[$variable]) ? $this->serverVariable[$variable] : null; + } +} diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Validator/HostValidator.php b/vendor/fguillot/json-rpc/src/JsonRPC/Validator/HostValidator.php new file mode 100644 index 0000000000..7f8c0a047d --- /dev/null +++ b/vendor/fguillot/json-rpc/src/JsonRPC/Validator/HostValidator.php @@ -0,0 +1,30 @@ +db = $db; + $this->conditionBuilder = $condition; + } + + /** + * Get object instance + * + * @static + * @access public + * @param Database $db + * @param ConditionBuilder $condition + * @return static + */ + public static function getInstance(Database $db, ConditionBuilder $condition) + { + return new static($db, $condition); + } + + /** + * Set table name + * + * @access public + * @param string $table + * @return $this + */ + public function withTable($table) + { + $this->table = $table; + return $this; + } + + /** + * Set columns name + * + * @access public + * @param string[] $columns + * @return $this + */ + public function withColumns(array $columns) + { + $this->columns = $columns; + return $this; + } +} diff --git a/vendor/fguillot/picodb/lib/PicoDb/Builder/ConditionBuilder.php b/vendor/fguillot/picodb/lib/PicoDb/Builder/ConditionBuilder.php new file mode 100644 index 0000000000..b0465b6eba --- /dev/null +++ b/vendor/fguillot/picodb/lib/PicoDb/Builder/ConditionBuilder.php @@ -0,0 +1,377 @@ +db = $db; + } + + /** + * Build the SQL condition + * + * @access public + * @return string + */ + public function build() + { + return empty($this->conditions) ? '' : ' WHERE '.implode(' AND ', $this->conditions); + } + + /** + * Get condition values + * + * @access public + * @return array + */ + public function getValues() + { + return $this->values; + } + + /** + * Returns true if there is some conditions + * + * @access public + * @return boolean + */ + public function hasCondition() + { + return ! empty($this->conditions); + } + + /** + * Add custom condition + * + * @access public + * @param string $sql + */ + public function addCondition($sql) + { + if ($this->orConditionOffset > 0) { + $this->orConditions[$this->orConditionOffset]->withCondition($sql); + } + else { + $this->conditions[] = $sql; + } + } + + /** + * Start OR condition + * + * @access public + */ + public function beginOr() + { + $this->orConditionOffset++; + $this->orConditions[$this->orConditionOffset] = new OrConditionBuilder(); + } + + /** + * Close OR condition + * + * @access public + */ + public function closeOr() + { + $condition = $this->orConditions[$this->orConditionOffset]->build(); + $this->orConditionOffset--; + + if ($this->orConditionOffset > 0) { + $this->orConditions[$this->orConditionOffset]->withCondition($condition); + } else { + $this->conditions[] = $condition; + } + } + + /** + * Equal condition + * + * @access public + * @param string $column + * @param mixed $value + */ + public function eq($column, $value) + { + $this->addCondition($this->db->escapeIdentifier($column).' = ?'); + $this->values[] = $value; + } + + /** + * Not equal condition + * + * @access public + * @param string $column + * @param mixed $value + */ + public function neq($column, $value) + { + $this->addCondition($this->db->escapeIdentifier($column).' != ?'); + $this->values[] = $value; + } + + /** + * IN condition + * + * @access public + * @param string $column + * @param array $values + */ + public function in($column, array $values) + { + if (! empty($values)) { + $this->addCondition($this->db->escapeIdentifier($column).' IN ('.implode(', ', array_fill(0, count($values), '?')).')'); + $this->values = array_merge($this->values, $values); + } + } + + /** + * IN condition with a subquery + * + * @access public + * @param string $column + * @param Table $subquery + */ + public function inSubquery($column, Table $subquery) + { + $this->addCondition($this->db->escapeIdentifier($column).' IN ('.$subquery->buildSelectQuery().')'); + $this->values = array_merge($this->values, $subquery->getConditionBuilder()->getValues()); + } + + /** + * NOT IN condition + * + * @access public + * @param string $column + * @param array $values + */ + public function notIn($column, array $values) + { + if (! empty($values)) { + $this->addCondition($this->db->escapeIdentifier($column).' NOT IN ('.implode(', ', array_fill(0, count($values), '?')).')'); + $this->values = array_merge($this->values, $values); + } + } + + /** + * NOT IN condition with a subquery + * + * @access public + * @param string $column + * @param Table $subquery + */ + public function notInSubquery($column, Table $subquery) + { + $this->addCondition($this->db->escapeIdentifier($column).' NOT IN ('.$subquery->buildSelectQuery().')'); + $this->values = array_merge($this->values, $subquery->getConditionBuilder()->getValues()); + } + + /** + * LIKE condition + * + * @access public + * @param string $column + * @param mixed $value + */ + public function like($column, $value) + { + $this->addCondition($this->db->escapeIdentifier($column).' '.$this->db->getDriver()->getOperator('LIKE').' ?'); + $this->values[] = $value; + } + + /** + * ILIKE condition + * + * @access public + * @param string $column + * @param mixed $value + */ + public function ilike($column, $value) + { + $this->addCondition($this->db->escapeIdentifier($column).' '.$this->db->getDriver()->getOperator('ILIKE').' ?'); + $this->values[] = $value; + } + + /** + * Greater than condition + * + * @access public + * @param string $column + * @param mixed $value + */ + public function gt($column, $value) + { + $this->addCondition($this->db->escapeIdentifier($column).' > ?'); + $this->values[] = $value; + } + + /** + * Greater than condition with subquery + * + * @access public + * @param string $column + * @param Table $subquery + */ + public function gtSubquery($column, Table $subquery) + { + $this->addCondition($this->db->escapeIdentifier($column).' > ('.$subquery->buildSelectQuery().')'); + $this->values = array_merge($this->values, $subquery->getConditionBuilder()->getValues()); + } + + /** + * Lower than condition + * + * @access public + * @param string $column + * @param mixed $value + */ + public function lt($column, $value) + { + $this->addCondition($this->db->escapeIdentifier($column).' < ?'); + $this->values[] = $value; + } + + /** + * Lower than condition with subquery + * + * @access public + * @param string $column + * @param Table $subquery + */ + public function ltSubquery($column, Table $subquery) + { + $this->addCondition($this->db->escapeIdentifier($column).' < ('.$subquery->buildSelectQuery().')'); + $this->values = array_merge($this->values, $subquery->getConditionBuilder()->getValues()); + } + + /** + * Greater than or equals condition + * + * @access public + * @param string $column + * @param mixed $value + */ + public function gte($column, $value) + { + $this->addCondition($this->db->escapeIdentifier($column).' >= ?'); + $this->values[] = $value; + } + + /** + * Greater than or equal condition with subquery + * + * @access public + * @param string $column + * @param Table $subquery + */ + public function gteSubquery($column, Table $subquery) + { + $this->addCondition($this->db->escapeIdentifier($column).' >= ('.$subquery->buildSelectQuery().')'); + $this->values = array_merge($this->values, $subquery->getConditionBuilder()->getValues()); + } + + /** + * Lower than or equals condition + * + * @access public + * @param string $column + * @param mixed $value + */ + public function lte($column, $value) + { + $this->addCondition($this->db->escapeIdentifier($column).' <= ?'); + $this->values[] = $value; + } + + /** + * Lower than or equal condition with subquery + * + * @access public + * @param string $column + * @param Table $subquery + */ + public function lteSubquery($column, Table $subquery) + { + $this->addCondition($this->db->escapeIdentifier($column).' <= ('.$subquery->buildSelectQuery().')'); + $this->values = array_merge($this->values, $subquery->getConditionBuilder()->getValues()); + } + + /** + * IS NULL condition + * + * @access public + * @param string $column + */ + public function isNull($column) + { + $this->addCondition($this->db->escapeIdentifier($column).' IS NULL'); + } + + /** + * IS NOT NULL condition + * + * @access public + * @param string $column + */ + public function notNull($column) + { + $this->addCondition($this->db->escapeIdentifier($column).' IS NOT NULL'); + } +} diff --git a/vendor/fguillot/picodb/lib/PicoDb/Builder/InsertBuilder.php b/vendor/fguillot/picodb/lib/PicoDb/Builder/InsertBuilder.php new file mode 100644 index 0000000000..9d06c4053d --- /dev/null +++ b/vendor/fguillot/picodb/lib/PicoDb/Builder/InsertBuilder.php @@ -0,0 +1,36 @@ +columns as $column) { + $columns[] = $this->db->escapeIdentifier($column); + $placeholders[] = ':'.$column; + } + + return sprintf( + 'INSERT INTO %s (%s) VALUES (%s)', + $this->db->escapeIdentifier($this->table), + implode(', ', $columns), + implode(', ', $placeholders) + ); + } +} diff --git a/vendor/fguillot/picodb/lib/PicoDb/Builder/OrConditionBuilder.php b/vendor/fguillot/picodb/lib/PicoDb/Builder/OrConditionBuilder.php new file mode 100644 index 0000000000..0defeaf47d --- /dev/null +++ b/vendor/fguillot/picodb/lib/PicoDb/Builder/OrConditionBuilder.php @@ -0,0 +1,43 @@ +conditions[] = $condition; + return $this; + } + + /** + * Build SQL + * + * @access public + * @return string + */ + public function build() + { + return '('.implode(' OR ', $this->conditions).')'; + } +} diff --git a/vendor/fguillot/picodb/lib/PicoDb/Builder/UpdateBuilder.php b/vendor/fguillot/picodb/lib/PicoDb/Builder/UpdateBuilder.php new file mode 100644 index 0000000000..300ea9b0fa --- /dev/null +++ b/vendor/fguillot/picodb/lib/PicoDb/Builder/UpdateBuilder.php @@ -0,0 +1,56 @@ +sumColumns = $columns; + return $this; + } + + /** + * Build SQL + * + * @access public + * @return string + */ + public function build() + { + $columns = array(); + + foreach ($this->columns as $column) { + $columns[] = $this->db->escapeIdentifier($column).'=?'; + } + + foreach ($this->sumColumns as $column) { + $columns[] = $this->db->escapeIdentifier($column).'='.$this->db->escapeIdentifier($column).' + ?'; + } + + return sprintf( + 'UPDATE %s SET %s %s', + $this->db->escapeIdentifier($this->table), + implode(', ', $columns), + $this->conditionBuilder->build() + ); + } +} diff --git a/vendor/fguillot/picodb/lib/PicoDb/Database.php b/vendor/fguillot/picodb/lib/PicoDb/Database.php new file mode 100644 index 0000000000..22c9d2fb61 --- /dev/null +++ b/vendor/fguillot/picodb/lib/PicoDb/Database.php @@ -0,0 +1,370 @@ +driver = DriverFactory::getDriver($settings); + $this->statementHandler = new StatementHandler($this); + } + + /** + * Destructor + * + * @access public + */ + public function __destruct() + { + $this->closeConnection(); + } + + /** + * Register a new database instance + * + * @static + * @access public + * @param string $name Instance name + * @param Closure $callback Callback + */ + public static function setInstance($name, Closure $callback) + { + self::$instances[$name] = $callback; + } + + /** + * Get a database instance + * + * @static + * @access public + * @param string $name Instance name + * @return Database + */ + public static function getInstance($name) + { + if (! isset(self::$instances[$name])) { + throw new LogicException('No database instance created with that name'); + } + + if (is_callable(self::$instances[$name])) { + self::$instances[$name] = call_user_func(self::$instances[$name]); + } + + return self::$instances[$name]; + } + + /** + * Add a log message + * + * @access public + * @param mixed $message + * @return Database + */ + public function setLogMessage($message) + { + $this->logs[] = is_array($message) ? var_export($message, true) : $message; + return $this; + } + + /** + * Add many log messages + * + * @access public + * @param array $messages + * @return Database + */ + public function setLogMessages(array $messages) + { + foreach ($messages as $message) { + $this->setLogMessage($message); + } + + return $this; + } + + /** + * Get all queries logs + * + * @access public + * @return array + */ + public function getLogMessages() + { + return $this->logs; + } + + /** + * Get the PDO connection + * + * @access public + * @return \PDO + */ + public function getConnection() + { + return $this->driver->getConnection(); + } + + /** + * Get the Driver instance + * + * @access public + * @return Mssql|Sqlite|Postgres|Mysql + */ + public function getDriver() + { + return $this->driver; + } + + /** + * Get the last inserted id + * + * @access public + * @return integer + */ + public function getLastId() + { + return (int) $this->driver->getLastId(); + } + + /** + * Get statement object + * + * @access public + * @return StatementHandler + */ + public function getStatementHandler() + { + return $this->statementHandler; + } + + /** + * Release the PDO connection + * + * @access public + */ + public function closeConnection() + { + $this->driver->closeConnection(); + } + + /** + * Escape an identifier (column, table name...) + * + * @access public + * @param string $value Value + * @param string $table Table name + * @return string + */ + public function escapeIdentifier($value, $table = '') + { + // Do not escape custom query + if (strpos($value, '.') !== false || strpos($value, ' ') !== false) { + return $value; + } + + if (! empty($table)) { + return $this->driver->escape($table).'.'.$this->driver->escape($value); + } + + return $this->driver->escape($value); + } + + /** + * Escape an identifier list + * + * @access public + * @param array $identifiers List of identifiers + * @param string $table Table name + * @return string[] + */ + public function escapeIdentifierList(array $identifiers, $table = '') + { + foreach ($identifiers as $key => $value) { + $identifiers[$key] = $this->escapeIdentifier($value, $table); + } + + return $identifiers; + } + + /** + * Execute a prepared statement + * + * Note: returns false on duplicate keys instead of SQLException + * + * @access public + * @param string $sql SQL query + * @param array $values Values + * @return \PDOStatement|false + */ + public function execute($sql, array $values = array()) + { + return $this->statementHandler + ->withSql($sql) + ->withPositionalParams($values) + ->execute(); + } + + /** + * Run a transaction + * + * @access public + * @param Closure $callback Callback + * @return mixed + */ + public function transaction(Closure $callback) + { + try { + + $this->startTransaction(); + $result = $callback($this); + $this->closeTransaction(); + + return $result === null ? true : $result; + } catch (PDOException $e) { + return $this->statementHandler->handleSqlError($e); + } + } + + /** + * Begin a transaction + * + * @access public + */ + public function startTransaction() + { + if (! $this->getConnection()->inTransaction()) { + $this->getConnection()->beginTransaction(); + } + } + + /** + * Commit a transaction + * + * @access public + */ + public function closeTransaction() + { + if ($this->getConnection()->inTransaction()) { + $this->getConnection()->commit(); + } + } + + /** + * Rollback a transaction + * + * @access public + */ + public function cancelTransaction() + { + if ($this->getConnection()->inTransaction()) { + $this->getConnection()->rollBack(); + } + } + + /** + * Get a table object + * + * @access public + * @param string $table + * @return Table + */ + public function table($table) + { + return new Table($this, $table); + } + + /** + * Get a hashtable object + * + * @access public + * @param string $table + * @return Hashtable + */ + public function hashtable($table) + { + return new Hashtable($this, $table); + } + + /** + * Get a LOB object + * + * @access public + * @param string $table + * @return LargeObject + */ + public function largeObject($table) + { + return new LargeObject($this, $table); + } + + /** + * Get a schema object + * + * @access public + * @param string $namespace + * @return Schema + */ + public function schema($namespace = null) + { + $schema = new Schema($this); + + if ($namespace !== null) { + $schema->setNamespace($namespace); + } + + return $schema; + } +} diff --git a/vendor/fguillot/picodb/lib/PicoDb/Driver/Base.php b/vendor/fguillot/picodb/lib/PicoDb/Driver/Base.php new file mode 100644 index 0000000000..790cd6236c --- /dev/null +++ b/vendor/fguillot/picodb/lib/PicoDb/Driver/Base.php @@ -0,0 +1,234 @@ +requiredAttributes as $attribute) { + if (! isset($settings[$attribute])) { + throw new LogicException('This configuration parameter is missing: "'.$attribute.'"'); + } + } + + $this->createConnection($settings); + $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + } + + /** + * Get the PDO connection + * + * @access public + * @return PDO + */ + public function getConnection() + { + return $this->pdo; + } + + /** + * Release the PDO connection + * + * @access public + */ + public function closeConnection() + { + $this->pdo = null; + } + + /** + * Upsert for a key/value variable + * + * @access public + * @param string $table + * @param string $keyColumn + * @param string $valueColumn + * @param array $dictionary + * @return bool False on failure + */ + public function upsert($table, $keyColumn, $valueColumn, array $dictionary) + { + try { + $this->pdo->beginTransaction(); + + foreach ($dictionary as $key => $value) { + + $rq = $this->pdo->prepare('SELECT 1 FROM '.$this->escape($table).' WHERE '.$this->escape($keyColumn).'=?'); + $rq->execute(array($key)); + + if ($rq->fetchColumn()) { + $rq = $this->pdo->prepare('UPDATE '.$this->escape($table).' SET '.$this->escape($valueColumn).'=? WHERE '.$this->escape($keyColumn).'=?'); + $rq->execute(array($value, $key)); + } + else { + $rq = $this->pdo->prepare('INSERT INTO '.$this->escape($table).' ('.$this->escape($keyColumn).', '.$this->escape($valueColumn).') VALUES (?, ?)'); + $rq->execute(array($key, $value)); + } + } + + $this->pdo->commit(); + + return true; + } + catch (PDOException $e) { + $this->pdo->rollBack(); + return false; + } + } + + /** + * Run EXPLAIN command + * + * @access public + * @param string $sql + * @param array $values + * @return array + */ + public function explain($sql, array $values) + { + return $this->getConnection()->query('EXPLAIN '.$this->getSqlFromPreparedStatement($sql, $values))->fetchAll(PDO::FETCH_ASSOC); + } + + /** + * Replace placeholder with values in prepared statement + * + * @access protected + * @param string $sql + * @param array $values + * @return string + */ + protected function getSqlFromPreparedStatement($sql, array $values) + { + foreach ($values as $value) { + $sql = substr_replace($sql, "'$value'", strpos($sql, '?'), 1); + } + + return $sql; + } + + /** + * Get database version + * + * @access public + * @return array + */ + public function getDatabaseVersion() + { + return $this->getConnection()->query('SELECT VERSION()')->fetchColumn(); + } +} diff --git a/vendor/fguillot/picodb/lib/PicoDb/Driver/Mssql.php b/vendor/fguillot/picodb/lib/PicoDb/Driver/Mssql.php new file mode 100644 index 0000000000..83e75af2db --- /dev/null +++ b/vendor/fguillot/picodb/lib/PicoDb/Driver/Mssql.php @@ -0,0 +1,178 @@ + + */ +class Mssql extends Base +{ + /** + * List of required settings options + * + * @access protected + * @var array + */ + protected $requiredAttributes = array( + 'hostname', + 'username', + 'password', + 'database', + ); + + /** + * Table to store the schema version + * + * @access private + * @var array + */ + private $schemaTable = 'schema_version'; + + /** + * Create a new PDO connection + * + * @access public + * @param array $settings + */ + public function createConnection(array $settings) + { + $dsn = 'sqlsrv:Server=' . $settings['hostname'] . ';Database=' . $settings['database']; + + if (! empty($settings['port'])) { + $dsn .= ';port=' . $settings['port']; + } + + $this->pdo = new PDO($dsn, $settings['username'], $settings['password']); + + if (isset($settings['schema_table'])) { + $this->schemaTable = $settings['schema_table']; + } + } + + /** + * Enable foreign keys + * + * @access public + */ + public function enableForeignKeys() + { + $this->pdo->exec('EXEC sp_MSforeachtable @command1="ALTER TABLE ? CHECK CONSTRAINT ALL"; GO;'); + } + + /** + * Disable foreign keys + * + * @access public + */ + public function disableForeignKeys() + { + $this->pdo->exec('EXEC sp_MSforeachtable @command1="ALTER TABLE ? NOCHECK CONSTRAINT ALL"; GO;'); + } + + /** + * Return true if the error code is a duplicate key + * + * @access public + * @param integer $code + * @return boolean + */ + public function isDuplicateKeyError($code) + { + return $code == 2601; + } + + /** + * Escape identifier + * + * https://msdn.microsoft.com/en-us/library/ms175874.aspx + * + * @access public + * @param string $identifier + * @return string + */ + public function escape($identifier) + { + return '['.$identifier.']'; + } + + /** + * Get non standard operator + * + * @access public + * @param string $operator + * @return string + */ + public function getOperator($operator) + { + if ($operator === 'LIKE' || $operator === 'ILIKE') { + return 'LIKE'; + } + + return ''; + } + + /** + * Get last inserted id + * + * @access public + * @return integer + */ + public function getLastId() + { + return $this->pdo->lastInsertId(); + } + + /** + * Get current schema version + * + * @access public + * @return integer + */ + public function getSchemaVersion() + { + $this->pdo->exec("CREATE TABLE IF NOT EXISTS [".$this->schemaTable."] ([version] INT DEFAULT '0')"); + + $rq = $this->pdo->prepare('SELECT [version] FROM ['.$this->schemaTable.']'); + $rq->execute(); + $result = $rq->fetchColumn(); + + if ($result !== false) { + return (int) $result; + } + else { + $this->pdo->exec('INSERT INTO ['.$this->schemaTable.'] VALUES(0)'); + } + + return 0; + } + + /** + * Set current schema version + * + * @access public + * @param integer $version + */ + public function setSchemaVersion($version) + { + $rq = $this->pdo->prepare('UPDATE ['.$this->schemaTable.'] SET [version]=?'); + $rq->execute(array($version)); + } + + /** + * Run EXPLAIN command + * + * @param string $sql + * @param array $values + * @return array + */ + public function explain($sql, array $values) + { + $this->getConnection()->exec('SET SHOWPLAN_ALL ON'); + return $this->getConnection()->query($this->getSqlFromPreparedStatement($sql, $values))->fetchAll(PDO::FETCH_ASSOC); + } +} diff --git a/vendor/fguillot/picodb/lib/PicoDb/Driver/Mysql.php b/vendor/fguillot/picodb/lib/PicoDb/Driver/Mysql.php new file mode 100644 index 0000000000..4f3ca64e34 --- /dev/null +++ b/vendor/fguillot/picodb/lib/PicoDb/Driver/Mysql.php @@ -0,0 +1,252 @@ +pdo = new PDO( + $this->buildDsn($settings), + $settings['username'], + $settings['password'], + $this->buildOptions($settings) + ); + + if (isset($settings['schema_table'])) { + $this->schemaTable = $settings['schema_table']; + } + } + + /** + * Build connection DSN + * + * @access protected + * @param array $settings + * @return string + */ + protected function buildDsn(array $settings) + { + $charset = empty($settings['charset']) ? 'utf8' : $settings['charset']; + $dsn = 'mysql:host='.$settings['hostname'].';dbname='.$settings['database'].';charset='.$charset; + + if (! empty($settings['port'])) { + $dsn .= ';port='.$settings['port']; + } + + return $dsn; + } + + /** + * Build connection options + * + * @access protected + * @param array $settings + * @return array + */ + protected function buildOptions(array $settings) + { + $options = array( + PDO::MYSQL_ATTR_INIT_COMMAND => 'SET sql_mode = STRICT_ALL_TABLES', + ); + + if (! empty($settings['ssl_key'])) { + $options[PDO::MYSQL_ATTR_SSL_KEY] = $settings['ssl_key']; + } + + if (! empty($settings['ssl_cert'])) { + $options[PDO::MYSQL_ATTR_SSL_CERT] = $settings['ssl_cert']; + } + + if (! empty($settings['ssl_ca'])) { + $options[PDO::MYSQL_ATTR_SSL_CA] = $settings['ssl_ca']; + } + + return $options; + } + + /** + * Enable foreign keys + * + * @access public + */ + public function enableForeignKeys() + { + $this->pdo->exec('SET FOREIGN_KEY_CHECKS=1'); + } + + /** + * Disable foreign keys + * + * @access public + */ + public function disableForeignKeys() + { + $this->pdo->exec('SET FOREIGN_KEY_CHECKS=0'); + } + + /** + * Return true if the error code is a duplicate key + * + * @access public + * @param integer $code + * @return boolean + */ + public function isDuplicateKeyError($code) + { + return $code == 23000; + } + + /** + * Escape identifier + * + * @access public + * @param string $identifier + * @return string + */ + public function escape($identifier) + { + return '`'.$identifier.'`'; + } + + /** + * Get non standard operator + * + * @access public + * @param string $operator + * @return string + */ + public function getOperator($operator) + { + if ($operator === 'LIKE') { + return 'LIKE BINARY'; + } + else if ($operator === 'ILIKE') { + return 'LIKE'; + } + + return ''; + } + + /** + * Get last inserted id + * + * @access public + * @return integer + */ + public function getLastId() + { + return $this->pdo->lastInsertId(); + } + + /** + * Get current schema version + * + * @access public + * @return integer + */ + public function getSchemaVersion() + { + $this->pdo->exec("CREATE TABLE IF NOT EXISTS `".$this->schemaTable."` (`version` INT DEFAULT '0') ENGINE=InnoDB CHARSET=utf8"); + + $rq = $this->pdo->prepare('SELECT `version` FROM `'.$this->schemaTable.'`'); + $rq->execute(); + $result = $rq->fetchColumn(); + + if ($result !== false) { + return (int) $result; + } + else { + $this->pdo->exec('INSERT INTO `'.$this->schemaTable.'` VALUES(0)'); + } + + return 0; + } + + /** + * Set current schema version + * + * @access public + * @param integer $version + */ + public function setSchemaVersion($version) + { + $rq = $this->pdo->prepare('UPDATE `'.$this->schemaTable.'` SET `version`=?'); + $rq->execute(array($version)); + } + + /** + * Upsert for a key/value variable + * + * @access public + * @param string $table + * @param string $keyColumn + * @param string $valueColumn + * @param array $dictionary + * @return bool False on failure + */ + public function upsert($table, $keyColumn, $valueColumn, array $dictionary) + { + try { + + $sql = sprintf( + 'REPLACE INTO %s (%s, %s) VALUES %s', + $this->escape($table), + $this->escape($keyColumn), + $this->escape($valueColumn), + implode(', ', array_fill(0, count($dictionary), '(?, ?)')) + ); + + $values = array(); + + foreach ($dictionary as $key => $value) { + $values[] = $key; + $values[] = $value; + } + + $rq = $this->pdo->prepare($sql); + $rq->execute($values); + + return true; + } + catch (PDOException $e) { + return false; + } + } +} diff --git a/vendor/fguillot/picodb/lib/PicoDb/Driver/Postgres.php b/vendor/fguillot/picodb/lib/PicoDb/Driver/Postgres.php new file mode 100644 index 0000000000..a494cdca81 --- /dev/null +++ b/vendor/fguillot/picodb/lib/PicoDb/Driver/Postgres.php @@ -0,0 +1,196 @@ +pdo = new PDO($dsn, $settings['username'], $settings['password']); + + if (isset($settings['schema_table'])) { + $this->schemaTable = $settings['schema_table']; + } + } + + /** + * Enable foreign keys + * + * @access public + */ + public function enableForeignKeys() + { + } + + /** + * Disable foreign keys + * + * @access public + */ + public function disableForeignKeys() + { + } + + /** + * Return true if the error code is a duplicate key + * + * @access public + * @param integer $code + * @return boolean + */ + public function isDuplicateKeyError($code) + { + return $code == 23505 || $code == 23503; + } + + /** + * Escape identifier + * + * @access public + * @param string $identifier + * @return string + */ + public function escape($identifier) + { + return '"'.$identifier.'"'; + } + + /** + * Get non standard operator + * + * @access public + * @param string $operator + * @return string + */ + public function getOperator($operator) + { + if ($operator === 'LIKE') { + return 'LIKE'; + } + else if ($operator === 'ILIKE') { + return 'ILIKE'; + } + + return ''; + } + + /** + * Get last inserted id + * + * @access public + * @return integer + */ + public function getLastId() + { + try { + $rq = $this->pdo->prepare('SELECT LASTVAL()'); + $rq->execute(); + + return $rq->fetchColumn(); + } + catch (PDOException $e) { + return 0; + } + } + + /** + * Get current schema version + * + * @access public + * @return integer + */ + public function getSchemaVersion() + { + $this->pdo->exec("CREATE TABLE IF NOT EXISTS ".$this->schemaTable." (version INTEGER DEFAULT 0)"); + + $rq = $this->pdo->prepare('SELECT "version" FROM "'.$this->schemaTable.'"'); + $rq->execute(); + $result = $rq->fetchColumn(); + + if ($result !== false) { + return (int) $result; + } + else { + $this->pdo->exec('INSERT INTO '.$this->schemaTable.' VALUES(0)'); + } + + return 0; + } + + /** + * Set current schema version + * + * @access public + * @param integer $version + */ + public function setSchemaVersion($version) + { + $rq = $this->pdo->prepare('UPDATE '.$this->schemaTable.' SET version=?'); + $rq->execute(array($version)); + } + + /** + * Run EXPLAIN command + * + * @param string $sql + * @param array $values + * @return array + */ + public function explain($sql, array $values) + { + return $this->getConnection()->query('EXPLAIN (FORMAT YAML) '.$this->getSqlFromPreparedStatement($sql, $values))->fetchAll(PDO::FETCH_ASSOC); + } + + /** + * Get database version + * + * @access public + * @return array + */ + public function getDatabaseVersion() + { + return $this->getConnection()->query('SHOW server_version')->fetchColumn(); + } +} diff --git a/vendor/fguillot/picodb/lib/PicoDb/Driver/Sqlite.php b/vendor/fguillot/picodb/lib/PicoDb/Driver/Sqlite.php new file mode 100644 index 0000000000..ea39f00754 --- /dev/null +++ b/vendor/fguillot/picodb/lib/PicoDb/Driver/Sqlite.php @@ -0,0 +1,193 @@ +pdo = new PDO('sqlite:'.$settings['filename']); + $this->enableForeignKeys(); + } + + /** + * Enable foreign keys + * + * @access public + */ + public function enableForeignKeys() + { + $this->pdo->exec('PRAGMA foreign_keys = ON'); + } + + /** + * Disable foreign keys + * + * @access public + */ + public function disableForeignKeys() + { + $this->pdo->exec('PRAGMA foreign_keys = OFF'); + } + + /** + * Return true if the error code is a duplicate key + * + * @access public + * @param integer $code + * @return boolean + */ + public function isDuplicateKeyError($code) + { + return $code == 23000; + } + + /** + * Escape identifier + * + * @access public + * @param string $identifier + * @return string + */ + public function escape($identifier) + { + return '"'.$identifier.'"'; + } + + /** + * Get non standard operator + * + * @access public + * @param string $operator + * @return string + */ + public function getOperator($operator) + { + if ($operator === 'LIKE' || $operator === 'ILIKE') { + return 'LIKE'; + } + + return ''; + } + + /** + * Get last inserted id + * + * @access public + * @return integer + */ + public function getLastId() + { + return $this->pdo->lastInsertId(); + } + + /** + * Get current schema version + * + * @access public + * @return integer + */ + public function getSchemaVersion() + { + $rq = $this->pdo->prepare('PRAGMA user_version'); + $rq->execute(); + + return (int) $rq->fetchColumn(); + } + + /** + * Set current schema version + * + * @access public + * @param integer $version + */ + public function setSchemaVersion($version) + { + $this->pdo->exec('PRAGMA user_version='.$version); + } + + /** + * Upsert for a key/value variable + * + * @access public + * @param string $table + * @param string $keyColumn + * @param string $valueColumn + * @param array $dictionary + * @return bool False on failure + */ + public function upsert($table, $keyColumn, $valueColumn, array $dictionary) + { + try { + $this->pdo->beginTransaction(); + + foreach ($dictionary as $key => $value) { + + $sql = sprintf( + 'INSERT OR REPLACE INTO %s (%s, %s) VALUES (?, ?)', + $this->escape($table), + $this->escape($keyColumn), + $this->escape($valueColumn) + ); + + $rq = $this->pdo->prepare($sql); + $rq->execute(array($key, $value)); + } + + $this->pdo->commit(); + + return true; + } + catch (PDOException $e) { + $this->pdo->rollBack(); + return false; + } + } + + /** + * Run EXPLAIN command + * + * @access public + * @param string $sql + * @param array $values + * @return array + */ + public function explain($sql, array $values) + { + return $this->getConnection()->query('EXPLAIN QUERY PLAN '.$this->getSqlFromPreparedStatement($sql, $values))->fetchAll(PDO::FETCH_ASSOC); + } + + /** + * Get database version + * + * @access public + * @return array + */ + public function getDatabaseVersion() + { + return $this->getConnection()->query('SELECT sqlite_version()')->fetchColumn(); + } +} diff --git a/vendor/fguillot/picodb/lib/PicoDb/DriverFactory.php b/vendor/fguillot/picodb/lib/PicoDb/DriverFactory.php new file mode 100644 index 0000000000..13151ba716 --- /dev/null +++ b/vendor/fguillot/picodb/lib/PicoDb/DriverFactory.php @@ -0,0 +1,45 @@ +keyColumn = $column; + return $this; + } + + /** + * Set the value column + * + * @access public + * @param string $column + * @return $this + */ + public function columnValue($column) + { + $this->valueColumn = $column; + return $this; + } + + /** + * Insert or update + * + * @access public + * @param array $hashmap + * @return boolean + */ + public function put(array $hashmap) + { + return $this->db->getDriver()->upsert($this->getName(), $this->keyColumn, $this->valueColumn, $hashmap); + } + + /** + * Hashmap result [ [column1 => column2], [], ...] + * + * @access public + * @return array + */ + public function get() + { + $hashmap = array(); + + // setup where condition + if (func_num_args() > 0) { + $this->in($this->keyColumn, func_get_args()); + } + + // setup to select columns in case that there are more than two + $this->columns($this->keyColumn, $this->valueColumn); + + $rq = $this->db->execute($this->buildSelectQuery(), $this->conditionBuilder->getValues()); + $rows = $rq->fetchAll(PDO::FETCH_NUM); + + foreach ($rows as $row) { + $hashmap[$row[0]] = $row[1]; + } + + return $hashmap; + } + + /** + * Shortcut method to get a hashmap result + * + * @access public + * @param string $key Key column + * @param string $value Value column + * @return array + */ + public function getAll($key, $value) + { + $this->keyColumn = $key; + $this->valueColumn = $value; + return $this->get(); + } +} diff --git a/vendor/fguillot/picodb/lib/PicoDb/LargeObject.php b/vendor/fguillot/picodb/lib/PicoDb/LargeObject.php new file mode 100644 index 0000000000..ba5e3b9256 --- /dev/null +++ b/vendor/fguillot/picodb/lib/PicoDb/LargeObject.php @@ -0,0 +1,167 @@ +limit(1); + $this->columns($column); + + $rq = $this->db->getStatementHandler() + ->withSql($this->buildSelectQuery()) + ->withPositionalParams($this->conditionBuilder->getValues()) + ->execute(); + + $rq->bindColumn($column, $fd, PDO::PARAM_LOB); + $rq->fetch(PDO::FETCH_BOUND); + + return $fd; + } + + /** + * Fetch large object as string + * + * @access public + * @param string $column + * @return string + */ + public function findOneColumnAsString($column) + { + $fd = $this->findOneColumnAsStream($column); + + if (is_string($fd)) { + return $fd; + } + + return stream_get_contents($fd); + } + + /** + * Insert large object from stream + * + * @access public + * @param string $blobColumn + * @param resource|string $blobDescriptor + * @param array $data + * @return bool + */ + public function insertFromStream($blobColumn, &$blobDescriptor, array $data = array()) + { + $columns = array_merge(array($blobColumn), array_keys($data)); + $this->db->startTransaction(); + + $result = $this->db->getStatementHandler() + ->withSql(InsertBuilder::getInstance($this->db, $this->conditionBuilder) + ->withTable($this->name) + ->withColumns($columns) + ->build() + ) + ->withNamedParams($data) + ->withLobParam($blobColumn, $blobDescriptor) + ->execute(); + + $this->db->closeTransaction(); + + return $result !== false; + } + + /** + * Insert large object from file + * + * @access public + * @param string $blobColumn + * @param string $filename + * @param array $data + * @return bool + */ + public function insertFromFile($blobColumn, $filename, array $data = array()) + { + $fp = fopen($filename, 'rb'); + $result = $this->insertFromStream($blobColumn, $fp, $data); + fclose($fp); + return $result; + } + + /** + * Insert large object from string + * + * @access public + * @param string $blobColumn + * @param string $blobData + * @param array $data + * @return bool + */ + public function insertFromString($blobColumn, &$blobData, array $data = array()) + { + return $this->insertFromStream($blobColumn, $blobData, $data); + } + + /** + * Update large object from stream + * + * @access public + * @param string $blobColumn + * @param resource $blobDescriptor + * @param array $data + * @return bool + */ + public function updateFromStream($blobColumn, &$blobDescriptor, array $data = array()) + { + $values = array_merge(array_values($data), $this->conditionBuilder->getValues()); + $columns = array_merge(array($blobColumn), array_keys($data)); + + $this->db->startTransaction(); + + $result = $this->db->getStatementHandler() + ->withSql(UpdateBuilder::getInstance($this->db, $this->conditionBuilder) + ->withTable($this->name) + ->withColumns($columns) + ->build() + ) + ->withPositionalParams($values) + ->withLobParam($blobColumn, $blobDescriptor) + ->execute(); + + $this->db->closeTransaction(); + + return $result !== false; + } + + /** + * Update large object from file + * + * @access public + * @param string $blobColumn + * @param string $filename + * @param array $data + * @return bool + */ + public function updateFromFile($blobColumn, $filename, array $data = array()) + { + $fp = fopen($filename, 'r'); + $result = $this->updateFromStream($blobColumn, $fp, $data); + fclose($fp); + return $result; + } +} diff --git a/vendor/fguillot/picodb/lib/PicoDb/SQLException.php b/vendor/fguillot/picodb/lib/PicoDb/SQLException.php new file mode 100644 index 0000000000..7e570834ff --- /dev/null +++ b/vendor/fguillot/picodb/lib/PicoDb/SQLException.php @@ -0,0 +1,15 @@ +db = $db; + } + + /** + * Set another namespace + * + * @access public + * @param string $namespace + * @return Schema + */ + public function setNamespace($namespace) + { + $this->namespace = $namespace; + return $this; + } + + /** + * Get schema namespace + * + * @access public + * @return string + */ + public function getNamespace() + { + return $this->namespace; + } + + /** + * Check the schema version and run the migrations + * + * @access public + * @param integer $last_version + * @return boolean + */ + public function check($last_version = 1) + { + $current_version = $this->db->getDriver()->getSchemaVersion(); + + if ($current_version < $last_version) { + return $this->migrateTo($current_version, $last_version); + } + + return true; + } + + /** + * Migrate the schema to one version to another + * + * @access public + * @param integer $current_version + * @param integer $next_version + * @return boolean + */ + public function migrateTo($current_version, $next_version) + { + try { + for ($i = $current_version + 1; $i <= $next_version; $i++) { + $this->db->startTransaction(); + $this->db->getDriver()->disableForeignKeys(); + + $function_name = $this->getNamespace().'\version_'.$i; + + if (function_exists($function_name)) { + $this->db->setLogMessage('Running migration '.$function_name); + call_user_func($function_name, $this->db->getConnection()); + } + + $this->db->getDriver()->setSchemaVersion($i); + $this->db->getDriver()->enableForeignKeys(); + $this->db->closeTransaction(); + } + } catch (PDOException $e) { + $this->db->setLogMessage($e->getMessage()); + $this->db->cancelTransaction(); + $this->db->getDriver()->enableForeignKeys(); + return false; + } + + return true; + } +} diff --git a/vendor/fguillot/picodb/lib/PicoDb/StatementHandler.php b/vendor/fguillot/picodb/lib/PicoDb/StatementHandler.php new file mode 100644 index 0000000000..a7021b3642 --- /dev/null +++ b/vendor/fguillot/picodb/lib/PicoDb/StatementHandler.php @@ -0,0 +1,353 @@ +db = $db; + } + + /** + * Enable query logging + * + * @access public + * @return $this + */ + public function withLogging() + { + $this->logQueries = true; + return $this; + } + + /** + * Record query execution time + * + * @access public + * @return $this + */ + public function withStopWatch() + { + $this->stopwatch = true; + return $this; + } + + /** + * Execute explain command on query + * + * @access public + * @return $this + */ + public function withExplain() + { + $this->explain = true; + return $this; + } + + /** + * Set SQL query + * + * @access public + * @param string $sql + * @return $this + */ + public function withSql($sql) + { + $this->sql = $sql; + return $this; + } + + /** + * Set positional parameters + * + * @access public + * @param array $params + * @return $this + */ + public function withPositionalParams(array $params) + { + $this->positionalParams = $params; + return $this; + } + + /** + * Set named parameters + * + * @access public + * @param array $params + * @return $this + */ + public function withNamedParams(array $params) + { + $this->namedParams = $params; + $this->useNamedParams = true; + return $this; + } + + /** + * Bind large object parameter + * + * @access public + * @param $name + * @param $fp + * @return $this + */ + public function withLobParam($name, &$fp) + { + $this->lobParams[$name] =& $fp; + return $this; + } + + /** + * Get number of queries executed + * + * @access public + * @return int + */ + public function getNbQueries() + { + return $this->nbQueries; + } + + /** + * Execute a prepared statement + * + * Note: returns false on duplicate keys instead of SQLException + * + * @access public + * @return PDOStatement|false + */ + public function execute() + { + try { + $this->beforeExecute(); + + $pdoStatement = $this->db->getConnection()->prepare($this->sql); + $this->bindParams($pdoStatement); + $pdoStatement->execute(); + + $this->afterExecute(); + return $pdoStatement; + } catch (PDOException $e) { + return $this->handleSqlError($e); + } + } + + /** + * Bind parameters to PDOStatement + * + * @access protected + * @param PDOStatement $pdoStatement + */ + protected function bindParams(PDOStatement $pdoStatement) + { + $i = 1; + + foreach ($this->lobParams as $name => $variable) { + if (! $this->useNamedParams) { + $parameter = $i; + $i++; + } else { + $parameter = $name; + } + + $pdoStatement->bindParam($parameter, $variable, PDO::PARAM_LOB); + } + + foreach ($this->positionalParams as $value) { + $pdoStatement->bindValue($i, $value, PDO::PARAM_STR); + $i++; + } + + foreach ($this->namedParams as $name => $value) { + $pdoStatement->bindValue($name, $value, PDO::PARAM_STR); + } + } + + /** + * Method executed before query execution + * + * @access protected + */ + protected function beforeExecute() + { + if ($this->logQueries) { + $this->db->setLogMessage($this->sql); + } + + if ($this->stopwatch) { + $this->startTime = microtime(true); + } + } + + /** + * Method executed after query execution + * + * @access protected + */ + protected function afterExecute() + { + if ($this->stopwatch) { + $duration = microtime(true) - $this->startTime; + $this->executionTime += $duration; + $this->db->setLogMessage('query_duration='.$duration); + $this->db->setLogMessage('total_execution_time='.$this->executionTime); + } + + if ($this->explain) { + $this->db->setLogMessages($this->db->getDriver()->explain($this->sql, $this->positionalParams)); + } + + $this->nbQueries++; + $this->cleanup(); + } + + /** + * Reset internal properties after execution + * The same object instance is used + * + * @access protected + */ + protected function cleanup() + { + $this->sql = ''; + $this->useNamedParams = false; + $this->positionalParams = array(); + $this->namedParams = array(); + $this->lobParams = array(); + } + + /** + * Handle PDOException + * + * @access public + * @param PDOException $e + * @return bool + * @throws SQLException + */ + public function handleSqlError(PDOException $e) + { + $this->cleanup(); + $this->db->cancelTransaction(); + $this->db->setLogMessage($e->getMessage()); + + if ($this->db->getDriver()->isDuplicateKeyError($e->getCode())) { + return false; + } + + throw new SQLException('SQL error'.($this->logQueries ? ': '.$e->getMessage() : '')); + } +} diff --git a/vendor/fguillot/picodb/lib/PicoDb/Table.php b/vendor/fguillot/picodb/lib/PicoDb/Table.php new file mode 100644 index 0000000000..09eb928eae --- /dev/null +++ b/vendor/fguillot/picodb/lib/PicoDb/Table.php @@ -0,0 +1,721 @@ +db = $db; + $this->name = $name; + $this->conditionBuilder = new ConditionBuilder($db); + } + + /** + * Return the table name + * + * @access public + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Return ConditionBuilder object + * + * @access public + * @return ConditionBuilder + */ + public function getConditionBuilder() + { + return $this->conditionBuilder; + } + + /** + * Insert or update + * + * @access public + * @param array $data + * @return boolean + */ + public function save(array $data) + { + return $this->conditionBuilder->hasCondition() ? $this->update($data) : $this->insert($data); + } + + /** + * Update + * + * @access public + * @param array $data + * @return boolean + */ + public function update(array $data = array()) + { + $values = array_merge(array_values($data), array_values($this->sumColumns), $this->conditionBuilder->getValues()); + $sql = UpdateBuilder::getInstance($this->db, $this->conditionBuilder) + ->withTable($this->name) + ->withColumns(array_keys($data)) + ->withSumColumns(array_keys($this->sumColumns)) + ->build(); + + return $this->db->execute($sql, $values) !== false; + } + + /** + * Insert + * + * @access public + * @param array $data + * @return boolean + */ + public function insert(array $data) + { + return $this->db->getStatementHandler() + ->withSql(InsertBuilder::getInstance($this->db, $this->conditionBuilder) + ->withTable($this->name) + ->withColumns(array_keys($data)) + ->build() + ) + ->withNamedParams($data) + ->execute() !== false; + } + + /** + * Insert a new row and return the ID of the primary key + * + * @access public + * @param array $data + * @return bool|int + */ + public function persist(array $data) + { + if ($this->insert($data)) { + return $this->db->getLastId(); + } + + return false; + } + + /** + * Remove + * + * @access public + * @return boolean + */ + public function remove() + { + $sql = sprintf( + 'DELETE FROM %s %s', + $this->db->escapeIdentifier($this->name), + $this->conditionBuilder->build() + ); + + $result = $this->db->execute($sql, $this->conditionBuilder->getValues()); + return $result->rowCount() > 0; + } + + /** + * Fetch all rows + * + * @access public + * @return array + */ + public function findAll() + { + $rq = $this->db->execute($this->buildSelectQuery(), $this->conditionBuilder->getValues()); + $results = $rq->fetchAll(PDO::FETCH_ASSOC); + + if (is_callable($this->callback) && ! empty($results)) { + return call_user_func($this->callback, $results); + } + + return $results; + } + + /** + * Find all with a single column + * + * @access public + * @param string $column + * @return mixed + */ + public function findAllByColumn($column) + { + $this->columns = array($column); + $rq = $this->db->execute($this->buildSelectQuery(), $this->conditionBuilder->getValues()); + + return $rq->fetchAll(PDO::FETCH_COLUMN, 0); + } + + /** + * Fetch one row + * + * @access public + * @return array|null + */ + public function findOne() + { + $this->limit(1); + $result = $this->findAll(); + + return isset($result[0]) ? $result[0] : null; + } + + /** + * Fetch one column, first row + * + * @access public + * @param string $column + * @return string + */ + public function findOneColumn($column) + { + $this->limit(1); + $this->columns = array($column); + + return $this->db->execute($this->buildSelectQuery(), $this->conditionBuilder->getValues())->fetchColumn(); + } + + /** + * Build a subquery with an alias + * + * @access public + * @param string $sql + * @param string $alias + * @return $this + */ + public function subquery($sql, $alias) + { + $this->columns[] = '('.$sql.') AS '.$this->db->escapeIdentifier($alias); + return $this; + } + + /** + * Exists + * + * @access public + * @return integer + */ + public function exists() + { + $sql = sprintf( + 'SELECT 1 FROM %s '.implode(' ', $this->joins).$this->conditionBuilder->build(), + $this->db->escapeIdentifier($this->name) + ); + + $rq = $this->db->execute($sql, $this->conditionBuilder->getValues()); + $result = $rq->fetchColumn(); + + return $result ? true : false; + } + + /** + * Count + * + * @access public + * @return integer + */ + public function count() + { + $sql = sprintf( + 'SELECT COUNT(*) FROM %s '.implode(' ', $this->joins).$this->conditionBuilder->build().$this->sqlOrder.$this->sqlLimit.$this->sqlOffset, + $this->db->escapeIdentifier($this->name) + ); + + $rq = $this->db->execute($sql, $this->conditionBuilder->getValues()); + $result = $rq->fetchColumn(); + + return $result ? (int) $result : 0; + } + + /** + * Sum + * + * @access public + * @param string $column + * @return float + */ + public function sum($column) + { + $sql = sprintf( + 'SELECT SUM(%s) FROM %s '.implode(' ', $this->joins).$this->conditionBuilder->build().$this->sqlOrder.$this->sqlLimit.$this->sqlOffset, + $this->db->escapeIdentifier($column), + $this->db->escapeIdentifier($this->name) + ); + + $rq = $this->db->execute($sql, $this->conditionBuilder->getValues()); + $result = $rq->fetchColumn(); + + return $result ? (float) $result : 0; + } + + /** + * Increment column value + * + * @access public + * @param string $column + * @param string $value + * @return boolean + */ + public function increment($column, $value) + { + $sql = sprintf( + 'UPDATE %s SET %s=%s+%d '.$this->conditionBuilder->build(), + $this->db->escapeIdentifier($this->name), + $this->db->escapeIdentifier($column), + $this->db->escapeIdentifier($column), + $value + ); + + return $this->db->execute($sql, $this->conditionBuilder->getValues()) !== false; + } + + /** + * Decrement column value + * + * @access public + * @param string $column + * @param string $value + * @return boolean + */ + public function decrement($column, $value) + { + $sql = sprintf( + 'UPDATE %s SET %s=%s-%d '.$this->conditionBuilder->build(), + $this->db->escapeIdentifier($this->name), + $this->db->escapeIdentifier($column), + $this->db->escapeIdentifier($column), + $value + ); + + return $this->db->execute($sql, $this->conditionBuilder->getValues()) !== false; + } + + /** + * Left join + * + * @access public + * @param string $table Join table + * @param string $foreign_column Foreign key on the join table + * @param string $local_column Local column + * @param string $local_table Local table + * @param string $alias Join table alias + * @return $this + */ + public function join($table, $foreign_column, $local_column, $local_table = '', $alias = '') + { + $this->joins[] = sprintf( + 'LEFT JOIN %s ON %s=%s', + $this->db->escapeIdentifier($table), + $this->db->escapeIdentifier($alias ?: $table).'.'.$this->db->escapeIdentifier($foreign_column), + $this->db->escapeIdentifier($local_table ?: $this->name).'.'.$this->db->escapeIdentifier($local_column) + ); + + return $this; + } + + /** + * Left join + * + * @access public + * @param string $table1 + * @param string $alias1 + * @param string $column1 + * @param string $table2 + * @param string $column2 + * @return $this + */ + public function left($table1, $alias1, $column1, $table2, $column2) + { + $this->joins[] = sprintf( + 'LEFT JOIN %s AS %s ON %s=%s', + $this->db->escapeIdentifier($table1), + $this->db->escapeIdentifier($alias1), + $this->db->escapeIdentifier($alias1).'.'.$this->db->escapeIdentifier($column1), + $this->db->escapeIdentifier($table2).'.'.$this->db->escapeIdentifier($column2) + ); + + return $this; + } + + /** + * Inner join + * + * @access public + * @param string $table1 + * @param string $alias1 + * @param string $column1 + * @param string $table2 + * @param string $column2 + * @return $this + */ + public function inner($table1, $alias1, $column1, $table2, $column2) + { + $this->joins[] = sprintf( + 'JOIN %s AS %s ON %s=%s', + $this->db->escapeIdentifier($table1), + $this->db->escapeIdentifier($alias1), + $this->db->escapeIdentifier($alias1).'.'.$this->db->escapeIdentifier($column1), + $this->db->escapeIdentifier($table2).'.'.$this->db->escapeIdentifier($column2) + ); + + return $this; + } + + /** + * Order by + * + * @access public + * @param string $column Column name + * @param string $order Direction ASC or DESC + * @return $this + */ + public function orderBy($column, $order = self::SORT_ASC) + { + $order = strtoupper($order); + $order = $order === self::SORT_ASC || $order === self::SORT_DESC ? $order : self::SORT_ASC; + + if ($this->sqlOrder === '') { + $this->sqlOrder = ' ORDER BY '.$this->db->escapeIdentifier($column).' '.$order; + } + else { + $this->sqlOrder .= ', '.$this->db->escapeIdentifier($column).' '.$order; + } + + return $this; + } + + /** + * Ascending sort + * + * @access public + * @param string $column + * @return $this + */ + public function asc($column) + { + $this->orderBy($column, self::SORT_ASC); + return $this; + } + + /** + * Descending sort + * + * @access public + * @param string $column + * @return $this + */ + public function desc($column) + { + $this->orderBy($column, self::SORT_DESC); + return $this; + } + + /** + * Limit + * + * @access public + * @param integer $value + * @return $this + */ + public function limit($value) + { + if (! is_null($value)) { + $this->sqlLimit = ' LIMIT '.(int) $value; + } + + return $this; + } + + /** + * Offset + * + * @access public + * @param integer $value + * @return $this + */ + public function offset($value) + { + if (! is_null($value)) { + $this->sqlOffset = ' OFFSET '.(int) $value; + } + + return $this; + } + + /** + * Group by + * + * @access public + * @return $this + */ + public function groupBy() + { + $this->groupBy = func_get_args(); + return $this; + } + + /** + * Custom select + * + * @access public + * @param string $select + * @return $this + */ + public function select($select) + { + $this->sqlSelect = $select; + return $this; + } + + /** + * Define the columns for the select + * + * @access public + * @return $this + */ + public function columns() + { + $this->columns = func_get_args(); + return $this; + } + + /** + * Sum column + * + * @access public + * @param string $column + * @param mixed $value + * @return $this + */ + public function sumColumn($column, $value) + { + $this->sumColumns[$column] = $value; + return $this; + } + + /** + * Distinct + * + * @access public + * @return $this + */ + public function distinct() + { + $this->columns = func_get_args(); + $this->distinct = true; + return $this; + } + + /** + * Add callback to alter the resultset + * + * @access public + * @param Closure|array $callback + * @return $this + */ + public function callback($callback) + { + $this->callback = $callback; + return $this; + } + + /** + * Build a select query + * + * @access public + * @return string + */ + public function buildSelectQuery() + { + if (empty($this->sqlSelect)) { + $this->columns = $this->db->escapeIdentifierList($this->columns); + $this->sqlSelect = ($this->distinct ? 'DISTINCT ' : '').(empty($this->columns) ? '*' : implode(', ', $this->columns)); + } + + $this->groupBy = $this->db->escapeIdentifierList($this->groupBy); + + return trim(sprintf( + 'SELECT %s FROM %s %s %s %s %s %s %s', + $this->sqlSelect, + $this->db->escapeIdentifier($this->name), + implode(' ', $this->joins), + $this->conditionBuilder->build(), + empty($this->groupBy) ? '' : 'GROUP BY '.implode(', ', $this->groupBy), + $this->sqlOrder, + $this->sqlLimit, + $this->sqlOffset + )); + } + + /** + * Magic method for sql conditions + * + * @access public + * @param string $name + * @param array $arguments + * @return $this + */ + public function __call($name, array $arguments) + { + call_user_func_array(array($this->conditionBuilder, $name), $arguments); + return $this; + } +} diff --git a/vendor/fguillot/picodb/lib/PicoDb/UrlParser.php b/vendor/fguillot/picodb/lib/PicoDb/UrlParser.php new file mode 100644 index 0000000000..d8fcaf0092 --- /dev/null +++ b/vendor/fguillot/picodb/lib/PicoDb/UrlParser.php @@ -0,0 +1,93 @@ +url = getenv($environmentVariable); + } + + /** + * Get object instance + * + * @access public + * @param string $environmentVariable + * @return static + */ + public static function getInstance($environmentVariable = 'DATABASE_URL') + { + return new static($environmentVariable); + } + + /** + * Return true if the variable is defined + * + * @access public + * @return bool + */ + public function isEnvironmentVariableDefined() + { + return ! empty($this->url); + } + + /** + * Get settings from URL + * + * @access public + * @param string $url + * @return array + */ + public function getSettings($url = '') + { + $url = $url ?: $this->url; + $components = parse_url($url); + + if ($components === false) { + return array(); + } + + return array( + 'driver' => $this->getUrlComponent($components, 'scheme'), + 'username' => $this->getUrlComponent($components, 'user'), + 'password' => $this->getUrlComponent($components, 'pass'), + 'hostname' => $this->getUrlComponent($components, 'host'), + 'port' => $this->getUrlComponent($components, 'port'), + 'database' => ltrim($this->getUrlComponent($components, 'path'), '/'), + ); + } + + /** + * Get URL component + * + * @access private + * @param array $components + * @param string $component + * @return mixed|null + */ + private function getUrlComponent(array $components, $component) + { + return ! empty($components[$component]) ? $components[$component] : null; + } +} diff --git a/vendor/fguillot/simple-queue/LICENSE b/vendor/fguillot/simple-queue/LICENSE new file mode 100644 index 0000000000..a19d63a30e --- /dev/null +++ b/vendor/fguillot/simple-queue/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Frédéric Guillot + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/fguillot/simple-queue/src/Adapter/AmqpQueueAdapter.php b/vendor/fguillot/simple-queue/src/Adapter/AmqpQueueAdapter.php new file mode 100644 index 0000000000..379dd9b886 --- /dev/null +++ b/vendor/fguillot/simple-queue/src/Adapter/AmqpQueueAdapter.php @@ -0,0 +1,138 @@ +channel = $channel; + $this->exchange = $exchange; + $this->queue = $queue; + } + + /** + * Send a job + * + * @access public + * @param Job $job + * @return $this + */ + public function push(Job $job) + { + $message = new AMQPMessage($job->serialize(), array('content_type' => 'text/plain')); + $this->channel->basic_publish($message, $this->exchange); + return $this; + } + + /** + * Schedule a job in the future + * + * @access public + * @param Job $job + * @param DateTime $dateTime + * @return $this + */ + public function schedule(Job $job, DateTime $dateTime) + { + $now = new DateTime(); + $when = clone($dateTime); + $delay = $when->getTimestamp() - $now->getTimestamp(); + + $message = new AMQPMessage($job->serialize(), array('delivery_mode' => 2)); + $message->set('application_headers', new AMQPTable(array('x-delay' => $delay))); + + $this->channel->basic_publish($message, $this->exchange); + return $this; + } + + /** + * Wait and get job from a queue + * + * @access public + * @return Job|null + */ + public function pull() + { + $message = null; + + $this->channel->basic_consume($this->queue, 'test', false, false, false, false, function ($msg) use (&$message) { + $message = $msg; + $message->delivery_info['channel']->basic_cancel($message->delivery_info['consumer_tag']); + }); + + while (count($this->channel->callbacks)) { + $this->channel->wait(); + } + + if ($message === null) { + return null; + } + + $job = new Job(); + $job->setId($message->get('delivery_tag')); + $job->unserialize($message->getBody()); + + return $job; + } + + /** + * Acknowledge a job + * + * @access public + * @param Job $job + * @return $this + */ + public function completed(Job $job) + { + $this->channel->basic_ack($job->getId()); + return $this; + } + + /** + * Mark a job as failed + * + * @access public + * @param Job $job + * @return $this + */ + public function failed(Job $job) + { + $this->channel->basic_nack($job->getId()); + return $this; + } +} diff --git a/vendor/fguillot/simple-queue/src/Adapter/AwsSqsQueueAdapter.php b/vendor/fguillot/simple-queue/src/Adapter/AwsSqsQueueAdapter.php new file mode 100644 index 0000000000..0137731771 --- /dev/null +++ b/vendor/fguillot/simple-queue/src/Adapter/AwsSqsQueueAdapter.php @@ -0,0 +1,150 @@ + + */ +class AwsSqsQueueAdapter implements QueueAdapterInterface +{ + /** + * @var string + */ + private $queueName; + + /** + * @var SqsClient + */ + private $sqsClient; + + /** + * @var string + */ + private $sqsUrl; + + /** + * @var array + */ + private $config; + + /** + * AwsSqsQueueAdapter constructor. + * + * @param string $queueName The name of the SQS queue + * @param SqsClient $sqsClient An SQS client + * @param array $config Array of config values + */ + public function __construct($queueName, SqsClient $sqsClient, $config = array()) + { + $this->queueName = $queueName; + $this->sqsClient = $sqsClient; + $this->sqsUrl = $this->sqsClient->getQueueUrl(array('QueueName' => $this->queueName))->get('QueueUrl'); + $this->config = $config; + } + + /** + * Send a job + * + * @access public + * @param Job $job + * @return $this + */ + public function push(Job $job) + { + $this->sqsClient->sendMessage(array( + 'QueueUrl' => $this->sqsUrl, + 'MessageBody' => $job->serialize() + )); + return $this; + } + + /** + * Schedule a job in the future + * + * @access public + * @param Job $job + * @param DateTime $dateTime + * @return $this + */ + public function schedule(Job $job, DateTime $dateTime) + { + $now = new DateTime(); + $when = clone($dateTime); + $delay = $when->getTimestamp() - $now->getTimestamp(); + + $this->sqsClient->sendMessage(array( + 'QueueUrl' => $this->sqsUrl, + 'MessageBody' => $job->serialize(), + 'VisibilityTimeout' => $delay + )); + + return $this; + } + + /** + * Wait and get job from a queue + * + * @access public + * @return Job|null + */ + public function pull() + { + $result = $this->sqsClient->receiveMessage(array( + 'QueueUrl' => $this->sqsUrl, + 'WaitTimeSeconds' => empty($this->config['LongPollingTime']) ? 0 : (int) $this->config['LongPollingTime'] + )); + + if ($result['Messages'] == null) { + return null; + } + + $resultMessage = array_pop($result['Messages']); + + $job = new Job(); + $job->setId($resultMessage['ReceiptHandle']); + $job->unserialize($resultMessage['Body']); + + return $job; + } + + /** + * Acknowledge a job + * + * @access public + * @param Job $job + * @return $this + */ + public function completed(Job $job) + { + $this->sqsClient->deleteMessage(array( + 'QueueUrl' => $this->sqsUrl, + 'ReceiptHandle' => $job->getId() + )); + return $this; + } + + /** + * Mark a job as failed + * + * @access public + * @param Job $job + * @return $this + */ + public function failed(Job $job) + { + $this->sqsClient->changeMessageVisibility(array( + 'QueueUrl' => $this->sqsUrl, + 'ReceiptHandle' => $job->getId(), + 'VisibilityTimeout' => 0 + )); + return $this; + } +} diff --git a/vendor/fguillot/simple-queue/src/Adapter/BeanstalkQueueAdapter.php b/vendor/fguillot/simple-queue/src/Adapter/BeanstalkQueueAdapter.php new file mode 100644 index 0000000000..407f60e27c --- /dev/null +++ b/vendor/fguillot/simple-queue/src/Adapter/BeanstalkQueueAdapter.php @@ -0,0 +1,120 @@ +beanstalk = $beanstalk; + $this->queueName = $queueName; + } + + /** + * Send a job + * + * @access public + * @param Job $job + * @return $this + */ + public function push(Job $job) + { + $this->beanstalk->putInTube($this->queueName, $job->serialize()); + return $this; + } + + /** + * Schedule a job in the future + * + * @access public + * @param Job $job + * @param DateTime $dateTime + * @return $this + */ + public function schedule(Job $job, DateTime $dateTime) + { + $now = new DateTime(); + $when = clone($dateTime); + $delay = $when->getTimestamp() - $now->getTimestamp(); + + $this->beanstalk->putInTube($this->queueName, $job->serialize(), Pheanstalk::DEFAULT_PRIORITY, $delay); + return $this; + } + + /** + * Wait and get job from a queue + * + * @access public + * @return Job|null + */ + public function pull() + { + $beanstalkJob = $this->beanstalk->reserveFromTube($this->queueName); + + if ($beanstalkJob === false) { + return null; + } + + $job = new Job(); + $job->setId($beanstalkJob->getId()); + $job->unserialize($beanstalkJob->getData()); + + return $job; + } + + /** + * Acknowledge a job + * + * @access public + * @param Job $job + * @return $this + */ + public function completed(Job $job) + { + $beanstalkJob = new BeanstalkJob($job->getId(), $job->serialize()); + $this->beanstalk->delete($beanstalkJob); + return $this; + } + + /** + * Mark a job as failed + * + * @access public + * @param Job $job + * @return $this + */ + public function failed(Job $job) + { + $beanstalkJob = new BeanstalkJob($job->getId(), $job->serialize()); + $this->beanstalk->bury($beanstalkJob); + return $this; + } +} diff --git a/vendor/fguillot/simple-queue/src/Adapter/DisqueQueueAdapter.php b/vendor/fguillot/simple-queue/src/Adapter/DisqueQueueAdapter.php new file mode 100644 index 0000000000..047658d77d --- /dev/null +++ b/vendor/fguillot/simple-queue/src/Adapter/DisqueQueueAdapter.php @@ -0,0 +1,109 @@ +disque = $disque; + $this->queueName = $queueName; + } + + /** + * Send a job + * + * @access public + * @param Job $job + * @return $this + */ + public function push(Job $job) + { + $this->disque->queue($this->queueName)->push(new DisqueJob($job->getBody())); + return $this; + } + + /** + * Schedule a job in the future + * + * @access public + * @param Job $job + * @param DateTime $dateTime + * @return $this + */ + public function schedule(Job $job, DateTime $dateTime) + { + $this->disque->queue($this->queueName)->schedule(new DisqueJob($job->serialize()), $dateTime); + return $this; + } + + /** + * Wait and get job from a queue + * + * @access public + * @return Job|null + */ + public function pull() + { + $disqueJob = $this->disque->queue($this->queueName)->pull(); + + if ($disqueJob === null) { + return null; + } + + return new Job($disqueJob->getBody(), $disqueJob->getId()); + } + + /** + * Acknowledge a job + * + * @access public + * @param Job $job + * @return $this + */ + public function completed(Job $job) + { + $this->disque->queue($this->queueName)->processed(new DisqueJob($job->getBody(), $job->getId())); + return $this; + } + + /** + * Mark a job as failed + * + * @access public + * @param Job $job + * @return $this + */ + public function failed(Job $job) + { + $this->disque->queue($this->queueName)->failed(new DisqueJob($job->getBody(), $job->getId())); + return $this; + } +} diff --git a/vendor/fguillot/simple-queue/src/Adapter/MemoryQueueAdapter.php b/vendor/fguillot/simple-queue/src/Adapter/MemoryQueueAdapter.php new file mode 100644 index 0000000000..36375d5b15 --- /dev/null +++ b/vendor/fguillot/simple-queue/src/Adapter/MemoryQueueAdapter.php @@ -0,0 +1,100 @@ +queue = new SplQueue(); + } + + /** + * Send a job + * + * @access public + * @param Job $job + * @return $this + */ + public function push(Job $job) + { + $this->queue->enqueue($job->serialize()); + return $this; + } + + /** + * Schedule a job in the future + * + * @access public + * @param Job $job + * @param DateTime $dateTime + * @return bool + * @throws NotSupportedException + */ + public function schedule(Job $job, DateTime $dateTime) + { + throw new NotSupportedException('Job delay is not supported by MemoryQueue'); + } + + /** + * Wait and get job from a queue + * + * @access public + * @return Job|null + */ + public function pull() + { + try { + $job = new Job(); + $payload = $this->queue->dequeue(); + return $job->unserialize($payload); + } catch (Exception $e) { + return null; + } + } + + /** + * Acknowledge a job + * + * @access public + * @param Job $job + * @return $this + */ + public function completed(Job $job) + { + return $this; + } + + /** + * Mark a job as failed + * + * @access public + * @param Job $job + * @return $this + */ + public function failed(Job $job) + { + $this->queue->enqueue($job->serialize()); + return $this; + } +} diff --git a/vendor/fguillot/simple-queue/src/Exception/NotSupportedException.php b/vendor/fguillot/simple-queue/src/Exception/NotSupportedException.php new file mode 100644 index 0000000000..36106659a5 --- /dev/null +++ b/vendor/fguillot/simple-queue/src/Exception/NotSupportedException.php @@ -0,0 +1,14 @@ +body = $body; + $this->id = $id; + } + + /** + * Unserialize a payload + * + * @param string $payload + * @return $this + */ + public function unserialize($payload) + { + $this->body = json_decode($payload, true); + return $this; + } + + /** + * Serialize the body + * + * @return string + */ + public function serialize() + { + return json_encode($this->body); + } + + /** + * Set body + * + * @param mixed $body + * @return Job + */ + public function setBody($body) + { + $this->body = $body; + return $this; + } + + /** + * Get body + * + * @return mixed + */ + public function getBody() + { + return $this->body; + } + + /** + * Set job ID + * + * @param mixed $jobId + * @return Job + */ + public function setId($jobId) + { + $this->id = $jobId; + return $this; + } + + /** + * Get job ID + * @return mixed + */ + public function getId() + { + return $this->id; + } + + /** + * Execute job + */ + public function execute() + { + } +} diff --git a/vendor/fguillot/simple-queue/src/Queue.php b/vendor/fguillot/simple-queue/src/Queue.php new file mode 100644 index 0000000000..a88b55cb0f --- /dev/null +++ b/vendor/fguillot/simple-queue/src/Queue.php @@ -0,0 +1,92 @@ +queueAdapter = $queueAdapter; + } + + /** + * Send a job + * + * @access public + * @param Job $job + * @return $this + */ + public function push(Job $job) + { + $this->queueAdapter->push($job); + return $this; + } + + /** + * Schedule a job in the future + * + * @access public + * @param Job $job + * @param DateTime $dateTime + * @return $this + */ + public function schedule(Job $job, DateTime $dateTime) + { + $this->queueAdapter->schedule($job, $dateTime); + return $this; + } + + /** + * Wait and get job from a queue + * + * @access public + * @return Job|null + */ + public function pull() + { + return $this->queueAdapter->pull(); + } + + /** + * Acknowledge a job + * + * @access public + * @param Job $job + * @return $this + */ + public function completed(Job $job) + { + $this->queueAdapter->completed($job); + return $this; + } + + /** + * Mark a job as failed + * + * @access public + * @param Job $job + * @return $this + */ + public function failed(Job $job) + { + $this->queueAdapter->failed($job); + return $this; + } +} diff --git a/vendor/fguillot/simple-queue/src/QueueAdapterInterface.php b/vendor/fguillot/simple-queue/src/QueueAdapterInterface.php new file mode 100644 index 0000000000..9bda3070c3 --- /dev/null +++ b/vendor/fguillot/simple-queue/src/QueueAdapterInterface.php @@ -0,0 +1,58 @@ +data = $data; + $this->validators = $validators; + } + + public function execute() + { + $result = true; + + foreach ($this->validators as $validator) { + if (! $validator->execute($this->data)) { + $this->addError($validator->getField(), $validator->getErrorMessage()); + $result = false; + } + } + + return $result; + } + + public function addError($field, $message) + { + if (! isset($this->errors[$field])) { + $this->errors[$field] = array(); + } + + $this->errors[$field][] = $message; + } + + public function getErrors() + { + return $this->errors; + } +} diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Alpha.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Alpha.php new file mode 100644 index 0000000000..c29ba48106 --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Alpha.php @@ -0,0 +1,15 @@ +isFieldNotEmpty($data)) { + return ctype_alpha($data[$this->field]); + } + + return true; + } +} diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/AlphaNumeric.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/AlphaNumeric.php new file mode 100644 index 0000000000..8d5000b483 --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/AlphaNumeric.php @@ -0,0 +1,15 @@ +isFieldNotEmpty($data)) { + return ctype_alnum($data[$this->field]); + } + + return true; + } +} diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Base.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Base.php new file mode 100644 index 0000000000..8157ae5044 --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Base.php @@ -0,0 +1,37 @@ +field = $field; + $this->error_message = $error_message; + } + + public function getErrorMessage() + { + return $this->error_message; + } + + public function getField() + { + if (is_array($this->field)) { + return $this->field[0]; + } + + return $this->field; + } + + public function isFieldNotEmpty(array $data) + { + return isset($data[$this->field]) && $data[$this->field] !== ''; + } +} diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Date.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Date.php new file mode 100644 index 0000000000..4ec4b7fd37 --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Date.php @@ -0,0 +1,45 @@ +formats = $formats; + } + + public function execute(array $data) + { + if ($this->isFieldNotEmpty($data)) { + foreach ($this->formats as $format) { + if ($this->isValidDate($data[$this->field], $format)) { + return true; + } + } + + return false; + } + + return true; + } + + public function isValidDate($value, $format) + { + $date = DateTime::createFromFormat($format, $value); + + if ($date !== false) { + $errors = DateTime::getLastErrors(); + if ($errors['error_count'] === 0 && $errors['warning_count'] === 0) { + return $date->getTimestamp() > 0; + } + } + + return false; + } +} diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Email.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Email.php new file mode 100644 index 0000000000..f3977042fc --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Email.php @@ -0,0 +1,67 @@ +isFieldNotEmpty($data)) { + + // I use the same validation method as Firefox + // http://hg.mozilla.org/mozilla-central/file/cf5da681d577/content/html/content/src/nsHTMLInputElement.cpp#l3967 + + $value = $data[$this->field]; + $length = strlen($value); + + // If the email address begins with a '@' or ends with a '.', + // we know it's invalid. + if ($value[0] === '@' || $value[$length - 1] === '.') { + + return false; + } + + // Check the username + for ($i = 0; $i < $length && $value[$i] !== '@'; ++$i) { + + $c = $value[$i]; + + if (! (ctype_alnum($c) || $c === '.' || $c === '!' || $c === '#' || $c === '$' || + $c === '%' || $c === '&' || $c === '\'' || $c === '*' || $c === '+' || + $c === '-' || $c === '/' || $c === '=' || $c === '?' || $c === '^' || + $c === '_' || $c === '`' || $c === '{' || $c === '|' || $c === '}' || + $c === '~')) { + + return false; + } + } + + // There is no domain name (or it's one-character long), + // that's not a valid email address. + if (++$i >= $length) return false; + if (($i + 1) === $length) return false; + + // The domain name can't begin with a dot. + if ($value[$i] === '.') return false; + + // Parsing the domain name. + for (; $i < $length; ++$i) { + + $c = $value[$i]; + + if ($c === '.') { + + // A dot can't follow a dot. + if ($value[$i - 1] === '.') return false; + } + elseif (! (ctype_alnum($c) || $c === '-')) { + + // The domain characters have to be in this list to be valid. + return false; + } + } + } + + return true; + } +} diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Equals.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Equals.php new file mode 100644 index 0000000000..6b69dd8070 --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Equals.php @@ -0,0 +1,27 @@ +field2 = $field2; + } + + public function execute(array $data) + { + if ($this->isFieldNotEmpty($data)) { + if (! isset($data[$this->field2])) { + return false; + } + + return $data[$this->field] === $data[$this->field2]; + } + + return true; + } +} diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Exists.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Exists.php new file mode 100644 index 0000000000..1998e6738a --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Exists.php @@ -0,0 +1,38 @@ +pdo = $pdo; + $this->table = $table; + $this->key = $key; + } + + + public function execute(array $data) + { + if (! $this->isFieldNotEmpty($data)) { + return true; + } + + if ($this->key === '') { + $this->key = $this->field; + } + + $rq = $this->pdo->prepare('SELECT 1 FROM '.$this->table.' WHERE '.$this->key.'=?'); + $rq->execute(array($data[$this->field])); + + return $rq->fetchColumn() == 1; + } +} diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/GreaterThan.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/GreaterThan.php new file mode 100644 index 0000000000..6e56031998 --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/GreaterThan.php @@ -0,0 +1,23 @@ +min = $min; + } + + public function execute(array $data) + { + if ($this->isFieldNotEmpty($data)) { + return $data[$this->field] > $this->min; + } + + return true; + } +} diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/InArray.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/InArray.php new file mode 100644 index 0000000000..f2f8c1340e --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/InArray.php @@ -0,0 +1,23 @@ +array = $array; + } + + public function execute(array $data) + { + if ($this->isFieldNotEmpty($data)) { + return in_array($data[$this->field], $this->array); + } + + return true; + } +} diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Integer.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Integer.php new file mode 100644 index 0000000000..5afdc1e0f6 --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Integer.php @@ -0,0 +1,25 @@ +isFieldNotEmpty($data)) { + if (is_string($data[$this->field])) { + + if ($data[$this->field][0] === '-') { + return ctype_digit(substr($data[$this->field], 1)); + } + + return ctype_digit($data[$this->field]); + } + else { + return is_int($data[$this->field]); + } + } + + return true; + } +} diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Ip.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Ip.php new file mode 100644 index 0000000000..754f4f3e6e --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Ip.php @@ -0,0 +1,15 @@ +isFieldNotEmpty($data)) { + return filter_var($data[$this->field], FILTER_VALIDATE_IP) !== false; + } + + return true; + } +} diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Length.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Length.php new file mode 100644 index 0000000000..7ef241c408 --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Length.php @@ -0,0 +1,26 @@ +min = $min; + $this->max = $max; + } + + public function execute(array $data) + { + if ($this->isFieldNotEmpty($data)) { + $length = mb_strlen($data[$this->field], 'UTF-8'); + return $length >= $this->min && $length <= $this->max; + } + + return true; + } +} diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/MaxLength.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/MaxLength.php new file mode 100644 index 0000000000..6c4e777142 --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/MaxLength.php @@ -0,0 +1,24 @@ +max = $max; + } + + public function execute(array $data) + { + if ($this->isFieldNotEmpty($data)) { + $length = mb_strlen($data[$this->field], 'UTF-8'); + return $length <= $this->max; + } + + return true; + } +} diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/MinLength.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/MinLength.php new file mode 100644 index 0000000000..0ac4217a22 --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/MinLength.php @@ -0,0 +1,24 @@ +min = $min; + } + + public function execute(array $data) + { + if ($this->isFieldNotEmpty($data)) { + $length = mb_strlen($data[$this->field], 'UTF-8'); + return $length >= $this->min; + } + + return true; + } +} diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/NotEmpty.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/NotEmpty.php new file mode 100644 index 0000000000..bbb14b5b8b --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/NotEmpty.php @@ -0,0 +1,15 @@ +field, $data)) { + return $data[$this->field] !== null && $data[$this->field] !== ''; + } + + return true; + } +} diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/NotEquals.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/NotEquals.php new file mode 100644 index 0000000000..d1d949ea99 --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/NotEquals.php @@ -0,0 +1,28 @@ +field2 = $field2; + } + + public function execute(array $data) + { + if ($this->isFieldNotEmpty($data)) { + + if (! isset($data[$this->field2])) { + return true; + } + + return $data[$this->field] !== $data[$this->field2]; + } + + return true; + } +} diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/NotInArray.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/NotInArray.php new file mode 100644 index 0000000000..98974c9cbc --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/NotInArray.php @@ -0,0 +1,15 @@ +isFieldNotEmpty($data)) { + return ! in_array($data[$this->field], $this->array); + } + + return true; + } +} diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Numeric.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Numeric.php new file mode 100644 index 0000000000..312268662e --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Numeric.php @@ -0,0 +1,15 @@ +isFieldNotEmpty($data)) { + return is_numeric($data[$this->field]); + } + + return true; + } +} diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Range.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Range.php new file mode 100644 index 0000000000..065b2b9d1b --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Range.php @@ -0,0 +1,33 @@ +min = $min; + $this->max = $max; + } + + public function execute(array $data) + { + if ($this->isFieldNotEmpty($data)) { + + if (! is_numeric($data[$this->field])) { + return false; + } + + if ($data[$this->field] < $this->min || $data[$this->field] > $this->max) { + return false; + } + } + + return true; + } +} diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Required.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Required.php new file mode 100644 index 0000000000..f5e65616e3 --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Required.php @@ -0,0 +1,11 @@ +isFieldNotEmpty($data); + } +} diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Unique.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Unique.php new file mode 100644 index 0000000000..00caeb547d --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Unique.php @@ -0,0 +1,48 @@ +pdo = $pdo; + $this->primary_key = $primary_key; + $this->table = $table; + } + + public function execute(array $data) + { + if ($this->isFieldNotEmpty($data)) { + if (! isset($data[$this->primary_key])) { + $rq = $this->pdo->prepare('SELECT 1 FROM '.$this->table.' WHERE '.$this->field.'=?'); + $rq->execute(array($data[$this->field])); + } + else { + + $rq = $this->pdo->prepare( + 'SELECT 1 FROM '.$this->table.' + WHERE '.$this->field.'=? AND '.$this->primary_key.' != ?' + ); + + $rq->execute(array($data[$this->field], $data[$this->primary_key])); + } + + $result = $rq->fetchColumn(); + + if ($result == 1) { // Postgresql returns an integer but other database returns a string '1' + return false; + } + } + + return true; + } +} diff --git a/vendor/fguillot/simpleLogger/LICENSE b/vendor/fguillot/simpleLogger/LICENSE new file mode 100644 index 0000000000..6a362bc163 --- /dev/null +++ b/vendor/fguillot/simpleLogger/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Frederic Guillot + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/fguillot/simpleLogger/src/SimpleLogger/Base.php b/vendor/fguillot/simpleLogger/src/SimpleLogger/Base.php new file mode 100644 index 0000000000..c662a1a387 --- /dev/null +++ b/vendor/fguillot/simpleLogger/src/SimpleLogger/Base.php @@ -0,0 +1,89 @@ +level = $level; + } + + /** + * Get minimum log level + * + * @access public + * @return string + */ + public function getLevel() + { + return $this->level; + } + + /** + * Dump to log a variable (by example an array) + * + * @param mixed $variable + */ + public function dump($variable) + { + $this->log(LogLevel::DEBUG, var_export($variable, true)); + } + + /** + * Interpolates context values into the message placeholders. + * + * @access protected + * @param string $message + * @param array $context + * @return string + */ + protected function interpolate($message, array $context = array()) + { + // build a replacement array with braces around the context keys + $replace = array(); + + foreach ($context as $key => $val) { + $replace['{' . $key . '}'] = $val; + } + + // interpolate replacement values into the message and return + return strtr($message, $replace); + } + + /** + * Format log message + * + * @param mixed $level + * @param string $message + * @param array $context + * @return string + */ + protected function formatMessage($level, $message, array $context = array()) + { + return '['.date('Y-m-d H:i:s').'] ['.$level.'] '.$this->interpolate($message, $context).PHP_EOL; + } +} diff --git a/vendor/fguillot/simpleLogger/src/SimpleLogger/File.php b/vendor/fguillot/simpleLogger/src/SimpleLogger/File.php new file mode 100644 index 0000000000..be3bfa8537 --- /dev/null +++ b/vendor/fguillot/simpleLogger/src/SimpleLogger/File.php @@ -0,0 +1,48 @@ +filename = $filename; + } + + /** + * Logs with an arbitrary level. + * + * @param mixed $level + * @param string $message + * @param array $context + */ + public function log($level, $message, array $context = array()) + { + $line = $this->formatMessage($level, $message, $context); + + if (file_put_contents($this->filename, $line, FILE_APPEND | LOCK_EX) === false) { + throw new RuntimeException('Unable to write to the log file.'); + } + } +} diff --git a/vendor/fguillot/simpleLogger/src/SimpleLogger/Logger.php b/vendor/fguillot/simpleLogger/src/SimpleLogger/Logger.php new file mode 100644 index 0000000000..dc340cdeef --- /dev/null +++ b/vendor/fguillot/simpleLogger/src/SimpleLogger/Logger.php @@ -0,0 +1,94 @@ +loggers[] = $logger; + } + + /** + * Proxy method to the real loggers + * + * @param mixed $level + * @param string $message + * @param array $context + * @return null + */ + public function log($level, $message, array $context = array()) + { + foreach ($this->loggers as $logger) { + if ($this->getLevelPriority($level) >= $this->getLevelPriority($logger->getLevel())) { + $logger->log($level, $message, $context); + } + } + } + + /** + * Dump variables for debugging + * + * @param mixed $variable + */ + public function dump($variable) + { + foreach ($this->loggers as $logger) { + if ($this->getLevelPriority(LogLevel::DEBUG) >= $this->getLevelPriority($logger->getLevel())) { + $logger->dump($variable); + } + } + } +} diff --git a/vendor/fguillot/simpleLogger/src/SimpleLogger/Stderr.php b/vendor/fguillot/simpleLogger/src/SimpleLogger/Stderr.php new file mode 100644 index 0000000000..2573177e2d --- /dev/null +++ b/vendor/fguillot/simpleLogger/src/SimpleLogger/Stderr.php @@ -0,0 +1,25 @@ +formatMessage($level, $message, $context), FILE_APPEND); + } +} diff --git a/vendor/fguillot/simpleLogger/src/SimpleLogger/Stdout.php b/vendor/fguillot/simpleLogger/src/SimpleLogger/Stdout.php new file mode 100644 index 0000000000..82d181b287 --- /dev/null +++ b/vendor/fguillot/simpleLogger/src/SimpleLogger/Stdout.php @@ -0,0 +1,25 @@ +formatMessage($level, $message, $context), FILE_APPEND); + } +} diff --git a/vendor/fguillot/simpleLogger/src/SimpleLogger/Syslog.php b/vendor/fguillot/simpleLogger/src/SimpleLogger/Syslog.php new file mode 100644 index 0000000000..c4e26a7ae4 --- /dev/null +++ b/vendor/fguillot/simpleLogger/src/SimpleLogger/Syslog.php @@ -0,0 +1,72 @@ +getSyslogPriority($level); + $syslogMessage = $this->interpolate($message, $context); + + syslog($syslogPriority, $syslogMessage); + } +} diff --git a/vendor/gregwar/captcha/.gitignore b/vendor/gregwar/captcha/.gitignore new file mode 100644 index 0000000000..5a275919a6 --- /dev/null +++ b/vendor/gregwar/captcha/.gitignore @@ -0,0 +1,3 @@ +demo/*.jpg +demo/*.pgm +demo/temp/ diff --git a/vendor/gregwar/captcha/.travis.yml b/vendor/gregwar/captcha/.travis.yml new file mode 100644 index 0000000000..9c4b70497a --- /dev/null +++ b/vendor/gregwar/captcha/.travis.yml @@ -0,0 +1,12 @@ +language: php + +php: + - 5.3.3 + - 5.3 + - 5.4 + - 5.5 + - 5.6 + +script: + - composer install + - phpunit diff --git a/vendor/gregwar/captcha/CaptchaBuilder.php b/vendor/gregwar/captcha/CaptchaBuilder.php new file mode 100644 index 0000000000..bc6173febe --- /dev/null +++ b/vendor/gregwar/captcha/CaptchaBuilder.php @@ -0,0 +1,720 @@ + + * @author Jeremy Livingston + */ +class CaptchaBuilder implements CaptchaBuilderInterface +{ + /** + * @var array + */ + protected $fingerprint = array(); + + /** + * @var bool + */ + protected $useFingerprint = false; + + /** + * @var array + */ + protected $textColor = null; + + /** + * @var array + */ + protected $backgroundColor = null; + + /** + * @var array + */ + protected $backgroundImages = array(); + + /** + * @var resource + */ + protected $contents = null; + + /** + * @var string + */ + protected $phrase = null; + + /** + * @var PhraseBuilderInterface + */ + protected $builder; + + /** + * @var bool + */ + protected $distortion = true; + + /** + * The maximum number of lines to draw in front of + * the image. null - use default algorithm + */ + protected $maxFrontLines = null; + + /** + * The maximum number of lines to draw behind + * the image. null - use default algorithm + */ + protected $maxBehindLines = null; + + /** + * The maximum angle of char + */ + protected $maxAngle = 8; + + /** + * The maximum offset of char + */ + protected $maxOffset = 5; + + /** + * Is the interpolation enabled ? + * + * @var bool + */ + protected $interpolation = true; + + /** + * Ignore all effects + * + * @var bool + */ + protected $ignoreAllEffects = false; + + /** + * Allowed image types for the background images + * + * @var array + */ + protected $allowedBackgroundImageTypes = array('image/png', 'image/jpeg', 'image/gif'); + + /** + * The image contents + */ + public function getContents() + { + return $this->contents; + } + + /** + * Enable/Disables the interpolation + * + * @param $interpolate bool True to enable, false to disable + * + * @return CaptchaBuilder + */ + public function setInterpolation($interpolate = true) + { + $this->interpolation = $interpolate; + + return $this; + } + + /** + * Temporary dir, for OCR check + */ + public $tempDir = 'temp/'; + + public function __construct($phrase = null, PhraseBuilderInterface $builder = null) + { + if ($builder === null) { + $this->builder = new PhraseBuilder; + } else { + $this->builder = $builder; + } + + if ($phrase === null) { + $phrase = $this->builder->build(); + } + + $this->phrase = $phrase; + } + + /** + * Setting the phrase + */ + public function setPhrase($phrase) + { + $this->phrase = (string) $phrase; + } + + /** + * Enables/disable distortion + */ + public function setDistortion($distortion) + { + $this->distortion = (bool) $distortion; + + return $this; + } + + public function setMaxBehindLines($maxBehindLines) + { + $this->maxBehindLines = $maxBehindLines; + + return $this; + } + + public function setMaxFrontLines($maxFrontLines) + { + $this->maxFrontLines = $maxFrontLines; + + return $this; + } + + public function setMaxAngle($maxAngle) + { + $this->maxAngle = $maxAngle; + + return $this; + } + + public function setMaxOffset($maxOffset) + { + $this->maxOffset = $maxOffset; + + return $this; + } + + /** + * Gets the captcha phrase + */ + public function getPhrase() + { + return $this->phrase; + } + + /** + * Returns true if the given phrase is good + */ + public function testPhrase($phrase) + { + return ($this->builder->niceize($phrase) == $this->builder->niceize($this->getPhrase())); + } + + /** + * Instantiation + */ + public static function create($phrase = null) + { + return new self($phrase); + } + + /** + * Sets the text color to use + */ + public function setTextColor($r, $g, $b) + { + $this->textColor = array($r, $g, $b); + + return $this; + } + + /** + * Sets the background color to use + */ + public function setBackgroundColor($r, $g, $b) + { + $this->backgroundColor = array($r, $g, $b); + + return $this; + } + + /** + * Sets the ignoreAllEffects value + * + * @param bool $ignoreAllEffects + * @return CaptchaBuilder + */ + public function setIgnoreAllEffects($ignoreAllEffects) + { + $this->ignoreAllEffects = $ignoreAllEffects; + + return $this; + } + + /** + * Sets the list of background images to use (one image is randomly selected) + */ + public function setBackgroundImages(array $backgroundImages) + { + $this->backgroundImages = $backgroundImages; + + return $this; + } + + /** + * Draw lines over the image + */ + protected function drawLine($image, $width, $height, $tcol = null) + { + if ($tcol === null) { + $tcol = imagecolorallocate($image, $this->rand(100, 255), $this->rand(100, 255), $this->rand(100, 255)); + } + + if ($this->rand(0, 1)) { // Horizontal + $Xa = $this->rand(0, $width/2); + $Ya = $this->rand(0, $height); + $Xb = $this->rand($width/2, $width); + $Yb = $this->rand(0, $height); + } else { // Vertical + $Xa = $this->rand(0, $width); + $Ya = $this->rand(0, $height/2); + $Xb = $this->rand(0, $width); + $Yb = $this->rand($height/2, $height); + } + imagesetthickness($image, $this->rand(1, 3)); + imageline($image, $Xa, $Ya, $Xb, $Yb, $tcol); + } + + /** + * Apply some post effects + */ + protected function postEffect($image) + { + if (!function_exists('imagefilter')) { + return; + } + + if ($this->backgroundColor != null || $this->textColor != null) { + return; + } + + // Negate ? + if ($this->rand(0, 1) == 0) { + imagefilter($image, IMG_FILTER_NEGATE); + } + + // Edge ? + if ($this->rand(0, 10) == 0) { + imagefilter($image, IMG_FILTER_EDGEDETECT); + } + + // Contrast + imagefilter($image, IMG_FILTER_CONTRAST, $this->rand(-50, 10)); + + // Colorize + if ($this->rand(0, 5) == 0) { + imagefilter($image, IMG_FILTER_COLORIZE, $this->rand(-80, 50), $this->rand(-80, 50), $this->rand(-80, 50)); + } + } + + /** + * Writes the phrase on the image + */ + protected function writePhrase($image, $phrase, $font, $width, $height) + { + $length = strlen($phrase); + if ($length === 0) { + return imagecolorallocate($image, 0, 0, 0); + } + + // Gets the text size and start position + $size = $width / $length - $this->rand(0, 3) - 1; + $box = imagettfbbox($size, 0, $font, $phrase); + $textWidth = $box[2] - $box[0]; + $textHeight = $box[1] - $box[7]; + $x = ($width - $textWidth) / 2; + $y = ($height - $textHeight) / 2 + $size; + + if (!count($this->textColor)) { + $textColor = array($this->rand(0, 150), $this->rand(0, 150), $this->rand(0, 150)); + } else { + $textColor = $this->textColor; + } + $col = imagecolorallocate($image, $textColor[0], $textColor[1], $textColor[2]); + + // Write the letters one by one, with random angle + for ($i=0; $i<$length; $i++) { + $box = imagettfbbox($size, 0, $font, $phrase[$i]); + $w = $box[2] - $box[0]; + $angle = $this->rand(-$this->maxAngle, $this->maxAngle); + $offset = $this->rand(-$this->maxOffset, $this->maxOffset); + imagettftext($image, $size, $angle, $x, $y + $offset, $col, $font, $phrase[$i]); + $x += $w; + } + + return $col; + } + + /** + * Try to read the code against an OCR + */ + public function isOCRReadable() + { + if (!is_dir($this->tempDir)) { + @mkdir($this->tempDir, 0755, true); + } + + $tempj = $this->tempDir . uniqid('captcha', true) . '.jpg'; + $tempp = $this->tempDir . uniqid('captcha', true) . '.pgm'; + + $this->save($tempj); + shell_exec("convert $tempj $tempp"); + $value = trim(strtolower(shell_exec("ocrad $tempp"))); + + @unlink($tempj); + @unlink($tempp); + + return $this->testPhrase($value); + } + + /** + * Builds while the code is readable against an OCR + */ + public function buildAgainstOCR($width = 150, $height = 40, $font = null, $fingerprint = null) + { + do { + $this->build($width, $height, $font, $fingerprint); + } while ($this->isOCRReadable()); + } + + /** + * Generate the image + */ + public function build($width = 150, $height = 40, $font = null, $fingerprint = null) + { + if (null !== $fingerprint) { + $this->fingerprint = $fingerprint; + $this->useFingerprint = true; + } else { + $this->fingerprint = array(); + $this->useFingerprint = false; + } + + if ($font === null) { + $font = __DIR__ . '/Font/captcha'.$this->rand(0, 5).'.ttf'; + } + + if (empty($this->backgroundImages)) { + // if background images list is not set, use a color fill as a background + $image = imagecreatetruecolor($width, $height); + if ($this->backgroundColor == null) { + $bg = imagecolorallocate($image, $this->rand(200, 255), $this->rand(200, 255), $this->rand(200, 255)); + } else { + $color = $this->backgroundColor; + $bg = imagecolorallocate($image, $color[0], $color[1], $color[2]); + } + $this->background = $bg; + imagefill($image, 0, 0, $bg); + } else { + // use a random background image + $randomBackgroundImage = $this->backgroundImages[rand(0, count($this->backgroundImages)-1)]; + + $imageType = $this->validateBackgroundImage($randomBackgroundImage); + + $image = $this->createBackgroundImageFromType($randomBackgroundImage, $imageType); + } + + // Apply effects + if (!$this->ignoreAllEffects) { + $square = $width * $height; + $effects = $this->rand($square/3000, $square/2000); + + // set the maximum number of lines to draw in front of the text + if ($this->maxBehindLines != null && $this->maxBehindLines > 0) { + $effects = min($this->maxBehindLines, $effects); + } + + if ($this->maxBehindLines !== 0) { + for ($e = 0; $e < $effects; $e++) { + $this->drawLine($image, $width, $height); + } + } + } + + // Write CAPTCHA text + $color = $this->writePhrase($image, $this->phrase, $font, $width, $height); + + // Apply effects + if (!$this->ignoreAllEffects) { + $square = $width * $height; + $effects = $this->rand($square/3000, $square/2000); + + // set the maximum number of lines to draw in front of the text + if ($this->maxFrontLines != null && $this->maxFrontLines > 0) { + $effects = min($this->maxFrontLines, $effects); + } + + if ($this->maxFrontLines !== 0) { + for ($e = 0; $e < $effects; $e++) { + $this->drawLine($image, $width, $height, $color); + } + } + } + + // Distort the image + if ($this->distortion && !$this->ignoreAllEffects) { + $image = $this->distort($image, $width, $height, $bg); + } + + // Post effects + if (!$this->ignoreAllEffects) { + $this->postEffect($image); + } + + $this->contents = $image; + + return $this; + } + + /** + * Distorts the image + */ + public function distort($image, $width, $height, $bg) + { + $contents = imagecreatetruecolor($width, $height); + $X = $this->rand(0, $width); + $Y = $this->rand(0, $height); + $phase = $this->rand(0, 10); + $scale = 1.1 + $this->rand(0, 10000) / 30000; + for ($x = 0; $x < $width; $x++) { + for ($y = 0; $y < $height; $y++) { + $Vx = $x - $X; + $Vy = $y - $Y; + $Vn = sqrt($Vx * $Vx + $Vy * $Vy); + + if ($Vn != 0) { + $Vn2 = $Vn + 4 * sin($Vn / 30); + $nX = $X + ($Vx * $Vn2 / $Vn); + $nY = $Y + ($Vy * $Vn2 / $Vn); + } else { + $nX = $X; + $nY = $Y; + } + $nY = $nY + $scale * sin($phase + $nX * 0.2); + + if ($this->interpolation) { + $p = $this->interpolate( + $nX - floor($nX), + $nY - floor($nY), + $this->getCol($image, floor($nX), floor($nY), $bg), + $this->getCol($image, ceil($nX), floor($nY), $bg), + $this->getCol($image, floor($nX), ceil($nY), $bg), + $this->getCol($image, ceil($nX), ceil($nY), $bg) + ); + } else { + $p = $this->getCol($image, round($nX), round($nY), $bg); + } + + if ($p == 0) { + $p = $bg; + } + + imagesetpixel($contents, $x, $y, $p); + } + } + + return $contents; + } + + /** + * Saves the Captcha to a jpeg file + */ + public function save($filename, $quality = 90) + { + imagejpeg($this->contents, $filename, $quality); + } + + /** + * Gets the image GD + */ + public function getGd() + { + return $this->contents; + } + + /** + * Gets the image contents + */ + public function get($quality = 90) + { + ob_start(); + $this->output($quality); + + return ob_get_clean(); + } + + /** + * Gets the HTML inline base64 + */ + public function inline($quality = 90) + { + return 'data:image/jpeg;base64,' . base64_encode($this->get($quality)); + } + + /** + * Outputs the image + */ + public function output($quality = 90) + { + imagejpeg($this->contents, null, $quality); + } + + /** + * @return array + */ + public function getFingerprint() + { + return $this->fingerprint; + } + + /** + * Returns a random number or the next number in the + * fingerprint + */ + protected function rand($min, $max) + { + if (!is_array($this->fingerprint)) { + $this->fingerprint = array(); + } + + if ($this->useFingerprint) { + $value = current($this->fingerprint); + next($this->fingerprint); + } else { + $value = mt_rand($min, $max); + $this->fingerprint[] = $value; + } + + return $value; + } + + /** + * @param $x + * @param $y + * @param $nw + * @param $ne + * @param $sw + * @param $se + * + * @return int + */ + protected function interpolate($x, $y, $nw, $ne, $sw, $se) + { + list($r0, $g0, $b0) = $this->getRGB($nw); + list($r1, $g1, $b1) = $this->getRGB($ne); + list($r2, $g2, $b2) = $this->getRGB($sw); + list($r3, $g3, $b3) = $this->getRGB($se); + + $cx = 1.0 - $x; + $cy = 1.0 - $y; + + $m0 = $cx * $r0 + $x * $r1; + $m1 = $cx * $r2 + $x * $r3; + $r = (int) ($cy * $m0 + $y * $m1); + + $m0 = $cx * $g0 + $x * $g1; + $m1 = $cx * $g2 + $x * $g3; + $g = (int) ($cy * $m0 + $y * $m1); + + $m0 = $cx * $b0 + $x * $b1; + $m1 = $cx * $b2 + $x * $b3; + $b = (int) ($cy * $m0 + $y * $m1); + + return ($r << 16) | ($g << 8) | $b; + } + + /** + * @param $image + * @param $x + * @param $y + * + * @return int + */ + protected function getCol($image, $x, $y, $background) + { + $L = imagesx($image); + $H = imagesy($image); + if ($x < 0 || $x >= $L || $y < 0 || $y >= $H) { + return $background; + } + + return imagecolorat($image, $x, $y); + } + + /** + * @param $col + * + * @return array + */ + protected function getRGB($col) + { + return array( + (int) ($col >> 16) & 0xff, + (int) ($col >> 8) & 0xff, + (int) ($col) & 0xff, + ); + } + + /** + * Validate the background image path. Return the image type if valid + * + * @param string $backgroundImage + * @return string + * @throws Exception + */ + protected function validateBackgroundImage($backgroundImage) + { + // check if file exists + if (!file_exists($backgroundImage)) { + $backgroundImageExploded = explode('/', $backgroundImage); + $imageFileName = count($backgroundImageExploded) > 1? $backgroundImageExploded[count($backgroundImageExploded)-1] : $backgroundImage; + + throw new Exception('Invalid background image: ' . $imageFileName); + } + + // check image type + $finfo = finfo_open(FILEINFO_MIME_TYPE); // return mime type ala mimetype extension + $imageType = finfo_file($finfo, $backgroundImage); + finfo_close($finfo); + + if (!in_array ($imageType, $this->allowedBackgroundImageTypes)) { + throw new Exception('Invalid background image type! Allowed types are: ' . join(', ', $this->allowedBackgroundImageTypes)); + } + + return $imageType; + } + + /** + * Create background image from type + * + * @param string $backgroundImage + * @param string $imageType + * @return resource + * @throws Exception + */ + protected function createBackgroundImageFromType($backgroundImage, $imageType) + { + switch ($imageType) { + case 'image/jpeg': + $image = imagecreatefromjpeg($backgroundImage); + break; + case 'image/png': + $image = imagecreatefrompng($backgroundImage); + break; + case 'image/gif': + $image = imagecreatefromgif($backgroundImage); + break; + + default: + throw new Exception('Not supported file type for background image!'); + break; + } + + return $image; + } +} diff --git a/vendor/gregwar/captcha/CaptchaBuilderInterface.php b/vendor/gregwar/captcha/CaptchaBuilderInterface.php new file mode 100644 index 0000000000..bdebf38f6c --- /dev/null +++ b/vendor/gregwar/captcha/CaptchaBuilderInterface.php @@ -0,0 +1,30 @@ +g-y`84XoT{ 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 literal 0 HcmV?d00001 diff --git a/vendor/gregwar/captcha/Font/captcha1.ttf b/vendor/gregwar/captcha/Font/captcha1.ttf new file mode 100644 index 0000000000000000000000000000000000000000..bb1abf6b6696261ffdfd5c17b74dfb03ce7c72b5 GIT binary patch literal 76232 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

@KU02>Big?7$LN=!il-X-8& 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 RIg`ApC4KXu~R{DH%B(a96XPoFsd)ZEEK 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 literal 0 HcmV?d00001 diff --git a/vendor/gregwar/captcha/Font/captcha3.ttf b/vendor/gregwar/captcha/Font/captcha3.ttf new file mode 100644 index 0000000000000000000000000000000000000000..d232902cf89aca63cb9a92ad4893ffddcf2eb63d GIT binary patch literal 15976 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 E3yqV3z5oCK literal 0 HcmV?d00001 diff --git a/vendor/gregwar/captcha/Font/captcha4.ttf b/vendor/gregwar/captcha/Font/captcha4.ttf new file mode 100644 index 0000000000000000000000000000000000000000..ab154440d796a815274ff79060759b5d1668b430 GIT binary patch literal 906980 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�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

Captchas gallery

+ + + + +
+ + + diff --git a/vendor/gregwar/captcha/demo/ocr.php b/vendor/gregwar/captcha/demo/ocr.php new file mode 100644 index 0000000000..3d745f65f2 --- /dev/null +++ b/vendor/gregwar/captcha/demo/ocr.php @@ -0,0 +1,42 @@ +setDistortion(false) + ->build() + ; + + if ($captcha->isOCRReadable()) { + $passed++; + $captcha->save("passed$passed.jpg"); + echo "passed at ocr... "; + } else { + echo "failed... "; + } + + echo "pass rate: ".round(100*$passed/($i+1),2)."%\n"; +} + +echo "\n"; +echo "Over, $passed/$tests readed with OCR\n"; diff --git a/vendor/gregwar/captcha/demo/output.php b/vendor/gregwar/captcha/demo/output.php new file mode 100644 index 0000000000..2a4f330369 --- /dev/null +++ b/vendor/gregwar/captcha/demo/output.php @@ -0,0 +1,15 @@ +build() + ->output() +; diff --git a/vendor/miniflux/picofeed/LICENSE b/vendor/miniflux/picofeed/LICENSE new file mode 100644 index 0000000000..6a362bc163 --- /dev/null +++ b/vendor/miniflux/picofeed/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Frederic Guillot + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Base.php b/vendor/miniflux/picofeed/lib/PicoFeed/Base.php new file mode 100644 index 0000000000..41a6f8f0d3 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Base.php @@ -0,0 +1,38 @@ +config = $config ?: new Config(); + Logger::setTimezone($this->config->getTimezone()); + } + + public function setConfig(Config $config) { + $this->config = $config; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/Client.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/Client.php new file mode 100644 index 0000000000..0548d5c6e7 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/Client.php @@ -0,0 +1,719 @@ +request_headers = $headers; + } + + /** + * Perform the HTTP request. + * + * @param string $url URL + * + * @return Client + */ + public function execute($url = '') + { + if ($url !== '') { + $this->url = $url; + } + + Logger::setMessage(get_called_class().' Fetch URL: '.$this->url); + Logger::setMessage(get_called_class().' Etag provided: '.$this->etag); + Logger::setMessage(get_called_class().' Last-Modified provided: '.$this->last_modified); + + $response = $this->doRequest(); + + $this->status_code = $response['status']; + $this->handleNotModifiedResponse($response); + $this->handleErrorResponse($response); + $this->handleNormalResponse($response); + + $this->expiration = $this->parseExpiration($response['headers']); + Logger::setMessage(get_called_class().' Expiration: '.$this->expiration->format(DATE_ISO8601)); + + return $this; + } + + /** + * Handle not modified response. + * + * @param array $response Client response + */ + protected function handleNotModifiedResponse(array $response) + { + if ($response['status'] == 304) { + $this->is_modified = false; + } elseif ($response['status'] == 200) { + $this->is_modified = $this->hasBeenModified($response, $this->etag, $this->last_modified); + $this->etag = $this->getHeader($response, 'ETag'); + $this->last_modified = $this->getHeader($response, 'Last-Modified'); + } + + if ($this->is_modified === false) { + Logger::setMessage(get_called_class().' Resource not modified'); + } + } + + /** + * Handle Http Error codes + * + * @param array $response Client response + * @throws ForbiddenException + * @throws InvalidUrlException + * @throws UnauthorizedException + */ + protected function handleErrorResponse(array $response) + { + $status = $response['status']; + if ($status == 401) { + throw new UnauthorizedException('Wrong or missing credentials'); + } else if ($status == 403) { + throw new ForbiddenException('Not allowed to access resource'); + } else if ($status == 404) { + throw new InvalidUrlException('Resource not found'); + } + } + + /** + * Handle normal response. + * + * @param array $response Client response + */ + protected function handleNormalResponse(array $response) + { + if ($response['status'] == 200) { + $this->content = $response['body']; + $this->content_type = $this->findContentType($response); + $this->encoding = $this->findCharset(); + } + } + + /** + * Check if a request has been modified according to the parameters. + * + * @param array $response + * @param string $etag + * @param string $lastModified + * + * @return bool + */ + private function hasBeenModified($response, $etag, $lastModified) + { + $headers = array( + 'Etag' => $etag, + 'Last-Modified' => $lastModified, + ); + + // Compare the values for each header that is present + $presentCacheHeaderCount = 0; + foreach ($headers as $key => $value) { + if (isset($response['headers'][$key])) { + if ($response['headers'][$key] !== $value) { + return true; + } + ++$presentCacheHeaderCount; + } + } + + // If at least one header is present and the values match, the response + // was not modified + if ($presentCacheHeaderCount > 0) { + return false; + } + + return true; + } + + /** + * Find content type from response headers. + * + * @param array $response Client response + * + * @return string + */ + public function findContentType(array $response) + { + return strtolower($this->getHeader($response, 'Content-Type')); + } + + /** + * Find charset from response headers. + * + * @return string + */ + public function findCharset() + { + $result = explode('charset=', $this->content_type); + + return isset($result[1]) ? $result[1] : ''; + } + + /** + * Get header value from a client response. + * + * @param array $response Client response + * @param string $header Header name + * + * @return string + */ + public function getHeader(array $response, $header) + { + return isset($response['headers'][$header]) ? $response['headers'][$header] : ''; + } + + /** + * Set the Last-Modified HTTP header. + * + * @param string $last_modified Header value + * + * @return \PicoFeed\Client\Client + */ + public function setLastModified($last_modified) + { + $this->last_modified = $last_modified; + + return $this; + } + + /** + * Get the value of the Last-Modified HTTP header. + * + * @return string + */ + public function getLastModified() + { + return $this->last_modified; + } + + /** + * Set the value of the Etag HTTP header. + * + * @param string $etag Etag HTTP header value + * + * @return \PicoFeed\Client\Client + */ + public function setEtag($etag) + { + $this->etag = $etag; + + return $this; + } + + /** + * Get the Etag HTTP header value. + * + * @return string + */ + public function getEtag() + { + return $this->etag; + } + + /** + * Get the final url value. + * + * @return string + */ + public function getUrl() + { + return $this->url; + } + + /** + * Set the url. + * + * @param $url + * @return string + */ + public function setUrl($url) + { + $this->url = $url; + return $this; + } + + /** + * Get the HTTP response status code. + * + * @return int + */ + public function getStatusCode() + { + return $this->status_code; + } + + /** + * Get the body of the HTTP response. + * + * @return string + */ + public function getContent() + { + return $this->content; + } + + /** + * Get the content type value from HTTP headers. + * + * @return string + */ + public function getContentType() + { + return $this->content_type; + } + + /** + * Get the encoding value from HTTP headers. + * + * @return string + */ + public function getEncoding() + { + return $this->encoding; + } + + /** + * Return true if the remote resource has changed. + * + * @return bool + */ + public function isModified() + { + return $this->is_modified; + } + + /** + * return true if passthrough mode is enabled. + * + * @return bool + */ + public function isPassthroughEnabled() + { + return $this->passthrough; + } + + /** + * Set connection timeout. + * + * @param int $timeout Connection timeout + * + * @return \PicoFeed\Client\Client + */ + public function setTimeout($timeout) + { + $this->timeout = $timeout ?: $this->timeout; + + return $this; + } + + /** + * Set a custom user agent. + * + * @param string $user_agent User Agent + * + * @return \PicoFeed\Client\Client + */ + public function setUserAgent($user_agent) + { + $this->user_agent = $user_agent ?: $this->user_agent; + + return $this; + } + + /** + * Set the maximum number of HTTP redirections. + * + * @param int $max Maximum + * + * @return \PicoFeed\Client\Client + */ + public function setMaxRedirections($max) + { + $this->max_redirects = $max ?: $this->max_redirects; + + return $this; + } + + /** + * Set the maximum size of the HTTP body. + * + * @param int $max Maximum + * + * @return \PicoFeed\Client\Client + */ + public function setMaxBodySize($max) + { + $this->max_body_size = $max ?: $this->max_body_size; + + return $this; + } + + /** + * Set the proxy hostname. + * + * @param string $hostname Proxy hostname + * + * @return \PicoFeed\Client\Client + */ + public function setProxyHostname($hostname) + { + $this->proxy_hostname = $hostname ?: $this->proxy_hostname; + + return $this; + } + + /** + * Set the proxy port. + * + * @param int $port Proxy port + * + * @return \PicoFeed\Client\Client + */ + public function setProxyPort($port) + { + $this->proxy_port = $port ?: $this->proxy_port; + + return $this; + } + + /** + * Set the proxy username. + * + * @param string $username Proxy username + * + * @return \PicoFeed\Client\Client + */ + public function setProxyUsername($username) + { + $this->proxy_username = $username ?: $this->proxy_username; + + return $this; + } + + /** + * Set the proxy password. + * + * @param string $password Password + * + * @return \PicoFeed\Client\Client + */ + public function setProxyPassword($password) + { + $this->proxy_password = $password ?: $this->proxy_password; + + return $this; + } + + /** + * Set the username. + * + * @param string $username Basic Auth username + * + * @return \PicoFeed\Client\Client + */ + public function setUsername($username) + { + $this->username = $username ?: $this->username; + + return $this; + } + + /** + * Set the password. + * + * @param string $password Basic Auth Password + * + * @return \PicoFeed\Client\Client + */ + public function setPassword($password) + { + $this->password = $password ?: $this->password; + + return $this; + } + + /** + * Enable the passthrough mode. + * + * @return \PicoFeed\Client\Client + */ + public function enablePassthroughMode() + { + $this->passthrough = true; + + return $this; + } + + /** + * Disable the passthrough mode. + * + * @return \PicoFeed\Client\Client + */ + public function disablePassthroughMode() + { + $this->passthrough = false; + + return $this; + } + + /** + * Set config object. + * + * @param \PicoFeed\Config\Config $config Config instance + * + * @return \PicoFeed\Client\Client + */ + public function setConfig(Config $config) + { + if ($config !== null) { + $this->setTimeout($config->getClientTimeout()); + $this->setUserAgent($config->getClientUserAgent()); + $this->setMaxRedirections($config->getMaxRedirections()); + $this->setMaxBodySize($config->getMaxBodySize()); + $this->setProxyHostname($config->getProxyHostname()); + $this->setProxyPort($config->getProxyPort()); + $this->setProxyUsername($config->getProxyUsername()); + $this->setProxyPassword($config->getProxyPassword()); + } + + return $this; + } + + /** + * Return true if the HTTP status code is a redirection + * + * @access protected + * @param integer $code + * @return boolean + */ + public function isRedirection($code) + { + return $code == 301 || $code == 302 || $code == 303 || $code == 307; + } + + public function parseExpiration(HttpHeaders $headers) + { + try { + + if (isset($headers['Cache-Control'])) { + if (preg_match('/s-maxage=(\d+)/', $headers['Cache-Control'], $matches)) { + return new DateTime('+' . $matches[1] . ' seconds'); + } else if (preg_match('/max-age=(\d+)/', $headers['Cache-Control'], $matches)) { + return new DateTime('+' . $matches[1] . ' seconds'); + } + } + + if (! empty($headers['Expires'])) { + return new DateTime($headers['Expires']); + } + } catch (Exception $e) { + Logger::setMessage('Unable to parse expiration date: '.$e->getMessage()); + } + + return new DateTime(); + } + + /** + * Get expiration date time from "Expires" or "Cache-Control" headers + * + * @return DateTime + */ + public function getExpiration() + { + return $this->expiration ?: new DateTime(); + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/ClientException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/ClientException.php new file mode 100644 index 0000000000..b3a95c9f7d --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/ClientException.php @@ -0,0 +1,14 @@ +body_length += $length; + + if ($this->body_length > $this->max_body_size) { + return -1; + } + + $this->body .= $buffer; + + return $length; + } + + /** + * cURL callback to read HTTP headers. + * + * @param resource $ch cURL handler + * @param string $buffer Header line + * + * @return int Length of the buffer + */ + public function readHeaders($ch, $buffer) + { + $length = strlen($buffer); + + if ($buffer === "\r\n" || $buffer === "\n") { + ++$this->response_headers_count; + } else { + if (!isset($this->response_headers[$this->response_headers_count])) { + $this->response_headers[$this->response_headers_count] = ''; + } + + $this->response_headers[$this->response_headers_count] .= $buffer; + } + + return $length; + } + + /** + * cURL callback to passthrough the HTTP body to the client. + * + * If the function return -1, curl stop to read the HTTP response + * + * @param resource $ch cURL handler + * @param string $buffer Chunk of data + * + * @return int Length of the buffer + */ + public function passthroughBody($ch, $buffer) + { + // do it only at the beginning of a transmission + if ($this->body_length === 0) { + list($status, $headers) = HttpHeaders::parse(explode("\n", $this->response_headers[$this->response_headers_count - 1])); + + if ($this->isRedirection($status)) { + return $this->handleRedirection($headers['Location']); + } + + // Do not work with PHP-FPM + if (strpos(PHP_SAPI, 'cgi') !== false) { + header(':', true, $status); + } + + if (isset($headers['Content-Type'])) { + header('Content-Type:' .$headers['Content-Type']); + } + } + + $length = strlen($buffer); + $this->body_length += $length; + + echo $buffer; + + return $length; + } + + /** + * Prepare HTTP headers. + * + * @return string[] + */ + private function prepareHeaders() + { + $headers = array( + 'Connection: close', + ); + + if ($this->etag) { + $headers[] = 'If-None-Match: '.$this->etag; + $headers[] = 'A-IM: feed'; + } + + if ($this->last_modified) { + $headers[] = 'If-Modified-Since: '.$this->last_modified; + } + + $headers = array_merge($headers, $this->request_headers); + + return $headers; + } + + /** + * Prepare curl proxy context. + * + * @param resource $ch + * + * @return resource $ch + */ + private function prepareProxyContext($ch) + { + if ($this->proxy_hostname) { + Logger::setMessage(get_called_class().' Proxy: '.$this->proxy_hostname.':'.$this->proxy_port); + + curl_setopt($ch, CURLOPT_PROXYPORT, $this->proxy_port); + curl_setopt($ch, CURLOPT_PROXYTYPE, 'HTTP'); + curl_setopt($ch, CURLOPT_PROXY, $this->proxy_hostname); + + if ($this->proxy_username) { + Logger::setMessage(get_called_class().' Proxy credentials: Yes'); + curl_setopt($ch, CURLOPT_PROXYUSERPWD, $this->proxy_username.':'.$this->proxy_password); + } else { + Logger::setMessage(get_called_class().' Proxy credentials: No'); + } + } + + return $ch; + } + + /** + * Prepare curl auth context. + * + * @param resource $ch + * + * @return resource $ch + */ + private function prepareAuthContext($ch) + { + if ($this->username && $this->password) { + curl_setopt($ch, CURLOPT_USERPWD, $this->username.':'.$this->password); + } + + return $ch; + } + + /** + * Set write/header functions. + * + * @param resource $ch + * + * @return resource $ch + */ + private function prepareDownloadMode($ch) + { + $this->body = ''; + $this->response_headers = array(); + $this->response_headers_count = 0; + $write_function = 'readBody'; + $header_function = 'readHeaders'; + + if ($this->isPassthroughEnabled()) { + $write_function = 'passthroughBody'; + } + + curl_setopt($ch, CURLOPT_WRITEFUNCTION, array($this, $write_function)); + curl_setopt($ch, CURLOPT_HEADERFUNCTION, array($this, $header_function)); + + return $ch; + } + + /** + * Prepare curl context. + * + * @return resource + */ + private function prepareContext() + { + $ch = curl_init(); + + curl_setopt($ch, CURLOPT_URL, $this->url); + curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->timeout); + curl_setopt($ch, CURLOPT_USERAGENT, $this->user_agent); + curl_setopt($ch, CURLOPT_HTTPHEADER, $this->prepareHeaders()); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false); + curl_setopt($ch, CURLOPT_ENCODING, ''); + curl_setopt($ch, CURLOPT_COOKIEJAR, 'php://memory'); + curl_setopt($ch, CURLOPT_COOKIEFILE, 'php://memory'); + + // Disable SSLv3 by enforcing TLSv1.x for curl >= 7.34.0 and < 7.39.0. + // Versions prior to 7.34 and at least when compiled against openssl + // interpret this parameter as "limit to TLSv1.0" which fails for sites + // which enforce TLS 1.1+. + // Starting with curl 7.39.0 SSLv3 is disabled by default. + $version = curl_version(); + if ($version['version_number'] >= 467456 && $version['version_number'] < 468736) { + curl_setopt($ch, CURLOPT_SSLVERSION, 1); + } + + $ch = $this->prepareDownloadMode($ch); + $ch = $this->prepareProxyContext($ch); + $ch = $this->prepareAuthContext($ch); + + return $ch; + } + + /** + * Execute curl context. + */ + private function executeContext() + { + $ch = $this->prepareContext(); + curl_exec($ch); + + Logger::setMessage(get_called_class().' cURL total time: '.curl_getinfo($ch, CURLINFO_TOTAL_TIME)); + Logger::setMessage(get_called_class().' cURL dns lookup time: '.curl_getinfo($ch, CURLINFO_NAMELOOKUP_TIME)); + Logger::setMessage(get_called_class().' cURL connect time: '.curl_getinfo($ch, CURLINFO_CONNECT_TIME)); + Logger::setMessage(get_called_class().' cURL speed download: '.curl_getinfo($ch, CURLINFO_SPEED_DOWNLOAD)); + Logger::setMessage(get_called_class().' cURL effective url: '.curl_getinfo($ch, CURLINFO_EFFECTIVE_URL)); + + $curl_errno = curl_errno($ch); + + if ($curl_errno) { + Logger::setMessage(get_called_class().' cURL error: '.curl_error($ch)); + curl_close($ch); + + $this->handleError($curl_errno); + } + + // Update the url if there where redirects + $this->url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL); + + curl_close($ch); + } + + /** + * Do the HTTP request. + * + * @return array HTTP response ['body' => ..., 'status' => ..., 'headers' => ...] + */ + public function doRequest() + { + $this->executeContext(); + + list($status, $headers) = HttpHeaders::parse(explode("\n", $this->response_headers[$this->response_headers_count - 1])); + + if ($this->isRedirection($status)) { + if (empty($headers['Location'])) { + $status = 200; + } else { + return $this->handleRedirection($headers['Location']); + } + } + + return array( + 'status' => $status, + 'body' => $this->body, + 'headers' => $headers, + ); + } + + /** + * Handle HTTP redirects + * + * @param string $location Redirected URL + * @return array + * @throws MaxRedirectException + */ + private function handleRedirection($location) + { + $result = array(); + $this->url = Url::resolve($location, $this->url); + $this->body = ''; + $this->body_length = 0; + $this->response_headers = array(); + $this->response_headers_count = 0; + + while (true) { + $this->nbRedirects++; + + if ($this->nbRedirects >= $this->max_redirects) { + throw new MaxRedirectException('Maximum number of redirections reached'); + } + + $result = $this->doRequest(); + + if ($this->isRedirection($result['status'])) { + $this->url = Url::resolve($result['headers']['Location'], $this->url); + $this->body = ''; + $this->body_length = 0; + $this->response_headers = array(); + $this->response_headers_count = 0; + } else { + break; + } + } + + return $result; + } + + /** + * Handle cURL errors (throw individual exceptions). + * + * We don't use constants because they are not necessary always available + * (depends of the version of libcurl linked to php) + * + * @see http://curl.haxx.se/libcurl/c/libcurl-errors.html + * + * @param int $errno cURL error code + * @throws InvalidCertificateException + * @throws InvalidUrlException + * @throws MaxRedirectException + * @throws MaxSizeException + * @throws TimeoutException + */ + private function handleError($errno) + { + switch ($errno) { + case 78: // CURLE_REMOTE_FILE_NOT_FOUND + throw new InvalidUrlException('Resource not found', $errno); + case 6: // CURLE_COULDNT_RESOLVE_HOST + throw new InvalidUrlException('Unable to resolve hostname', $errno); + case 7: // CURLE_COULDNT_CONNECT + throw new InvalidUrlException('Unable to connect to the remote host', $errno); + case 23: // CURLE_WRITE_ERROR + throw new MaxSizeException('Maximum response size exceeded', $errno); + case 28: // CURLE_OPERATION_TIMEDOUT + throw new TimeoutException('Operation timeout', $errno); + case 35: // CURLE_SSL_CONNECT_ERROR + case 51: // CURLE_PEER_FAILED_VERIFICATION + case 58: // CURLE_SSL_CERTPROBLEM + case 60: // CURLE_SSL_CACERT + case 59: // CURLE_SSL_CIPHER + case 64: // CURLE_USE_SSL_FAILED + case 66: // CURLE_SSL_ENGINE_INITFAILED + case 77: // CURLE_SSL_CACERT_BADFILE + case 83: // CURLE_SSL_ISSUER_ERROR + $msg = 'Invalid SSL certificate caused by CURL error number ' . $errno; + throw new InvalidCertificateException($msg, $errno); + case 47: // CURLE_TOO_MANY_REDIRECTS + throw new MaxRedirectException('Maximum number of redirections reached', $errno); + case 63: // CURLE_FILESIZE_EXCEEDED + throw new MaxSizeException('Maximum response size exceeded', $errno); + default: + throw new InvalidUrlException('Unable to fetch the URL', $errno); + } + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/ForbiddenException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/ForbiddenException.php new file mode 100644 index 0000000000..c226e95a30 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/ForbiddenException.php @@ -0,0 +1,10 @@ + $value) { + $this->headers[strtolower($key)] = $value; + } + } + + public function offsetGet($offset) + { + return $this->offsetExists($offset) ? $this->headers[strtolower($offset)] : ''; + } + + public function offsetSet($offset, $value) + { + $this->headers[strtolower($offset)] = $value; + } + + public function offsetExists($offset) + { + return isset($this->headers[strtolower($offset)]); + } + + public function offsetUnset($offset) + { + unset($this->headers[strtolower($offset)]); + } + + /** + * Parse HTTP headers. + * + * @static + * + * @param array $lines List of headers + * + * @return array + */ + public static function parse(array $lines) + { + $status = 0; + $headers = array(); + + foreach ($lines as $line) { + if (strpos($line, 'HTTP/1') === 0) { + $headers = array(); + $status = (int) substr($line, 9, 3); + } elseif (strpos($line, ': ') !== false) { + list($name, $value) = explode(': ', $line); + if ($value) { + $headers[trim($name)] = trim($value); + } + } + } + + Logger::setMessage(get_called_class().' HTTP status code: '.$status); + + foreach ($headers as $name => $value) { + Logger::setMessage(get_called_class().' HTTP header: '.$name.' => '.$value); + } + + return array($status, new self($headers)); + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/InvalidCertificateException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/InvalidCertificateException.php new file mode 100644 index 0000000000..8d25d7e40b --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/InvalidCertificateException.php @@ -0,0 +1,12 @@ +user_agent, + ); + + // disable compression in passthrough mode. It could result in double + // compressed content which isn't decodeable by browsers + if (function_exists('gzdecode') && !$this->isPassthroughEnabled()) { + $headers[] = 'Accept-Encoding: gzip'; + } + + if ($this->etag) { + $headers[] = 'If-None-Match: '.$this->etag; + $headers[] = 'A-IM: feed'; + } + + if ($this->last_modified) { + $headers[] = 'If-Modified-Since: '.$this->last_modified; + } + + if ($this->proxy_username) { + $headers[] = 'Proxy-Authorization: Basic '.base64_encode($this->proxy_username.':'.$this->proxy_password); + } + + if ($this->username && $this->password) { + $headers[] = 'Authorization: Basic '.base64_encode($this->username.':'.$this->password); + } + + $headers = array_merge($headers, $this->request_headers); + + return $headers; + } + + /** + * Construct the final URL from location headers. + * + * @param array $headers List of HTTP response header + */ + private function setEffectiveUrl($headers) + { + foreach ($headers as $header) { + if (stripos($header, 'Location') === 0) { + list(, $value) = explode(': ', $header); + + $this->url = Url::resolve($value, $this->url); + } + } + } + + /** + * Prepare stream context. + * + * @return array + */ + private function prepareContext() + { + $context = array( + 'http' => array( + 'method' => 'GET', + 'protocol_version' => 1.1, + 'timeout' => $this->timeout, + 'max_redirects' => $this->max_redirects, + ), + ); + + if ($this->proxy_hostname) { + Logger::setMessage(get_called_class().' Proxy: '.$this->proxy_hostname.':'.$this->proxy_port); + + $context['http']['proxy'] = 'tcp://'.$this->proxy_hostname.':'.$this->proxy_port; + $context['http']['request_fulluri'] = true; + + if ($this->proxy_username) { + Logger::setMessage(get_called_class().' Proxy credentials: Yes'); + } else { + Logger::setMessage(get_called_class().' Proxy credentials: No'); + } + } + + $context['http']['header'] = implode("\r\n", $this->prepareHeaders()); + + return $context; + } + + /** + * Do the HTTP request. + * + * @return array HTTP response ['body' => ..., 'status' => ..., 'headers' => ...] + * @throws InvalidUrlException + * @throws MaxSizeException + * @throws TimeoutException + */ + public function doRequest() + { + $body = ''; + + // Create context + $context = stream_context_create($this->prepareContext()); + + // Make HTTP request + $stream = @fopen($this->url, 'r', false, $context); + if (!is_resource($stream)) { + throw new InvalidUrlException('Unable to establish a connection'); + } + + // Get HTTP headers response + $metadata = stream_get_meta_data($stream); + list($status, $headers) = HttpHeaders::parse($metadata['wrapper_data']); + + if ($this->isPassthroughEnabled()) { + header(':', true, $status); + + if (isset($headers['Content-Type'])) { + header('Content-Type: '.$headers['Content-Type']); + } + + fpassthru($stream); + } else { + // Get the entire body until the max size + $body = stream_get_contents($stream, $this->max_body_size + 1); + + // If the body size is too large abort everything + if (strlen($body) > $this->max_body_size) { + throw new MaxSizeException('Content size too large'); + } + + if ($metadata['timed_out']) { + throw new TimeoutException('Operation timeout'); + } + } + + fclose($stream); + + $this->setEffectiveUrl($metadata['wrapper_data']); + + return array( + 'status' => $status, + 'body' => $this->decodeBody($body, $headers), + 'headers' => $headers, + ); + } + + /** + * Decode body response according to the HTTP headers. + * + * @param string $body Raw body + * @param HttpHeaders $headers HTTP headers + * + * @return string + */ + public function decodeBody($body, HttpHeaders $headers) + { + if (isset($headers['Transfer-Encoding']) && $headers['Transfer-Encoding'] === 'chunked') { + $body = $this->decodeChunked($body); + } + + if (isset($headers['Content-Encoding']) && $headers['Content-Encoding'] === 'gzip') { + $body = gzdecode($body); + } + + return $body; + } + + /** + * Decode a chunked body. + * + * @param string $str Raw body + * + * @return string Decoded body + */ + public function decodeChunked($str) + { + for ($result = ''; !empty($str); $str = trim($str)) { + + // Get the chunk length + $pos = strpos($str, "\r\n"); + $len = hexdec(substr($str, 0, $pos)); + + // Append the chunk to the result + $result .= substr($str, $pos + 2, $len); + $str = substr($str, $pos + 2 + $len); + } + + return $result; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/TimeoutException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/TimeoutException.php new file mode 100644 index 0000000000..da98da12e7 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/TimeoutException.php @@ -0,0 +1,12 @@ +url = $url; + $this->components = parse_url($url) ?: array(); + + // Issue with PHP < 5.4.7 and protocol relative url + if (version_compare(PHP_VERSION, '5.4.7', '<') && $this->isProtocolRelative()) { + $pos = strpos($this->components['path'], '/', 2); + + if ($pos === false) { + $pos = strlen($this->components['path']); + } + + $this->components['host'] = substr($this->components['path'], 2, $pos - 2); + $this->components['path'] = substr($this->components['path'], $pos); + } + } + + /** + * Shortcut method to get an absolute url from relative url. + * + * @static + * + * @param mixed $item_url Unknown url (can be relative or not) + * @param mixed $website_url Website url + * + * @return string + */ + public static function resolve($item_url, $website_url) + { + $link = is_string($item_url) ? new self($item_url) : $item_url; + $website = is_string($website_url) ? new self($website_url) : $website_url; + + if ($link->isRelativeUrl()) { + if ($link->isRelativePath()) { + return $link->getAbsoluteUrl($website->getBaseUrl($website->getBasePath())); + } + + return $link->getAbsoluteUrl($website->getBaseUrl()); + } elseif ($link->isProtocolRelative()) { + $link->setScheme($website->getScheme()); + } + + return $link->getAbsoluteUrl(); + } + + /** + * Shortcut method to get a base url. + * + * @static + * + * @param string $url + * + * @return string + */ + public static function base($url) + { + $link = new self($url); + + return $link->getBaseUrl(); + } + + /** + * Get the base URL. + * + * @param string $suffix Add a suffix to the url + * + * @return string + */ + public function getBaseUrl($suffix = '') + { + return $this->hasHost() ? $this->getScheme('://').$this->getHost().$this->getPort(':').$suffix : ''; + } + + /** + * Get the absolute URL. + * + * @param string $base_url Use this url as base url + * + * @return string + */ + public function getAbsoluteUrl($base_url = '') + { + if ($base_url) { + $base = new self($base_url); + $url = $base->getAbsoluteUrl().substr($this->getFullPath(), 1); + } else { + $url = $this->hasHost() ? $this->getBaseUrl().$this->getFullPath() : ''; + } + + return $url; + } + + /** + * Return true if the url is relative. + * + * @return bool + */ + public function isRelativeUrl() + { + return !$this->hasScheme() && !$this->isProtocolRelative(); + } + + /** + * Return true if the path is relative. + * + * @return bool + */ + public function isRelativePath() + { + $path = $this->getPath(); + + return empty($path) || $path{0} + !== '/'; + } + + /** + * Filters the path of a URI. + * + * Imported from Guzzle library: https://github.com/guzzle/psr7/blob/master/src/Uri.php#L568-L582 + * + * @param $path + * + * @return string + */ + public function filterPath($path, $charUnreserved = 'a-zA-Z0-9_\-\.~', $charSubDelims = '!\$&\'\(\)\*\+,;=') + { + return preg_replace_callback( + '/(?:[^'.$charUnreserved.$charSubDelims.':@\/%]+|%(?![A-Fa-f0-9]{2}))/', + function (array $matches) { return rawurlencode($matches[0]); }, + $path + ); + } + + /** + * Get the path. + * + * @return string + */ + public function getPath() + { + return $this->filterPath(empty($this->components['path']) ? '' : $this->components['path']); + } + + /** + * Get the base path. + * + * @return string + */ + public function getBasePath() + { + $current_path = $this->getPath(); + + $path = $this->isRelativePath() ? '/' : ''; + $path .= substr($current_path, -1) === '/' ? $current_path : dirname($current_path); + + return preg_replace('/\\\\\/|\/\//', '/', $path.'/'); + } + + /** + * Get the full path (path + querystring + fragment). + * + * @return string + */ + public function getFullPath() + { + $path = $this->isRelativePath() ? '/' : ''; + $path .= $this->getPath(); + $path .= empty($this->components['query']) ? '' : '?'.$this->components['query']; + $path .= empty($this->components['fragment']) ? '' : '#'.$this->components['fragment']; + + return $path; + } + + /** + * Get the hostname. + * + * @return string + */ + public function getHost() + { + return empty($this->components['host']) ? '' : $this->components['host']; + } + + /** + * Return true if the url has a hostname. + * + * @return bool + */ + public function hasHost() + { + return !empty($this->components['host']); + } + + /** + * Get the scheme. + * + * @param string $suffix Suffix to add when there is a scheme + * + * @return string + */ + public function getScheme($suffix = '') + { + return ($this->hasScheme() ? $this->components['scheme'] : 'http').$suffix; + } + + /** + * Set the scheme. + * + * @param string $scheme Set a scheme + * + * @return string + */ + public function setScheme($scheme) + { + $this->components['scheme'] = $scheme; + } + + /** + * Return true if the url has a scheme. + * + * @return bool + */ + public function hasScheme() + { + return !empty($this->components['scheme']); + } + + /** + * Get the port. + * + * @param string $prefix Prefix to add when there is a port + * + * @return string + */ + public function getPort($prefix = '') + { + return $this->hasPort() ? $prefix.$this->components['port'] : ''; + } + + /** + * Return true if the url has a port. + * + * @return bool + */ + public function hasPort() + { + return !empty($this->components['port']); + } + + /** + * Return true if the url is protocol relative (start with //). + * + * @return bool + */ + public function isProtocolRelative() + { + return strpos($this->url, '//') === 0; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Encoding/Encoding.php b/vendor/miniflux/picofeed/lib/PicoFeed/Encoding/Encoding.php new file mode 100644 index 0000000000..fa0917e80d --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Encoding/Encoding.php @@ -0,0 +1,33 @@ + array('controls', 'src'), + 'video' => array('poster', 'controls', 'height', 'width', 'src'), + 'source' => array('src', 'type'), + 'dt' => array(), + 'dd' => array(), + 'dl' => array(), + 'table' => array(), + 'caption' => array(), + 'tr' => array(), + 'th' => array(), + 'td' => array(), + 'tbody' => array(), + 'thead' => array(), + 'h1' => array(), + 'h2' => array(), + 'h3' => array(), + 'h4' => array(), + 'h5' => array(), + 'h6' => array(), + 'strong' => array(), + 'em' => array(), + 'code' => array(), + 'pre' => array(), + 'blockquote' => array(), + 'p' => array(), + 'ul' => array(), + 'li' => array(), + 'ol' => array(), + 'br' => array(), + 'del' => array(), + 'a' => array('href'), + 'img' => array('src', 'title', 'alt'), + 'figure' => array(), + 'figcaption' => array(), + 'cite' => array(), + 'time' => array('datetime'), + 'abbr' => array('title'), + 'iframe' => array('width', 'height', 'frameborder', 'src', 'allowfullscreen'), + 'q' => array('cite'), + ); + + /** + * Scheme whitelist. + * + * For a complete list go to http://en.wikipedia.org/wiki/URI_scheme + * + * @var array + */ + private $scheme_whitelist = array( + 'bitcoin:', + 'callto:', + 'ed2k://', + 'facetime://', + 'feed:', + 'ftp://', + 'geo:', + 'git://', + 'http://', + 'https://', + 'irc://', + 'irc6://', + 'ircs://', + 'jabber:', + 'magnet:', + 'mailto:', + 'nntp://', + 'rtmp://', + 'sftp://', + 'sip:', + 'sips:', + 'skype:', + 'smb://', + 'sms:', + 'spotify:', + 'ssh:', + 'steam:', + 'svn://', + 'tel:', + ); + + /** + * Iframe source whitelist, everything else is ignored. + * + * @var array + */ + private $iframe_whitelist = array( + 'http://www.youtube.com', + 'https://www.youtube.com', + 'http://player.vimeo.com', + 'https://player.vimeo.com', + 'http://www.dailymotion.com', + 'https://www.dailymotion.com', + 'http://vk.com', + 'https://vk.com', + ); + + /** + * Blacklisted resources. + * + * @var array + */ + private $media_blacklist = array( + 'api.flattr.com', + 'feeds.feedburner.com', + 'share.feedsportal.com', + 'da.feedsportal.com', + 'rc.feedsportal.com', + 'rss.feedsportal.com', + 'res.feedsportal.com', + 'res1.feedsportal.com', + 'res2.feedsportal.com', + 'res3.feedsportal.com', + 'pi.feedsportal.com', + 'rss.nytimes.com', + 'feeds.wordpress.com', + 'stats.wordpress.com', + 'rss.cnn.com', + 'twitter.com/home?status=', + 'twitter.com/share', + 'twitter_icon_large.png', + 'www.facebook.com/sharer.php', + 'facebook_icon_large.png', + 'plus.google.com/share', + 'www.gstatic.com/images/icons/gplus-16.png', + 'www.gstatic.com/images/icons/gplus-32.png', + 'www.gstatic.com/images/icons/gplus-64.png', + ); + + /** + * Attributes used for external resources. + * + * @var array + */ + private $media_attributes = array( + 'src', + 'href', + 'poster', + ); + + /** + * Attributes that must be integer. + * + * @var array + */ + private $integer_attributes = array( + 'width', + 'height', + 'frameborder', + ); + + /** + * Mandatory attributes for specified tags. + * + * @var array + */ + private $required_attributes = array( + 'a' => array('href'), + 'img' => array('src'), + 'iframe' => array('src'), + 'audio' => array('src'), + 'source' => array('src'), + ); + + /** + * Add attributes to specified tags. + * + * @var array + */ + private $add_attributes = array( + 'a' => array('rel' => 'noreferrer', 'target' => '_blank'), + 'video' => array('controls' => 'true'), + ); + + /** + * List of filters to apply. + * + * @var array + */ + private $filters = array( + 'filterAllowedAttribute', + 'filterIntegerAttribute', + 'rewriteAbsoluteUrl', + 'filterIframeAttribute', + 'filterBlacklistResourceAttribute', + 'filterProtocolUrlAttribute', + 'rewriteImageProxyUrl', + 'secureIframeSrc', + 'removeYouTubeAutoplay', + ); + + /** + * Add attributes to specified tags. + * + * @var \PicoFeed\Client\Url + */ + private $website; + + /** + * Constructor. + * + * @param \PicoFeed\Client\Url $website Website url instance + */ + public function __construct(Url $website) + { + $this->website = $website; + } + + /** + * Apply filters to the attributes list. + * + * @param string $tag Tag name + * @param array $attributes Attributes dictionary + * + * @return array Filtered attributes + */ + public function filter($tag, array $attributes) + { + foreach ($attributes as $attribute => &$value) { + foreach ($this->filters as $filter) { + if (!$this->$filter($tag, $attribute, $value)) { + unset($attributes[$attribute]); + break; + } + } + } + + return $attributes; + } + + /** + * Return true if the value is allowed (remove not allowed attributes). + * + * @param string $tag Tag name + * @param string $attribute Attribute name + * @param string $value Attribute value + * + * @return bool + */ + public function filterAllowedAttribute($tag, $attribute, $value) + { + return isset($this->attribute_whitelist[$tag]) && in_array($attribute, $this->attribute_whitelist[$tag]); + } + + /** + * Return true if the value is not integer (remove attributes that should have an integer value). + * + * @param string $tag Tag name + * @param string $attribute Attribute name + * @param string $value Attribute value + * + * @return bool + */ + public function filterIntegerAttribute($tag, $attribute, $value) + { + if (in_array($attribute, $this->integer_attributes)) { + return ctype_digit($value); + } + + return true; + } + + /** + * Return true if the iframe source is allowed (remove not allowed iframe). + * + * @param string $tag Tag name + * @param string $attribute Attribute name + * @param string $value Attribute value + * + * @return bool + */ + public function filterIframeAttribute($tag, $attribute, $value) + { + if ($tag === 'iframe' && $attribute === 'src') { + foreach ($this->iframe_whitelist as $url) { + if (strpos($value, $url) === 0) { + return true; + } + } + + return false; + } + + return true; + } + + /** + * Return true if the resource is not blacklisted (remove blacklisted resource attributes). + * + * @param string $tag Tag name + * @param string $attribute Attribute name + * @param string $value Attribute value + * + * @return bool + */ + public function filterBlacklistResourceAttribute($tag, $attribute, $value) + { + if ($this->isResource($attribute) && $this->isBlacklistedMedia($value)) { + return false; + } + + return true; + } + + /** + * Convert all relative links to absolute url. + * + * @param string $tag Tag name + * @param string $attribute Attribute name + * @param string $value Attribute value + * + * @return bool + */ + public function rewriteAbsoluteUrl($tag, $attribute, &$value) + { + if ($this->isResource($attribute)) { + $value = Url::resolve($value, $this->website); + } + + return true; + } + + /** + * Turns iframes' src attribute from http to https to prevent + * mixed active content. + * + * @param string $tag Tag name + * @param array $attribute Atttributes name + * @param string $value Attribute value + * + * @return bool + */ + public function secureIframeSrc($tag, $attribute, &$value) + { + if ($tag === 'iframe' && $attribute === 'src' && strpos($value, 'http://') === 0) { + $value = substr_replace($value, 's', 4, 0); + } + + return true; + } + + /** + * Removes YouTube autoplay from iframes. + * + * @param string $tag Tag name + * @param array $attribute Atttributes name + * @param string $value Attribute value + * + * @return bool + */ + public function removeYouTubeAutoplay($tag, $attribute, &$value) + { + $regex = '%^(https://(?:www\.)?youtube.com/.*\?.*autoplay=)(1)(.*)%i'; + if ($tag === 'iframe' && $attribute === 'src' && preg_match($regex, $value)) { + $value = preg_replace($regex, '${1}0$3', $value); + } + + return true; + } + + /** + * Rewrite image url to use with a proxy. + * + * @param string $tag Tag name + * @param string $attribute Attribute name + * @param string $value Attribute value + * + * @return bool + */ + public function rewriteImageProxyUrl($tag, $attribute, &$value) + { + if ($tag === 'img' && $attribute === 'src' + && !($this->image_proxy_limit_protocol !== '' && stripos($value, $this->image_proxy_limit_protocol.':') !== 0)) { + if ($this->image_proxy_url) { + $value = sprintf($this->image_proxy_url, rawurlencode($value)); + } elseif (is_callable($this->image_proxy_callback)) { + $value = call_user_func($this->image_proxy_callback, $value); + } + } + + return true; + } + + /** + * Return true if the scheme is authorized. + * + * @param string $tag Tag name + * @param string $attribute Attribute name + * @param string $value Attribute value + * + * @return bool + */ + public function filterProtocolUrlAttribute($tag, $attribute, $value) + { + if ($this->isResource($attribute) && !$this->isAllowedProtocol($value)) { + return false; + } + + return true; + } + + /** + * Automatically add/override some attributes for specific tags. + * + * @param string $tag Tag name + * @param array $attributes Attributes list + * + * @return array + */ + public function addAttributes($tag, array $attributes) + { + if (isset($this->add_attributes[$tag])) { + $attributes += $this->add_attributes[$tag]; + } + + return $attributes; + } + + /** + * Return true if all required attributes are present. + * + * @param string $tag Tag name + * @param array $attributes Attributes list + * + * @return bool + */ + public function hasRequiredAttributes($tag, array $attributes) + { + if (isset($this->required_attributes[$tag])) { + foreach ($this->required_attributes[$tag] as $attribute) { + if (!isset($attributes[$attribute])) { + return false; + } + } + } + + return true; + } + + /** + * Check if an attribute name is an external resource. + * + * @param string $attribute Attribute name + * + * @return bool + */ + public function isResource($attribute) + { + return in_array($attribute, $this->media_attributes); + } + + /** + * Detect if the protocol is allowed or not. + * + * @param string $value Attribute value + * + * @return bool + */ + public function isAllowedProtocol($value) + { + foreach ($this->scheme_whitelist as $protocol) { + if (strpos($value, $protocol) === 0) { + return true; + } + } + + return false; + } + + /** + * Detect if an url is blacklisted. + * + * @param string $resource Attribute value (URL) + * + * @return bool + */ + public function isBlacklistedMedia($resource) + { + foreach ($this->media_blacklist as $name) { + if (strpos($resource, $name) !== false) { + return true; + } + } + + return false; + } + + /** + * Convert the attribute list to html. + * + * @param array $attributes Attributes + * + * @return string + */ + public function toHtml(array $attributes) + { + $html = array(); + + foreach ($attributes as $attribute => $value) { + $html[] = sprintf('%s="%s"', $attribute, Filter::escape($value)); + } + + return implode(' ', $html); + } + + /** + * Set whitelisted tags and attributes for each tag. + * + * @param array $values List of tags: ['video' => ['src', 'cover'], 'img' => ['src']] + * + * @return Attribute + */ + public function setWhitelistedAttributes(array $values) + { + $this->attribute_whitelist = $values ?: $this->attribute_whitelist; + + return $this; + } + + /** + * Set scheme whitelist. + * + * @param array $values List of scheme: ['http://', 'ftp://'] + * + * @return Attribute + */ + public function setSchemeWhitelist(array $values) + { + $this->scheme_whitelist = $values ?: $this->scheme_whitelist; + + return $this; + } + + /** + * Set media attributes (used to load external resources). + * + * @param array $values List of values: ['src', 'href'] + * + * @return Attribute + */ + public function setMediaAttributes(array $values) + { + $this->media_attributes = $values ?: $this->media_attributes; + + return $this; + } + + /** + * Set blacklisted external resources. + * + * @param array $values List of tags: ['http://google.com/', '...'] + * + * @return Attribute + */ + public function setMediaBlacklist(array $values) + { + $this->media_blacklist = $values ?: $this->media_blacklist; + + return $this; + } + + /** + * Set mandatory attributes for whitelisted tags. + * + * @param array $values List of tags: ['img' => 'src'] + * + * @return Attribute + */ + public function setRequiredAttributes(array $values) + { + $this->required_attributes = $values ?: $this->required_attributes; + + return $this; + } + + /** + * Set attributes to automatically to specific tags. + * + * @param array $values List of tags: ['a' => 'target="_blank"'] + * + * @return Attribute + */ + public function setAttributeOverrides(array $values) + { + $this->add_attributes = $values ?: $this->add_attributes; + + return $this; + } + + /** + * Set attributes that must be an integer. + * + * @param array $values List of tags: ['width', 'height'] + * + * @return Attribute + */ + public function setIntegerAttributes(array $values) + { + $this->integer_attributes = $values ?: $this->integer_attributes; + + return $this; + } + + /** + * Set allowed iframe resources. + * + * @param array $values List of tags: ['http://www.youtube.com'] + * + * @return Attribute + */ + public function setIframeWhitelist(array $values) + { + $this->iframe_whitelist = $values ?: $this->iframe_whitelist; + + return $this; + } + + /** + * Set image proxy URL. + * + * The original image url will be urlencoded + * + * @param string $url Proxy URL + * + * @return Attribute + */ + public function setImageProxyUrl($url) + { + $this->image_proxy_url = $url ?: $this->image_proxy_url; + + return $this; + } + + /** + * Set image proxy callback. + * + * @param \Closure $callback + * + * @return Attribute + */ + public function setImageProxyCallback($callback) + { + $this->image_proxy_callback = $callback ?: $this->image_proxy_callback; + + return $this; + } + + /** + * Set image proxy protocol restriction. + * + * @param string $value + * + * @return Attribute + */ + public function setImageProxyProtocol($value) + { + $this->image_proxy_limit_protocol = $value ?: $this->image_proxy_limit_protocol; + + return $this; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Filter.php b/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Filter.php new file mode 100644 index 0000000000..bae2aff0b6 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Filter.php @@ -0,0 +1,155 @@ +]*>\s*~i', '', $data); + } + + /** + * Remove the XML tag from a document. + * + * @static + * + * @param string $data Input data + * + * @return string + */ + public static function stripXmlTag($data) + { + if (strpos($data, '') + 2)); + } + + do { + $pos = strpos($data, '') + 2)); + } + } while ($pos !== false && $pos < 200); + + return $data; + } + + /** + * Strip head tag from the HTML content. + * + * @static + * + * @param string $data Input data + * + * @return string + */ + public static function stripHeadTags($data) + { + return preg_replace('@]*?>.*?@siu', '', $data); + } + + /** + * Trim whitespace from the begining, the end and inside a string and don't break utf-8 string. + * + * @static + * + * @param string $value Raw data + * + * @return string Normalized data + */ + public static function stripWhiteSpace($value) + { + $value = str_replace("\r", ' ', $value); + $value = str_replace("\t", ' ', $value); + $value = str_replace("\n", ' ', $value); + // $value = preg_replace('/\s+/', ' ', $value); <= break utf-8 + return trim($value); + } + + /** + * Fixes before XML parsing. + * + * @static + * + * @param string $data Raw data + * + * @return string Normalized data + */ + public static function normalizeData($data) + { + $entities = array( + '/(&#)(\d+);/m', // decimal encoded + '/(&#x)([a-f0-9]+);/mi', // hex encoded + ); + + // strip invalid XML 1.0 characters which are encoded as entities + $data = preg_replace_callback($entities, function ($matches) { + $code_point = $matches[2]; + + // convert hex entity to decimal + if (strtolower($matches[1]) === '&#x') { + $code_point = hexdec($code_point); + } + + $code_point = (int) $code_point; + + // replace invalid characters + if ($code_point < 9 + || ($code_point > 10 && $code_point < 13) + || ($code_point > 13 && $code_point < 32) + || ($code_point > 55295 && $code_point < 57344) + || ($code_point > 65533 && $code_point < 65536) + || $code_point > 1114111 + ) { + return ''; + }; + + return $matches[0]; + }, $data); + + // strip every utf-8 character than isn't in the range of valid XML 1.0 characters + return (string) preg_replace('/[^\x{0009}\x{000A}\x{000D}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]/u', '', $data); + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Html.php b/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Html.php new file mode 100644 index 0000000000..0ccc192fc6 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Html.php @@ -0,0 +1,243 @@ +config = new Config(); + $this->input = XmlParser::htmlToXml($html); + $this->output = ''; + $this->tag = new Tag($this->config); + $this->website = $website; + $this->attribute = new Attribute(new Url($website)); + } + + /** + * Set config object. + * + * @param \PicoFeed\Config\Config $config Config instance + * + * @return \PicoFeed\Filter\Html + */ + public function setConfig($config) + { + $this->config = $config; + + if ($this->config !== null) { + $this->attribute->setImageProxyCallback($this->config->getFilterImageProxyCallback()); + $this->attribute->setImageProxyUrl($this->config->getFilterImageProxyUrl()); + $this->attribute->setImageProxyProtocol($this->config->getFilterImageProxyProtocol()); + $this->attribute->setIframeWhitelist($this->config->getFilterIframeWhitelist(array())); + $this->attribute->setIntegerAttributes($this->config->getFilterIntegerAttributes(array())); + $this->attribute->setAttributeOverrides($this->config->getFilterAttributeOverrides(array())); + $this->attribute->setRequiredAttributes($this->config->getFilterRequiredAttributes(array())); + $this->attribute->setMediaBlacklist($this->config->getFilterMediaBlacklist(array())); + $this->attribute->setMediaAttributes($this->config->getFilterMediaAttributes(array())); + $this->attribute->setSchemeWhitelist($this->config->getFilterSchemeWhitelist(array())); + $this->attribute->setWhitelistedAttributes($this->config->getFilterWhitelistedTags(array())); + $this->tag->setWhitelistedTags(array_keys($this->config->getFilterWhitelistedTags(array()))); + } + + return $this; + } + + /** + * Run tags/attributes filtering. + * + * @return string + */ + public function execute() + { + $this->preFilter(); + + $parser = xml_parser_create(); + + xml_set_object($parser, $this); + xml_set_element_handler($parser, 'startTag', 'endTag'); + xml_set_character_data_handler($parser, 'dataTag'); + xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, false); + xml_parse($parser, $this->input, true); + xml_parser_free($parser); + + $this->postFilter(); + + return $this->output; + } + + /** + * Called before XML parsing. + */ + public function preFilter() + { + $this->input = $this->tag->removeBlacklistedTags($this->input); + } + + /** + * Called after XML parsing. + */ + public function postFilter() + { + $this->output = $this->tag->removeEmptyTags($this->output); + $this->output = $this->filterRules($this->output); + $this->output = $this->tag->removeMultipleBreakTags($this->output); + $this->output = trim($this->output); + } + + /** + * Called after XML parsing. + * + * @param string $content the content that should be filtered + */ + public function filterRules($content) + { + // the constructor should require a config, then this if can be removed + if ($this->config === null) { + $config = new Config(); + } else { + $config = $this->config; + } + + $loader = new RuleLoader($config); + $rules = $loader->getRules($this->website); + + $url = new Url($this->website); + $sub_url = $url->getFullPath(); + + if (isset($rules['filter'])) { + foreach ($rules['filter'] as $pattern => $rule) { + if (preg_match($pattern, $sub_url)) { + foreach ($rule as $search => $replace) { + $content = preg_replace($search, $replace, $content); + } + } + } + } + + return $content; + } + + /** + * Parse opening tag. + * + * @param resource $parser XML parser + * @param string $tag Tag name + * @param array $attributes Tag attributes + */ + public function startTag($parser, $tag, array $attributes) + { + $this->empty = true; + + if ($this->tag->isAllowed($tag, $attributes)) { + $attributes = $this->attribute->filter($tag, $attributes); + + if ($this->attribute->hasRequiredAttributes($tag, $attributes)) { + $attributes = $this->attribute->addAttributes($tag, $attributes); + + $this->output .= $this->tag->openHtmlTag($tag, $this->attribute->toHtml($attributes)); + $this->empty = false; + } + } + + $this->empty_tags[] = $this->empty; + } + + /** + * Parse closing tag. + * + * @param resource $parser XML parser + * @param string $tag Tag name + */ + public function endTag($parser, $tag) + { + if (!array_pop($this->empty_tags) && $this->tag->isAllowedTag($tag)) { + $this->output .= $this->tag->closeHtmlTag($tag); + } + } + + /** + * Parse tag content. + * + * @param resource $parser XML parser + * @param string $content Tag content + */ + public function dataTag($parser, $content) + { + // Replace   with normal space + $content = str_replace("\xc2\xa0", ' ', $content); + $this->output .= Filter::escape($content); + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Tag.php b/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Tag.php new file mode 100644 index 0000000000..84a298a73a --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Tag.php @@ -0,0 +1,218 @@ +isAllowedTag($tag) && !$this->isPixelTracker($tag, $attributes); + } + + /** + * Return the HTML opening tag. + * + * @param string $tag Tag name + * @param string $attributes Attributes converted in html + * + * @return string + */ + public function openHtmlTag($tag, $attributes = '') + { + return '<'.$tag.(empty($attributes) ? '' : ' '.$attributes).($this->isSelfClosingTag($tag) ? '/>' : '>'); + } + + /** + * Return the HTML closing tag. + * + * @param string $tag Tag name + * + * @return string + */ + public function closeHtmlTag($tag) + { + return $this->isSelfClosingTag($tag) ? '' : ''; + } + + /** + * Return true is the tag is self-closing. + * + * @param string $tag Tag name + * + * @return bool + */ + public function isSelfClosingTag($tag) + { + return $tag === 'br' || $tag === 'img'; + } + + /** + * Check if a tag is on the whitelist. + * + * @param string $tag Tag name + * + * @return bool + */ + public function isAllowedTag($tag) + { + return in_array($tag, array_merge( + $this->tag_whitelist, + array_keys($this->config->getFilterWhitelistedTags(array())) + )); + } + + /** + * Detect if an image tag is a pixel tracker. + * + * @param string $tag Tag name + * @param array $attributes Tag attributes + * + * @return bool + */ + public function isPixelTracker($tag, array $attributes) + { + return $tag === 'img' && + isset($attributes['height']) && isset($attributes['width']) && + $attributes['height'] == 1 && $attributes['width'] == 1; + } + + /** + * Remove script tags. + * + * @param string $data Input data + * + * @return string + */ + public function removeBlacklistedTags($data) + { + $dom = XmlParser::getDomDocument($data); + + if ($dom === false) { + return ''; + } + + $xpath = new DOMXpath($dom); + + $nodes = $xpath->query(implode(' | ', $this->tag_blacklist)); + + foreach ($nodes as $node) { + $node->parentNode->removeChild($node); + } + + return $dom->saveXML(); + } + + /** + * Remove empty tags. + * + * @param string $data Input data + * + * @return string + */ + public function removeEmptyTags($data) + { + return preg_replace('/<([^<\/>]*)>([\s]*?|(?R))<\/\1>/imsU', '', $data); + } + + /** + * Replace

by only one. + * + * @param string $data Input data + * + * @return string + */ + public function removeMultipleBreakTags($data) + { + return preg_replace("/(\s*)+/", '
', $data); + } + + /** + * Set whitelisted tags adn attributes for each tag. + * + * @param array $values List of tags: ['video' => ['src', 'cover'], 'img' => ['src']] + * + * @return Tag + */ + public function setWhitelistedTags(array $values) + { + $this->tag_whitelist = $values ?: $this->tag_whitelist; + + return $this; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Generator/ContentGeneratorInterface.php b/vendor/miniflux/picofeed/lib/PicoFeed/Generator/ContentGeneratorInterface.php new file mode 100644 index 0000000000..5c2f205c67 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Generator/ContentGeneratorInterface.php @@ -0,0 +1,23 @@ +extensions as $extension) { + if (substr($item->getUrl(), - strlen($extension)) === $extension) { + $item->setContent('
'.$item->getUrl().''); + return true; + } + } + + return false; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Generator/YoutubeContentGenerator.php b/vendor/miniflux/picofeed/lib/PicoFeed/Generator/YoutubeContentGenerator.php new file mode 100644 index 0000000000..198090d4ff --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Generator/YoutubeContentGenerator.php @@ -0,0 +1,67 @@ +hasNamespace('yt')) { + return $this->generateHtmlFromXml($item); + } + + return $this->generateHtmlFromUrl($item); + } + + /** + * Generate HTML + * + * @access public + * @param Item $item + * @return boolean + */ + private function generateHtmlFromXml(Item $item) + { + $videoId = $item->getTag('yt:videoId'); + + if (! empty($videoId)) { + $item->setContent(''); + return true; + } + + return false; + } + + /** + * Generate HTML from item URL + * + * @access public + * @param Item $item + * @return bool + */ + public function generateHtmlFromUrl(Item $item) + { + if (preg_match('/youtube\.com\/watch\?v=(.*)/', $item->getUrl(), $matches)) { + $item->setContent(''); + return true; + } + + return false; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Logging/Logger.php b/vendor/miniflux/picofeed/lib/PicoFeed/Logging/Logger.php new file mode 100644 index 0000000000..caec463f02 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Logging/Logger.php @@ -0,0 +1,114 @@ +format('Y-m-d H:i:s').'] '.$message; + } + } + + /** + * Get all logged messages. + * + * @static + * + * @return array + */ + public static function getMessages() + { + return self::$messages; + } + + /** + * Remove all logged messages. + * + * @static + */ + public static function deleteMessages() + { + self::$messages = array(); + } + + /** + * Set a different timezone. + * + * @static + * + * @see http://php.net/manual/en/timezones.php + * + * @param string $timezone Timezone + */ + public static function setTimeZone($timezone) + { + self::$timezone = $timezone ?: self::$timezone; + } + + /** + * Get all messages serialized into a string. + * + * @static + * + * @return string + */ + public static function toString() + { + return implode(PHP_EOL, self::$messages).PHP_EOL; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Atom.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Atom.php new file mode 100644 index 0000000000..1c570a08dd --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Atom.php @@ -0,0 +1,382 @@ + 'http://www.w3.org/2005/Atom', + ); + + /** + * Get the path to the items XML tree. + * + * @param SimpleXMLElement $xml Feed xml + * + * @return SimpleXMLElement + */ + public function getItemsTree(SimpleXMLElement $xml) + { + return XmlParser::getXPathResult($xml, 'atom:entry', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'entry'); + } + + /** + * Find the feed url. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedUrl(SimpleXMLElement $xml, Feed $feed) + { + $feed->setFeedUrl($this->getUrl($xml, 'self')); + } + + /** + * Find the site url. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findSiteUrl(SimpleXMLElement $xml, Feed $feed) + { + $feed->setSiteUrl($this->getUrl($xml, 'alternate', true)); + } + + /** + * Find the feed description. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedDescription(SimpleXMLElement $xml, Feed $feed) + { + $description = XmlParser::getXPathResult($xml, 'atom:subtitle', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'subtitle'); + + $feed->setDescription(XmlParser::getValue($description)); + } + + /** + * Find the feed logo url. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedLogo(SimpleXMLElement $xml, Feed $feed) + { + $logo = XmlParser::getXPathResult($xml, 'atom:logo', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'logo'); + + $feed->setLogo(XmlParser::getValue($logo)); + } + + /** + * Find the feed icon. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedIcon(SimpleXMLElement $xml, Feed $feed) + { + $icon = XmlParser::getXPathResult($xml, 'atom:icon', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'icon'); + + $feed->setIcon(XmlParser::getValue($icon)); + } + + /** + * Find the feed title. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedTitle(SimpleXMLElement $xml, Feed $feed) + { + $title = XmlParser::getXPathResult($xml, 'atom:title', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'title'); + + $feed->setTitle(Filter::stripWhiteSpace(XmlParser::getValue($title)) ?: $feed->getSiteUrl()); + } + + /** + * Find the feed language. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedLanguage(SimpleXMLElement $xml, Feed $feed) + { + $language = XmlParser::getXPathResult($xml, '*[not(self::atom:entry)]/@xml:lang', $this->namespaces) + ?: XmlParser::getXPathResult($xml, '@xml:lang'); + + $feed->setLanguage(XmlParser::getValue($language)); + } + + /** + * Find the feed id. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedId(SimpleXMLElement $xml, Feed $feed) + { + $id = XmlParser::getXPathResult($xml, 'atom:id', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'id'); + + $feed->setId(XmlParser::getValue($id)); + } + + /** + * Find the feed date. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedDate(SimpleXMLElement $xml, Feed $feed) + { + $updated = XmlParser::getXPathResult($xml, 'atom:updated', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'updated'); + + $feed->setDate($this->getDateParser()->getDateTime(XmlParser::getValue($updated))); + } + + /** + * Find the item published date. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemPublishedDate(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $date = XmlParser::getXPathResult($entry, 'atom:published', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'published'); + + $item->setPublishedDate(!empty($date) ? $this->getDateParser()->getDateTime((string) current($date)) : null); + } + + /** + * Find the item updated date. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemUpdatedDate(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $date = XmlParser::getXPathResult($entry, 'atom:updated', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'updated'); + + $item->setUpdatedDate(!empty($date) ? $this->getDateParser()->getDateTime((string) current($date)) : null); + } + + /** + * Find the item title. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + */ + public function findItemTitle(SimpleXMLElement $entry, Item $item) + { + $title = XmlParser::getXPathResult($entry, 'atom:title', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'title'); + + $item->setTitle(Filter::stripWhiteSpace(XmlParser::getValue($title)) ?: $item->getUrl()); + } + + /** + * Find the item author. + * + * @param SimpleXMLElement $xml Feed + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + */ + public function findItemAuthor(SimpleXMLElement $xml, SimpleXMLElement $entry, Item $item) + { + $author = XmlParser::getXPathResult($entry, 'atom:author/atom:name', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'author/name') + ?: XmlParser::getXPathResult($xml, 'atom:author/atom:name', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'author/name'); + + $item->setAuthor(XmlParser::getValue($author)); + } + + /** + * Find the item content. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + */ + public function findItemContent(SimpleXMLElement $entry, Item $item) + { + $item->setContent($this->getContent($entry)); + } + + /** + * Find the item URL. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + */ + public function findItemUrl(SimpleXMLElement $entry, Item $item) + { + $item->setUrl($this->getUrl($entry, 'alternate', true)); + } + + /** + * Genereate the item id. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemId(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $id = XmlParser::getXPathResult($entry, 'atom:id', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'id'); + + if (!empty($id)) { + $item->setId($this->generateId(XmlParser::getValue($id))); + } else { + $item->setId($this->generateId( + $item->getTitle(), $item->getUrl(), $item->getContent() + )); + } + } + + /** + * Find the item enclosure. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemEnclosure(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $enclosure = $this->findLink($entry, 'enclosure'); + + if ($enclosure) { + $item->setEnclosureUrl(Url::resolve((string) $enclosure['href'], $feed->getSiteUrl())); + $item->setEnclosureType((string) $enclosure['type']); + } + } + + /** + * Find the item language. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemLanguage(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $language = XmlParser::getXPathResult($entry, './/@xml:lang'); + $item->setLanguage(XmlParser::getValue($language) ?: $feed->getLanguage()); + } + + /** + * Find the item categories. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + * @param Feed $feed Feed object + */ + public function findItemCategories(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $categories = XmlParser::getXPathResult($entry, 'atom:category/@term', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'category/@term'); + $item->setCategoriesFromXml($categories); + } + + /** + * Get the URL from a link tag. + * + * @param SimpleXMLElement $xml XML tag + * @param string $rel Link relationship: alternate, enclosure, related, self, via + * + * @return string + */ + private function getUrl(SimpleXMLElement $xml, $rel, $fallback = false) + { + $link = $this->findLink($xml, $rel); + + if ($link) { + return (string) $link['href']; + } + + if ($fallback) { + $link = $this->findLink($xml, ''); + return $link ? (string) $link['href'] : ''; + } + + return ''; + } + + /** + * Get a link tag that match a relationship. + * + * @param SimpleXMLElement $xml XML tag + * @param string $rel Link relationship: alternate, enclosure, related, self, via + * + * @return SimpleXMLElement|null + */ + private function findLink(SimpleXMLElement $xml, $rel) + { + $links = XmlParser::getXPathResult($xml, 'atom:link', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'link'); + + foreach ($links as $link) { + if ($rel === (string) $link['rel']) { + return $link; + } + } + + return null; + } + + /** + * Get the entry content. + * + * @param SimpleXMLElement $entry XML Entry + * + * @return string + */ + private function getContent(SimpleXMLElement $entry) + { + $content = current( + XmlParser::getXPathResult($entry, 'atom:content', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'content') + ); + + if (!empty($content) && count($content->children())) { + $xml_string = ''; + + foreach ($content->children() as $child) { + $xml_string .= $child->asXML(); + } + + return $xml_string; + } elseif (trim((string) $content) !== '') { + return (string) $content; + } + + $summary = XmlParser::getXPathResult($entry, 'atom:summary', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'summary'); + + return (string) current($summary); + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/DateParser.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/DateParser.php new file mode 100644 index 0000000000..0e5b80e398 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/DateParser.php @@ -0,0 +1,128 @@ + length ]. + * + * @var array + */ + public $formats = array( + DATE_ATOM => null, + DATE_RSS => null, + DATE_COOKIE => null, + DATE_ISO8601 => null, + DATE_RFC822 => null, + DATE_RFC850 => null, + DATE_RFC1036 => null, + DATE_RFC1123 => null, + DATE_RFC2822 => null, + DATE_RFC3339 => null, + 'l, d M Y H:i:s' => null, + 'D, d M Y H:i:s' => 25, + 'D, d M Y h:i:s' => 25, + 'D M d Y H:i:s' => 24, + 'j M Y H:i:s' => 20, + 'Y-m-d H:i:s' => 19, + 'Y-m-d\TH:i:s' => 19, + 'd/m/Y H:i:s' => 19, + 'D, d M Y' => 16, + 'Y-m-d' => 10, + 'd-m-Y' => 10, + 'm-d-Y' => 10, + 'd.m.Y' => 10, + 'm.d.Y' => 10, + 'd/m/Y' => 10, + 'm/d/Y' => 10, + ); + + /** + * Try to parse all date format for broken feeds. + * + * @param string $value Original date format + * + * @return DateTime + */ + public function getDateTime($value) + { + $value = trim($value); + + foreach ($this->formats as $format => $length) { + $truncated_value = $value; + if ($length !== null) { + $truncated_value = substr($truncated_value, 0, $length); + } + + $date = $this->getValidDate($format, $truncated_value); + if ($date !== false) { + return $date; + } + } + + return $this->getCurrentDateTime(); + } + + /** + * Get a valid date from a given format. + * + * @param string $format Date format + * @param string $value Original date value + * + * @return DateTime|bool + */ + public function getValidDate($format, $value) + { + $date = DateTime::createFromFormat($format, $value, $this->getTimeZone()); + + if ($date !== false) { + $errors = DateTime::getLastErrors(); + + if ($errors['error_count'] === 0 && $errors['warning_count'] === 0) { + return $date; + } + } + + return false; + } + + /** + * Get the current datetime. + * + * @return DateTime + */ + public function getCurrentDateTime() + { + return new DateTime('now', $this->getTimeZone()); + } + + /** + * Get DateTimeZone instance + * + * @access public + * @return DateTimeZone + */ + public function getTimeZone() + { + return new DateTimeZone($this->config->getTimezone() ?: $this->timezone); + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Feed.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Feed.php new file mode 100644 index 0000000000..a56e71c38e --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Feed.php @@ -0,0 +1,315 @@ +$property.PHP_EOL; + } + + $output .= 'Feed::date = '.$this->date->format(DATE_RFC822).PHP_EOL; + $output .= 'Feed::isRTL() = '.($this->isRTL() ? 'true' : 'false').PHP_EOL; + $output .= 'Feed::items = '.count($this->items).' items'.PHP_EOL; + + foreach ($this->items as $item) { + $output .= '----'.PHP_EOL; + $output .= $item; + } + + return $output; + } + + /** + * Get title. + */ + public function getTitle() + { + return $this->title; + } + + /** + * Get description. + */ + public function getDescription() + { + return $this->description; + } + + /** + * Get the logo url. + */ + public function getLogo() + { + return $this->logo; + } + + /** + * Get the icon url. + */ + public function getIcon() + { + return $this->icon; + } + + /** + * Get feed url. + */ + public function getFeedUrl() + { + return $this->feedUrl; + } + + /** + * Get site url. + */ + public function getSiteUrl() + { + return $this->siteUrl; + } + + /** + * Get date. + */ + public function getDate() + { + return $this->date; + } + + /** + * Get language. + */ + public function getLanguage() + { + return $this->language; + } + + /** + * Get id. + */ + public function getId() + { + return $this->id; + } + + /** + * Get feed items. + */ + public function getItems() + { + return $this->items; + } + + /** + * Return true if the feed is "Right to Left". + * + * @return bool + */ + public function isRTL() + { + return Parser::isLanguageRTL($this->language); + } + + /** + * Set feed items. + * + * @param Item[] $items + * @return Feed + */ + public function setItems(array $items) + { + $this->items = $items; + return $this; + } + + /** + * Set feed id. + * + * @param string $id + * @return Feed + */ + public function setId($id) + { + $this->id = $id; + return $this; + } + + /** + * Set feed title. + * + * @param string $title + * @return Feed + */ + public function setTitle($title) + { + $this->title = $title; + return $this; + } + + /** + * Set feed description. + * + * @param string $description + * @return Feed + */ + public function setDescription($description) + { + $this->description = $description; + return $this; + } + + /** + * Set feed url. + * + * @param string $feedUrl + * @return Feed + */ + public function setFeedUrl($feedUrl) + { + $this->feedUrl = $feedUrl; + return $this; + } + + /** + * Set feed website url. + * + * @param string $siteUrl + * @return Feed + */ + public function setSiteUrl($siteUrl) + { + $this->siteUrl = $siteUrl; + return $this; + } + + /** + * Set feed date. + * + * @param \DateTime $date + * @return Feed + */ + public function setDate($date) + { + $this->date = $date; + return $this; + } + + /** + * Set feed language. + * + * @param string $language + * @return Feed + */ + public function setLanguage($language) + { + $this->language = $language; + return $this; + } + + /** + * Set feed logo. + * + * @param string $logo + * @return Feed + */ + public function setLogo($logo) + { + $this->logo = $logo; + return $this; + } + + /** + * Set feed icon. + * + * @param string $icon + * @return Feed + */ + public function setIcon($icon) + { + $this->icon = $icon; + return $this; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Item.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Item.php new file mode 100644 index 0000000000..f9581941f1 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Item.php @@ -0,0 +1,534 @@ +namespaces); + } + + /** + * Get specific XML tag or attribute value. + * + * @param string $tag Tag name (examples: guid, media:content) + * @param string $attribute Tag attribute + * + * @return array|false Tag values or error + */ + public function getTag($tag, $attribute = '') + { + if ($attribute !== '') { + $attribute = '/@'.$attribute; + } + + $query = './/'.$tag.$attribute; + $elements = XmlParser::getXPathResult($this->xml, $query, $this->namespaces); + + if ($elements === false) { // xPath error + return false; + } + + return array_map(function ($element) { return (string) $element;}, $elements); + } + + /** + * Return item information. + * + * @return string + */ + public function __toString() + { + $output = ''; + + foreach (array('id', 'title', 'url', 'language', 'author', 'enclosureUrl', 'enclosureType') as $property) { + $output .= 'Item::'.$property.' = '.$this->$property.PHP_EOL; + } + + $publishedDate = $this->publishedDate != null ? $this->publishedDate->format(DATE_RFC822) : null; + $updatedDate = $this->updatedDate != null ? $this->updatedDate->format(DATE_RFC822) : null; + + $categoryString = $this->categories != null ? implode(',', $this->categories) : null; + + $output .= 'Item::date = '.$this->date->format(DATE_RFC822).PHP_EOL; + $output .= 'Item::publishedDate = '.$publishedDate.PHP_EOL; + $output .= 'Item::updatedDate = '.$updatedDate.PHP_EOL; + $output .= 'Item::isRTL() = '.($this->isRTL() ? 'true' : 'false').PHP_EOL; + $output .= 'Item::categories = ['.$categoryString.']'.PHP_EOL; + $output .= 'Item::content = '.strlen($this->content).' bytes'.PHP_EOL; + + return $output; + } + + /** + * Get title. + * + * @return string + */ + public function getTitle() + { + return $this->title; + } + + /** + * Get URL + * + * @access public + * @return string + */ + public function getUrl() + { + return $this->url; + } + + /** + * Set URL + * + * @access public + * @param string $url + * @return Item + */ + public function setUrl($url) + { + $this->url = $url; + return $this; + } + + /** + * Get id. + * + * @return string + */ + public function getId() + { + return $this->id; + } + + /** + * Get date. + * + * @return \DateTime + */ + public function getDate() + { + return $this->date; + } + + /** + * Get published date. + * + * @return \DateTime + */ + public function getPublishedDate() + { + return $this->publishedDate; + } + + /** + * Get updated date. + * + * @return \DateTime + */ + public function getUpdatedDate() + { + return $this->updatedDate; + } + + /** + * Get content. + * + * @return string + */ + public function getContent() + { + return $this->content; + } + + /** + * Set content + * + * @access public + * @param string $value + * @return Item + */ + public function setContent($value) + { + $this->content = $value; + return $this; + } + + /** + * Get enclosure url. + * + * @return string + */ + public function getEnclosureUrl() + { + return $this->enclosureUrl; + } + + /** + * Get enclosure type. + * + * @return string + */ + public function getEnclosureType() + { + return $this->enclosureType; + } + + /** + * Get language. + * + * @return string + */ + public function getLanguage() + { + return $this->language; + } + + /** + * Get categories. + * + * @return string + */ + public function getCategories() + { + return $this->categories; + } + + /** + * Get author. + * + * @return string + */ + public function getAuthor() + { + return $this->author; + } + + /** + * Return true if the item is "Right to Left". + * + * @return bool + */ + public function isRTL() + { + return Parser::isLanguageRTL($this->language); + } + + /** + * Set item id. + * + * @param string $id + * @return Item + */ + public function setId($id) + { + $this->id = $id; + return $this; + } + + /** + * Set item title. + * + * @param string $title + * @return Item + */ + public function setTitle($title) + { + $this->title = $title; + return $this; + } + + /** + * Set author. + * + * @param string $author + * @return Item + */ + public function setAuthor($author) + { + $this->author = $author; + return $this; + } + + /** + * Set item date. + * + * @param \DateTime $date + * @return Item + */ + public function setDate($date) + { + $this->date = $date; + return $this; + } + + /** + * Set item published date. + * + * @param \DateTime $publishedDate + * @return Item + */ + public function setPublishedDate($publishedDate) + { + $this->publishedDate = $publishedDate; + return $this; + } + + /** + * Set item updated date. + * + * @param \DateTime $updatedDate + * @return Item + */ + public function setUpdatedDate($updatedDate) + { + $this->updatedDate = $updatedDate; + return $this; + } + + /** + * Set enclosure url. + * + * @param string $enclosureUrl + * @return Item + */ + public function setEnclosureUrl($enclosureUrl) + { + $this->enclosureUrl = $enclosureUrl; + return $this; + } + + /** + * Set enclosure type. + * + * @param string $enclosureType + * @return Item + */ + public function setEnclosureType($enclosureType) + { + $this->enclosureType = $enclosureType; + return $this; + } + + /** + * Set item language. + * + * @param string $language + * @return Item + */ + public function setLanguage($language) + { + $this->language = $language; + return $this; + } + + /** + * Set item categories. + * + * @param array $categories + * @return Item + */ + public function setCategories($categories) + { + $this->categories = $categories; + return $this; + } + + /** + * Set item categories from xml. + * + * @param SimpleXMLElement[] $categories + * @return Item + */ + public function setCategoriesFromXml($categories) + { + if ($categories !== false) { + $this->setCategories( + array_map( + function ($element) { + return trim((string) $element); + }, + $categories + ) + ); + } else { + $categories = array(); + } + return $this; + } + + /** + * Set raw XML. + * + * @param \SimpleXMLElement $xml + * @return Item + */ + public function setXml($xml) + { + $this->xml = $xml; + return $this; + } + + /** + * Get raw XML. + * + * @return \SimpleXMLElement + */ + public function getXml() + { + return $this->xml; + } + + /** + * Set XML namespaces. + * + * @param array $namespaces + * @return Item + */ + public function setNamespaces($namespaces) + { + $this->namespaces = $namespaces; + return $this; + } + + /** + * Get XML namespaces. + * + * @return array + */ + public function getNamespaces() + { + return $this->namespaces; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/MalformedXmlException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/MalformedXmlException.php new file mode 100644 index 0000000000..efaf0ff14a --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/MalformedXmlException.php @@ -0,0 +1,13 @@ +fallback_url = $fallback_url; + $xml_encoding = XmlParser::getEncodingFromXmlTag($content); + + // Strip XML tag to avoid multiple encoding/decoding in the next XML processing + $this->content = Filter::stripXmlTag($content); + + // Encode everything in UTF-8 + Logger::setMessage(get_called_class().': HTTP Encoding "'.$http_encoding.'" ; XML Encoding "'.$xml_encoding.'"'); + $this->content = Encoding::convert($this->content, $xml_encoding ?: $http_encoding); + + $this->itemPostProcessor = new ItemPostProcessor($this->config); + $this->itemPostProcessor->register(new ContentGeneratorProcessor($this->config)); + $this->itemPostProcessor->register(new ContentFilterProcessor($this->config)); + } + + /** + * Parse the document. + * + * @return \PicoFeed\Parser\Feed + */ + public function execute() + { + Logger::setMessage(get_called_class().': begin parsing'); + + $xml = XmlParser::getSimpleXml($this->content); + + if ($xml === false) { + Logger::setMessage(get_called_class().': Applying XML workarounds'); + $this->content = Filter::normalizeData($this->content); + $xml = XmlParser::getSimpleXml($this->content); + + if ($xml === false) { + Logger::setMessage(get_called_class().': XML parsing error'); + Logger::setMessage(XmlParser::getErrors()); + throw new MalformedXmlException('XML parsing error'); + } + } + + $this->used_namespaces = $xml->getNamespaces(true); + $xml = $this->registerSupportedNamespaces($xml); + + $feed = new Feed(); + + $this->findFeedUrl($xml, $feed); + $this->checkFeedUrl($feed); + + $this->findSiteUrl($xml, $feed); + $this->checkSiteUrl($feed); + + $this->findFeedTitle($xml, $feed); + $this->findFeedDescription($xml, $feed); + $this->findFeedLanguage($xml, $feed); + $this->findFeedId($xml, $feed); + $this->findFeedDate($xml, $feed); + $this->findFeedLogo($xml, $feed); + $this->findFeedIcon($xml, $feed); + + foreach ($this->getItemsTree($xml) as $entry) { + $entry = $this->registerSupportedNamespaces($entry); + + $item = new Item(); + $item->xml = $entry; + $item->namespaces = $this->used_namespaces; + + $this->findItemAuthor($xml, $entry, $item); + + $this->findItemUrl($entry, $item); + $this->checkItemUrl($feed, $item); + + $this->findItemTitle($entry, $item); + $this->findItemContent($entry, $item); + + // Id generation can use the item url/title/content (order is important) + $this->findItemId($entry, $item, $feed); + $this->findItemDate($entry, $item, $feed); + $this->findItemEnclosure($entry, $item, $feed); + $this->findItemLanguage($entry, $item, $feed); + $this->findItemCategories($entry, $item, $feed); + + $this->itemPostProcessor->execute($feed, $item); + $feed->items[] = $item; + } + + Logger::setMessage(get_called_class().PHP_EOL.$feed); + + return $feed; + } + + /** + * Check if the feed url is correct. + * + * @param Feed $feed Feed object + */ + public function checkFeedUrl(Feed $feed) + { + if ($feed->getFeedUrl() === '') { + $feed->feedUrl = $this->fallback_url; + } else { + $feed->feedUrl = Url::resolve($feed->getFeedUrl(), $this->fallback_url); + } + } + + /** + * Check if the site url is correct. + * + * @param Feed $feed Feed object + */ + public function checkSiteUrl(Feed $feed) + { + if ($feed->getSiteUrl() === '') { + $feed->siteUrl = Url::base($feed->getFeedUrl()); + } else { + $feed->siteUrl = Url::resolve($feed->getSiteUrl(), $this->fallback_url); + } + } + + /** + * Check if the item url is correct. + * + * @param Feed $feed Feed object + * @param Item $item Item object + */ + public function checkItemUrl(Feed $feed, Item $item) + { + $item->url = Url::resolve($item->getUrl(), $feed->getSiteUrl()); + } + + /** + * Find the item date. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemDate(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $this->findItemPublishedDate($entry, $item, $feed); + $this->findItemUpdatedDate($entry, $item, $feed); + + if ($item->getPublishedDate() === null) { + // Use the updated date if available, otherwise use the feed date + $item->setPublishedDate($item->getUpdatedDate() ?: $feed->getDate()); + } + + if ($item->getUpdatedDate() === null) { + // Use the published date as fallback + $item->setUpdatedDate($item->getPublishedDate()); + } + + // Use the most recent of published and updated dates + $item->setDate(max($item->getPublishedDate(), $item->getUpdatedDate())); + } + + /** + * Get Item Post Processor instance + * + * @access public + * @return ItemPostProcessor + */ + public function getItemPostProcessor() + { + return $this->itemPostProcessor; + } + + /** + * Get DateParser instance + * + * @access public + * @return DateParser + */ + public function getDateParser() + { + if ($this->dateParser === null) { + $this->dateParser = new DateParser($this->config); + } + + return $this->dateParser; + } + + /** + * Generate a unique id for an entry (hash all arguments). + * + * @return string + */ + public function generateId() + { + return hash($this->hash_algo, implode(func_get_args())); + } + + /** + * Return true if the given language is "Right to Left". + * + * @static + * + * @param string $language Language: fr-FR, en-US + * + * @return bool + */ + public static function isLanguageRTL($language) + { + $language = strtolower($language); + + $rtl_languages = array( + 'ar', // Arabic (ar-**) + 'fa', // Farsi (fa-**) + 'ur', // Urdu (ur-**) + 'ps', // Pashtu (ps-**) + 'syr', // Syriac (syr-**) + 'dv', // Divehi (dv-**) + 'he', // Hebrew (he-**) + 'yi', // Yiddish (yi-**) + ); + + foreach ($rtl_languages as $prefix) { + if (strpos($language, $prefix) === 0) { + return true; + } + } + + return false; + } + + /** + * Set Hash algorithm used for id generation. + * + * @param string $algo Algorithm name + * @return \PicoFeed\Parser\Parser + */ + public function setHashAlgo($algo) + { + $this->hash_algo = $algo ?: $this->hash_algo; + return $this; + } + + /** + * Set config object. + * + * @param \PicoFeed\Config\Config $config Config instance + * + * @return \PicoFeed\Parser\Parser + */ + public function setConfig($config) + { + $this->config = $config; + $this->itemPostProcessor->setConfig($config); + return $this; + } + + /** + * Enable the content grabber. + * + * @return \PicoFeed\Parser\Parser + */ + public function disableContentFiltering() + { + $this->itemPostProcessor->unregister('PicoFeed\Processor\ContentFilterProcessor'); + return $this; + } + + /** + * Enable the content grabber. + * + * @param bool $needsRuleFile true if only pages with rule files should be + * scraped + * @param null|\Closure $scraperCallback Callback function that gets called for each + * scraper execution + * + * @return \PicoFeed\Parser\Parser + */ + public function enableContentGrabber($needsRuleFile = false, $scraperCallback = null) + { + $processor = new ScraperProcessor($this->config); + + if ($needsRuleFile) { + $processor->getScraper()->disableCandidateParser(); + } + + if ($scraperCallback !== null) { + $processor->setExecutionCallback($scraperCallback); + } + + $this->itemPostProcessor->register($processor); + return $this; + } + + /** + * Set ignored URLs for the content grabber. + * + * @param array $urls URLs + * + * @return \PicoFeed\Parser\Parser + */ + public function setGrabberIgnoreUrls(array $urls) + { + $this->itemPostProcessor->getProcessor('PicoFeed\Processor\ScraperProcessor')->ignoreUrls($urls); + return $this; + } + + /** + * Register all supported namespaces to be used within an xpath query. + * + * @param SimpleXMLElement $xml Feed xml + * + * @return SimpleXMLElement + */ + public function registerSupportedNamespaces(SimpleXMLElement $xml) + { + foreach ($this->namespaces as $prefix => $ns) { + $xml->registerXPathNamespace($prefix, $ns); + } + + return $xml; + } + + +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/ParserException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/ParserException.php new file mode 100644 index 0000000000..b5fbb69901 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/ParserException.php @@ -0,0 +1,15 @@ + 'http://purl.org/rss/1.0/', + 'dc' => 'http://purl.org/dc/elements/1.1/', + 'content' => 'http://purl.org/rss/1.0/modules/content/', + 'feedburner' => 'http://rssnamespace.org/feedburner/ext/1.0', + ); + + /** + * Get the path to the items XML tree. + * + * @param SimpleXMLElement $xml Feed xml + * + * @return SimpleXMLElement + */ + public function getItemsTree(SimpleXMLElement $xml) + { + return XmlParser::getXPathResult($xml, 'rss:item', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'item') + ?: $xml->item; + } + + /** + * Find the feed url. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedUrl(SimpleXMLElement $xml, Feed $feed) + { + $feed->setFeedUrl(''); + } + + /** + * Find the site url. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findSiteUrl(SimpleXMLElement $xml, Feed $feed) + { + $value = XmlParser::getXPathResult($xml, 'rss:channel/rss:link', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'channel/link') + ?: $xml->channel->link; + + $feed->setSiteUrl(XmlParser::getValue($value)); + } + + /** + * Find the feed description. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedDescription(SimpleXMLElement $xml, Feed $feed) + { + $description = XmlParser::getXPathResult($xml, 'rss:channel/rss:description', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'channel/description') + ?: $xml->channel->description; + + $feed->setDescription(XmlParser::getValue($description)); + } + + /** + * Find the feed logo url. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedLogo(SimpleXMLElement $xml, Feed $feed) + { + $logo = XmlParser::getXPathResult($xml, 'rss:image/rss:url', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'image/url'); + + $feed->setLogo(XmlParser::getValue($logo)); + } + + /** + * Find the feed icon. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedIcon(SimpleXMLElement $xml, Feed $feed) + { + $feed->setIcon(''); + } + + /** + * Find the feed title. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedTitle(SimpleXMLElement $xml, Feed $feed) + { + $title = XmlParser::getXPathResult($xml, 'rss:channel/rss:title', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'channel/title') + ?: $xml->channel->title; + + $feed->setTitle(Filter::stripWhiteSpace(XmlParser::getValue($title)) ?: $feed->getSiteUrl()); + } + + /** + * Find the feed language. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedLanguage(SimpleXMLElement $xml, Feed $feed) + { + $language = XmlParser::getXPathResult($xml, 'rss:channel/dc:language', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'channel/dc:language', $this->namespaces); + + $feed->setLanguage(XmlParser::getValue($language)); + } + + /** + * Find the feed id. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedId(SimpleXMLElement $xml, Feed $feed) + { + $feed->setId($feed->getFeedUrl() ?: $feed->getSiteUrl()); + } + + /** + * Find the feed date. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedDate(SimpleXMLElement $xml, Feed $feed) + { + $date = XmlParser::getXPathResult($xml, 'rss:channel/dc:date', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'channel/dc:date', $this->namespaces); + + $feed->setDate($this->getDateParser()->getDateTime(XmlParser::getValue($date))); + } + + /** + * Find the item published date. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemPublishedDate(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $date = XmlParser::getXPathResult($entry, 'dc:date', $this->namespaces); + + $item->setPublishedDate(!empty($date) ? $this->getDateParser()->getDateTime(XmlParser::getValue($date)) : null); + } + + /** + * Find the item updated date. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemUpdatedDate(SimpleXMLElement $entry, Item $item, Feed $feed) + { + if ($item->publishedDate === null) { + $this->findItemPublishedDate($entry, $item, $feed); + } + $item->setUpdatedDate($item->getPublishedDate()); // No updated date in RSS 1.0 specifications + } + + /** + * Find the item title. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + */ + public function findItemTitle(SimpleXMLElement $entry, Item $item) + { + $title = XmlParser::getXPathResult($entry, 'rss:title', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'title') + ?: $entry->title; + + $item->setTitle(Filter::stripWhiteSpace(XmlParser::getValue($title)) ?: $item->getUrl()); + } + + /** + * Find the item author. + * + * @param SimpleXMLElement $xml Feed + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + */ + public function findItemAuthor(SimpleXMLElement $xml, SimpleXMLElement $entry, Item $item) + { + $author = XmlParser::getXPathResult($entry, 'dc:creator', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'rss:channel/dc:creator', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'channel/dc:creator', $this->namespaces); + + $item->setAuthor(XmlParser::getValue($author)); + } + + /** + * Find the item content. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + */ + public function findItemContent(SimpleXMLElement $entry, Item $item) + { + $content = XmlParser::getXPathResult($entry, 'content:encoded', $this->namespaces); + + if (XmlParser::getValue($content) === '') { + $content = XmlParser::getXPathResult($entry, 'rss:description', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'description') + ?: $entry->description; + } + + $item->setContent(XmlParser::getValue($content)); + } + + /** + * Find the item URL. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + */ + public function findItemUrl(SimpleXMLElement $entry, Item $item) + { + $link = XmlParser::getXPathResult($entry, 'feedburner:origLink', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'rss:link', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'link') + ?: $entry->link; + + $item->setUrl(XmlParser::getValue($link)); + } + + /** + * Genereate the item id. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemId(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $item->setId($this->generateId( + $item->getTitle(), $item->getUrl(), $item->getContent() + )); + } + + /** + * Find the item enclosure. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemEnclosure(SimpleXMLElement $entry, Item $item, Feed $feed) + { + } + + /** + * Find the item language. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemLanguage(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $language = XmlParser::getXPathResult($entry, 'dc:language', $this->namespaces); + + $item->setLanguage(XmlParser::getValue($language) ?: $feed->getLanguage()); + } + + /** + * Find the item categories. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + * @param Feed $feed Feed object + */ + public function findItemCategories(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $categories = XmlParser::getXPathResult($entry, 'dc:subject', $this->namespaces); + $item->setCategoriesFromXml($categories); + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss20.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss20.php new file mode 100644 index 0000000000..1dd3bf8c70 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss20.php @@ -0,0 +1,319 @@ + 'http://purl.org/dc/elements/1.1/', + 'content' => 'http://purl.org/rss/1.0/modules/content/', + 'feedburner' => 'http://rssnamespace.org/feedburner/ext/1.0', + 'atom' => 'http://www.w3.org/2005/Atom', + ); + + /** + * Get the path to the items XML tree. + * + * @param SimpleXMLElement $xml Feed xml + * + * @return SimpleXMLElement + */ + public function getItemsTree(SimpleXMLElement $xml) + { + return XmlParser::getXPathResult($xml, 'channel/item'); + } + + /** + * Find the feed url. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedUrl(SimpleXMLElement $xml, Feed $feed) + { + $feed->setFeedUrl(''); + } + + /** + * Find the site url. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findSiteUrl(SimpleXMLElement $xml, Feed $feed) + { + $value = XmlParser::getXPathResult($xml, 'channel/link'); + $feed->setSiteUrl(XmlParser::getValue($value)); + } + + /** + * Find the feed description. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedDescription(SimpleXMLElement $xml, Feed $feed) + { + $value = XmlParser::getXPathResult($xml, 'channel/description'); + $feed->setDescription(XmlParser::getValue($value)); + } + + /** + * Find the feed logo url. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedLogo(SimpleXMLElement $xml, Feed $feed) + { + $value = XmlParser::getXPathResult($xml, 'channel/image/url'); + $feed->setLogo(XmlParser::getValue($value)); + } + + /** + * Find the feed icon. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedIcon(SimpleXMLElement $xml, Feed $feed) + { + $feed->setIcon(''); + } + + /** + * Find the feed title. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedTitle(SimpleXMLElement $xml, Feed $feed) + { + $title = XmlParser::getXPathResult($xml, 'channel/title'); + $feed->setTitle(Filter::stripWhiteSpace(XmlParser::getValue($title)) ?: $feed->getSiteUrl()); + } + + /** + * Find the feed language. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedLanguage(SimpleXMLElement $xml, Feed $feed) + { + $value = XmlParser::getXPathResult($xml, 'channel/language'); + $feed->setLanguage(XmlParser::getValue($value)); + } + + /** + * Find the feed id. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedId(SimpleXMLElement $xml, Feed $feed) + { + $feed->setId($feed->getFeedUrl() ?: $feed->getSiteUrl()); + } + + /** + * Find the feed date. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedDate(SimpleXMLElement $xml, Feed $feed) + { + $publish_date = XmlParser::getXPathResult($xml, 'channel/pubDate'); + $update_date = XmlParser::getXPathResult($xml, 'channel/lastBuildDate'); + + $published = !empty($publish_date) ? $this->getDateParser()->getDateTime(XmlParser::getValue($publish_date)) : null; + $updated = !empty($update_date) ? $this->getDateParser()->getDateTime(XmlParser::getValue($update_date)) : null; + + if ($published === null && $updated === null) { + $feed->setDate($this->getDateParser()->getCurrentDateTime()); // We use the current date if there is no date for the feed + } elseif ($published !== null && $updated !== null) { + $feed->setDate(max($published, $updated)); // We use the most recent date between published and updated + } else { + $feed->setDate($updated ?: $published); + } + } + + /** + * Find the item published date. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemPublishedDate(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $date = XmlParser::getXPathResult($entry, 'pubDate'); + + $item->setPublishedDate(!empty($date) ? $this->getDateParser()->getDateTime(XmlParser::getValue($date)) : null); + } + + /** + * Find the item updated date. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemUpdatedDate(SimpleXMLElement $entry, Item $item, Feed $feed) + { + if ($item->publishedDate === null) { + $this->findItemPublishedDate($entry, $item, $feed); + } + $item->setUpdatedDate($item->getPublishedDate()); // No updated date in RSS 2.0 specifications + } + + /** + * Find the item title. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + */ + public function findItemTitle(SimpleXMLElement $entry, Item $item) + { + $value = XmlParser::getXPathResult($entry, 'title'); + $item->setTitle(Filter::stripWhiteSpace(XmlParser::getValue($value)) ?: $item->getUrl()); + } + + /** + * Find the item author. + * + * @param SimpleXMLElement $xml Feed + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + */ + public function findItemAuthor(SimpleXMLElement $xml, SimpleXMLElement $entry, Item $item) + { + $value = XmlParser::getXPathResult($entry, 'dc:creator', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'author') + ?: XmlParser::getXPathResult($xml, 'channel/dc:creator', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'channel/managingEditor'); + + $item->setAuthor(XmlParser::getValue($value)); + } + + /** + * Find the item content. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + */ + public function findItemContent(SimpleXMLElement $entry, Item $item) + { + $content = XmlParser::getXPathResult($entry, 'content:encoded', $this->namespaces); + + if (XmlParser::getValue($content) === '') { + $content = XmlParser::getXPathResult($entry, 'description'); + } + + $item->setContent(XmlParser::getValue($content)); + } + + /** + * Find the item URL. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + */ + public function findItemUrl(SimpleXMLElement $entry, Item $item) + { + $link = XmlParser::getXPathResult($entry, 'feedburner:origLink', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'link') + ?: XmlParser::getXPathResult($entry, 'atom:link/@href', $this->namespaces); + + if (!empty($link)) { + $item->setUrl(XmlParser::getValue($link)); + } else { + $link = XmlParser::getXPathResult($entry, 'guid'); + $link = XmlParser::getValue($link); + + if (filter_var($link, FILTER_VALIDATE_URL) !== false) { + $item->setUrl($link); + } + } + } + + /** + * Genereate the item id. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemId(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $id = XmlParser::getValue(XmlParser::getXPathResult($entry, 'guid')); + + if ($id) { + $item->setId($this->generateId($id)); + } else { + $item->setId($this->generateId( + $item->getTitle(), $item->getUrl(), $item->getContent() + )); + } + } + + /** + * Find the item enclosure. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemEnclosure(SimpleXMLElement $entry, Item $item, Feed $feed) + { + if (isset($entry->enclosure)) { + $type = XmlParser::getXPathResult($entry, 'enclosure/@type'); + $url = XmlParser::getXPathResult($entry, 'feedburner:origEnclosureLink', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'enclosure/@url'); + + $item->setEnclosureUrl(Url::resolve(XmlParser::getValue($url), $feed->getSiteUrl())); + $item->setEnclosureType(XmlParser::getValue($type)); + } + } + + /** + * Find the item language. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemLanguage(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $language = XmlParser::getXPathResult($entry, 'dc:language', $this->namespaces); + $item->setLanguage(XmlParser::getValue($language) ?: $feed->getLanguage()); + } + + /** + * Find the item categories. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + * @param Feed $feed Feed object + */ + public function findItemCategories(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $categories = XmlParser::getXPathResult($entry, 'category'); + $item->setCategoriesFromXml($categories); + } + +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss91.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss91.php new file mode 100644 index 0000000000..058fca1b51 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss91.php @@ -0,0 +1,13 @@ +childNodes->length === 0) { + return false; + } + + return $dom; + } + + /** + * Small wrapper around ZendXml to turn their exceptions into PicoFeed exceptions + * + * @static + * @access private + * @param string $input + * @param DOMDocument $dom + * @throws XmlEntityException + * @return SimpleXMLElement|DomDocument|boolean + */ + private static function scan($input, $dom = null) + { + try { + return Security::scan($input, $dom); + } catch(RuntimeException $e) { + throw new XmlEntityException($e->getMessage()); + } + } + + /** + * Load HTML document by using a DomDocument instance or return false on failure. + * + * @static + * @access public + * @param string $input XML content + * @return DOMDocument + */ + public static function getHtmlDocument($input) + { + $dom = new DomDocument(); + + if (empty($input)) { + return $dom; + } + + libxml_use_internal_errors(true); + + if (version_compare(PHP_VERSION, '5.4.0', '>=')) { + $dom->loadHTML($input, LIBXML_NONET); + } else { + $dom->loadHTML($input); + } + + return $dom; + } + + /** + * Convert a HTML document to XML. + * + * @static + * @access public + * @param string $html HTML document + * @return string + */ + public static function htmlToXml($html) + { + $dom = self::getHtmlDocument(''.$html); + return $dom->saveXML($dom->getElementsByTagName('body')->item(0)); + } + + /** + * Get XML parser errors. + * + * @static + * @access public + * @return string + */ + public static function getErrors() + { + $errors = array(); + + foreach (libxml_get_errors() as $error) { + $errors[] = sprintf('XML error: %s (Line: %d - Column: %d - Code: %d)', + $error->message, + $error->line, + $error->column, + $error->code + ); + } + + return implode(', ', $errors); + } + + /** + * Get the encoding from a xml tag. + * + * @static + * @access public + * @param string $data Input data + * @return string + */ + public static function getEncodingFromXmlTag($data) + { + $encoding = ''; + + if (strpos($data, '')); + $data = str_replace("'", '"', $data); + + $p1 = strpos($data, 'encoding='); + $p2 = strpos($data, '"', $p1 + 10); + + if ($p1 !== false && $p2 !== false) { + $encoding = substr($data, $p1 + 10, $p2 - $p1 - 10); + $encoding = strtolower($encoding); + } + } + + return $encoding; + } + + /** + * Get the charset from a meta tag. + * + * @static + * @access public + * @param string $data Input data + * @return string + */ + public static function getEncodingFromMetaTag($data) + { + $encoding = ''; + + if (preg_match('/;]+)/i', $data, $match) === 1) { + $encoding = strtolower($match[1]); + } + + return $encoding; + } + + /** + * Rewrite XPath query to use namespace-uri and local-name derived from prefix. + * + * @static + * @access public + * @param string $query XPath query + * @param array $ns Prefix to namespace URI mapping + * @return string + */ + public static function replaceXPathPrefixWithNamespaceURI($query, array $ns) + { + return preg_replace_callback('/([A-Z0-9]+):([A-Z0-9]+)/iu', function ($matches) use ($ns) { + // don't try to map the special prefix XML + if (strtolower($matches[1]) === 'xml') { + return $matches[0]; + } + + return '*[namespace-uri()="'.$ns[$matches[1]].'" and local-name()="'.$matches[2].'"]'; + }, + $query); + } + + /** + * Get the result elements of a XPath query. + * + * @static + * @access public + * @param SimpleXMLElement $xml XML element + * @param string $query XPath query + * @param array $ns Prefix to namespace URI mapping + * @return SimpleXMLElement[] + */ + public static function getXPathResult(SimpleXMLElement $xml, $query, array $ns = array()) + { + if (!empty($ns)) { + $query = static::replaceXPathPrefixWithNamespaceURI($query, $ns); + } + + return $xml->xpath($query); + } + + /** + * Get the first Xpath result or SimpleXMLElement value + * + * @static + * @access public + * @param mixed $value + * @return string + */ + public static function getValue($value) + { + $result = ''; + + if (is_array($value) && count($value) > 0) { + $result = (string) $value[0]; + } elseif (is_a($value, 'SimpleXMLElement')) { + return $result = (string) $value; + } + + return trim($result); + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/PicoFeedException.php b/vendor/miniflux/picofeed/lib/PicoFeed/PicoFeedException.php new file mode 100644 index 0000000000..2de9e4b7da --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/PicoFeedException.php @@ -0,0 +1,14 @@ +config->getContentFiltering(true)) { + $filter = Filter::html($item->getContent(), $feed->getSiteUrl()); + $filter->setConfig($this->config); + $item->setContent($filter->execute()); + } else { + Logger::setMessage(get_called_class().': Content filtering disabled'); + } + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ContentGeneratorProcessor.php b/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ContentGeneratorProcessor.php new file mode 100644 index 0000000000..49adf9ccba --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ContentGeneratorProcessor.php @@ -0,0 +1,49 @@ +generators as $generator) { + $className = '\PicoFeed\Generator\\'.ucfirst($generator).'ContentGenerator'; + $object = new $className($this->config); + + if ($object->execute($item)) { + return true; + } + } + + return false; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ItemPostProcessor.php b/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ItemPostProcessor.php new file mode 100644 index 0000000000..d2787677e3 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ItemPostProcessor.php @@ -0,0 +1,106 @@ +processors as $processor) { + if ($processor->execute($feed, $item)) { + return true; + } + } + + return false; + } + + /** + * Register a new Item post-processor + * + * @access public + * @param ItemProcessorInterface $processor + * @return ItemPostProcessor + */ + public function register(ItemProcessorInterface $processor) + { + $this->processors[get_class($processor)] = $processor; + return $this; + } + + /** + * Remove Processor instance + * + * @access public + * @param string $class + * @return ItemPostProcessor + */ + public function unregister($class) + { + if (isset($this->processors[$class])) { + unset($this->processors[$class]); + } + + return $this; + } + + /** + * Checks wheather a specific processor is registered or not + * + * @access public + * @param string $class + * @return bool + */ + public function hasProcessor($class) + { + return isset($this->processors[$class]); + } + + /** + * Get Processor instance + * + * @access public + * @param string $class + * @return ItemProcessorInterface|null + */ + public function getProcessor($class) + { + return isset($this->processors[$class]) ? $this->processors[$class] : null; + } + + public function setConfig(Config $config) + { + foreach ($this->processors as $processor) { + $processor->setConfig($config); + } + + return false; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ItemProcessorInterface.php b/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ItemProcessorInterface.php new file mode 100644 index 0000000000..5d5322624e --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ItemProcessorInterface.php @@ -0,0 +1,25 @@ +executionCallback = $executionCallback; + return $this; + } + + /** + * Execute Item Processor + * + * @access public + * @param Feed $feed + * @param Item $item + * @return bool + */ + public function execute(Feed $feed, Item $item) + { + if (!in_array($item->getUrl(), $this->ignoredUrls)) { + $scraper = $this->getScraper(); + $scraper->setUrl($item->getUrl()); + $scraper->execute(); + + if ($this->executionCallback && is_callable($this->executionCallback)) { + call_user_func($this->executionCallback, $feed, $item, $scraper); + } + + if ($scraper->hasRelevantContent()) { + $item->setContent($scraper->getFilteredContent()); + } + } + + return false; + } + + /** + * Ignore list of URLs + * + * @access public + * @param array $urls + * @return $this + */ + public function ignoreUrls(array $urls) + { + $this->ignoredUrls = $urls; + return $this; + } + + /** + * Returns Scraper instance + * + * @access public + * @return Scraper + */ + public function getScraper() + { + if ($this->scraper === null) { + $this->scraper = new Scraper($this->config); + } + + return $this->scraper; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Reader/Favicon.php b/vendor/miniflux/picofeed/lib/PicoFeed/Reader/Favicon.php new file mode 100644 index 0000000000..d4ca07db14 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Reader/Favicon.php @@ -0,0 +1,186 @@ +content; + } + + /** + * Get the icon file type (available only after the download). + * + * @return string + */ + public function getType() + { + foreach ($this->types as $type) { + if (strpos($this->content_type, $type) === 0) { + return $type; + } + } + + return 'image/x-icon'; + } + + /** + * Get data URI (http://en.wikipedia.org/wiki/Data_URI_scheme). + * + * @return string + */ + public function getDataUri() + { + if (empty($this->content)) { + return ''; + } + + return sprintf( + 'data:%s;base64,%s', + $this->getType(), + base64_encode($this->content) + ); + } + + /** + * Download and check if a resource exists. + * + * @param string $url URL + * @return \PicoFeed\Client\Client Client instance + */ + public function download($url) + { + $client = Client::getInstance(); + $client->setConfig($this->config); + + Logger::setMessage(get_called_class().' Download => '.$url); + + try { + $client->execute($url); + } catch (ClientException $e) { + Logger::setMessage(get_called_class().' Download Failed => '.$e->getMessage()); + } + + return $client; + } + + /** + * Check if a remote file exists. + * + * @param string $url URL + * @return bool + */ + public function exists($url) + { + return $this->download($url)->getContent() !== ''; + } + + /** + * Get the icon link for a website. + * + * @param string $website_link URL + * @param string $favicon_link optional URL + * @return string + */ + public function find($website_link, $favicon_link = '') + { + $website = new Url($website_link); + + if ($favicon_link !== '') { + $icons = array($favicon_link); + } else { + $icons = $this->extract($this->download($website->getBaseUrl('/'))->getContent()); + $icons[] = $website->getBaseUrl('/favicon.ico'); + } + + foreach ($icons as $icon_link) { + $icon_link = Url::resolve($icon_link, $website); + $resource = $this->download($icon_link); + $this->content = $resource->getContent(); + $this->content_type = $resource->getContentType(); + + if ($this->content !== '') { + return $icon_link; + } elseif ($favicon_link !== '') { + return $this->find($website_link); + } + } + + return ''; + } + + /** + * Extract the icon links from the HTML. + * + * @param string $html HTML + * @return array + */ + public function extract($html) + { + $icons = array(); + + if (empty($html)) { + return $icons; + } + + $dom = XmlParser::getHtmlDocument($html); + + $xpath = new DOMXpath($dom); + $elements = $xpath->query('//link[@rel="icon" or @rel="shortcut icon" or @rel="Shortcut Icon" or @rel="icon shortcut"]'); + + for ($i = 0; $i < $elements->length; ++$i) { + $icons[] = $elements->item($i)->getAttribute('href'); + } + + return $icons; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Reader/Reader.php b/vendor/miniflux/picofeed/lib/PicoFeed/Reader/Reader.php new file mode 100644 index 0000000000..769ffe931f --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Reader/Reader.php @@ -0,0 +1,190 @@ + '//feed', + 'Rss20' => '//rss[@version="2.0"]', + 'Rss92' => '//rss[@version="0.92"]', + 'Rss91' => '//rss[@version="0.91"]', + 'Rss10' => '//rdf', + ); + + /** + * Download a feed (no discovery). + * + * @param string $url Feed url + * @param string $last_modified Last modified HTTP header + * @param string $etag Etag HTTP header + * @param string $username HTTP basic auth username + * @param string $password HTTP basic auth password + * + * @return \PicoFeed\Client\Client + */ + public function download($url, $last_modified = '', $etag = '', $username = '', $password = '') + { + $url = $this->prependScheme($url); + + return Client::getInstance() + ->setConfig($this->config) + ->setLastModified($last_modified) + ->setEtag($etag) + ->setUsername($username) + ->setPassword($password) + ->execute($url); + } + + /** + * Discover and download a feed. + * + * @param string $url Feed or website url + * @param string $last_modified Last modified HTTP header + * @param string $etag Etag HTTP header + * @param string $username HTTP basic auth username + * @param string $password HTTP basic auth password + * @return Client + * @throws SubscriptionNotFoundException + */ + public function discover($url, $last_modified = '', $etag = '', $username = '', $password = '') + { + $client = $this->download($url, $last_modified, $etag, $username, $password); + + // It's already a feed or the feed was not modified + if (!$client->isModified() || $this->detectFormat($client->getContent())) { + return $client; + } + + // Try to find a subscription + $links = $this->find($client->getUrl(), $client->getContent()); + + if (empty($links)) { + throw new SubscriptionNotFoundException('Unable to find a subscription'); + } + + return $this->download($links[0], $last_modified, $etag, $username, $password); + } + + /** + * Find feed urls inside a HTML document. + * + * @param string $url Website url + * @param string $html HTML content + * + * @return array List of feed links + */ + public function find($url, $html) + { + Logger::setMessage(get_called_class().': Try to discover subscriptions'); + + $dom = XmlParser::getHtmlDocument($html); + $xpath = new DOMXPath($dom); + $links = array(); + + $queries = array( + '//link[@type="application/rss+xml"]', + '//link[@type="application/atom+xml"]', + ); + + foreach ($queries as $query) { + $nodes = $xpath->query($query); + + foreach ($nodes as $node) { + $link = $node->getAttribute('href'); + + if (!empty($link)) { + $feedUrl = new Url($link); + $siteUrl = new Url($url); + + $links[] = $feedUrl->getAbsoluteUrl($feedUrl->isRelativeUrl() ? $siteUrl->getBaseUrl() : ''); + } + } + } + + Logger::setMessage(get_called_class().': '.implode(', ', $links)); + + return $links; + } + + /** + * Get a parser instance. + * + * @param string $url Site url + * @param string $content Feed content + * @param string $encoding HTTP encoding + * + * @return \PicoFeed\Parser\Parser + */ + public function getParser($url, $content, $encoding) + { + $format = $this->detectFormat($content); + + if (empty($format)) { + throw new UnsupportedFeedFormatException('Unable to detect feed format'); + } + + $className = '\PicoFeed\Parser\\'.$format; + + $parser = new $className($content, $encoding, $url); + $parser->setHashAlgo($this->config->getParserHashAlgo()); + $parser->setConfig($this->config); + + return $parser; + } + + /** + * Detect the feed format. + * + * @param string $content Feed content + * + * @return string + */ + public function detectFormat($content) + { + $dom = XmlParser::getHtmlDocument($content); + $xpath = new DOMXPath($dom); + + foreach ($this->formats as $parser_name => $query) { + $nodes = $xpath->query($query); + + if ($nodes->length === 1) { + return $parser_name; + } + } + + return ''; + } + + /** + * Add the prefix "http://" if the end-user just enter a domain name. + * + * @param string $url Url + * @retunr string + */ + public function prependScheme($url) + { + if (!preg_match('%^https?://%', $url)) { + $url = 'http://'.$url; + } + + return $url; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Reader/ReaderException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Reader/ReaderException.php new file mode 100644 index 0000000000..4f03dbe0dc --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Reader/ReaderException.php @@ -0,0 +1,14 @@ + array( + '%.*%' => array( + 'test_url' => 'http://combat.blog.lemonde.fr/2013/08/31/teddy-riner-le-rookie-devenu-rambo/#xtor=RSS-3208', + 'body' => array( + '//div[@class="entry-content"]', + ), + 'strip' => array( + '//*[contains(@class, "fb-like") or contains(@class, "social")]' + ), + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.blogs.nytimes.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.blogs.nytimes.com.php new file mode 100644 index 0000000000..ee641b0960 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.blogs.nytimes.com.php @@ -0,0 +1,15 @@ + array( + '%.*%' => array( + 'title' => '//header/h1', + 'test_url' => 'http://bits.blogs.nytimes.com/2012/01/16/wikipedia-plans-to-go-dark-on-wednesday-to-protest-sopa/', + 'body' => array( + '//div[@class="postContent"]', + ), + 'strip' => array( + '//*[@class="shareToolsBox"]', + ), + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.igen.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.igen.fr.php new file mode 100644 index 0000000000..f2028f4e13 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.igen.fr.php @@ -0,0 +1,13 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.igen.fr/ailleurs/2014/05/nvidia-va-delaisser-les-smartphones-grand-public-86031', + 'body' => array( + '//div[contains(@class, "field-name-body")]' + ), + 'strip' => array( + ), + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.nytimes.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.nytimes.com.php new file mode 100644 index 0000000000..ed27bb5c5f --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.nytimes.com.php @@ -0,0 +1,11 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.nytimes.com/2011/05/15/world/middleeast/15prince.html', + 'body' => array( + '//div[@class="articleBody"]', + ), + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.over-blog.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.over-blog.com.php new file mode 100644 index 0000000000..cc5d83c78d --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.over-blog.com.php @@ -0,0 +1,11 @@ + array( + '%.*%' => array( + 'test_url' => 'http://eliascarpe.over-blog.com/2015/12/re-upload-projets-d-avenir.html', + 'body' => array( + '//div[contains(concat(" ", normalize-space(@class), " "), " ob-section ")]', + ), + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.phoronix.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.phoronix.com.php new file mode 100644 index 0000000000..66713f71ee --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.phoronix.com.php @@ -0,0 +1,12 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.phoronix.com/scan.php?page=article&item=amazon_ec2_bare&num=1', + 'body' => array( + '//div[@class="content"]', + ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.slate.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.slate.com.php new file mode 100644 index 0000000000..a795bca396 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.slate.com.php @@ -0,0 +1,20 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.slate.com/articles/business/moneybox/2013/08/microsoft_ceo_steve_ballmer_retires_a_firsthand_account_of_the_company_s.html', + 'body' => array( + '//div[@class="sl-art-body"]', + ), + 'strip' => array( + '//*[contains(@class, "social") or contains(@class, "comments") or contains(@class, "sl-article-floatin-tools") or contains(@class, "sl-art-pag")]', + '//*[@id="mys_slate_logged_in"]', + '//*[@id="sl_article_tools_myslate_bottom"]', + '//*[@id="mys_myslate"]', + '//*[@class="sl-viral-container"]', + '//*[@class="sl-art-creds-cntr"]', + '//*[@class="sl-art-ad-midflex"]', + ) + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.theguardian.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.theguardian.com.php new file mode 100644 index 0000000000..e0d6f3fd03 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.theguardian.com.php @@ -0,0 +1,14 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.theguardian.com/sustainable-business/2015/feb/02/2015-hyper-transparency-global-business', + 'body' => array( + '//div[contains(@class, "content__main-column--article")]', + ), + 'strip' => array( + '//div[contains(@class, "meta-container")]', + ), + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.wikipedia.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.wikipedia.org.php new file mode 100644 index 0000000000..7b8f76e5c8 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.wikipedia.org.php @@ -0,0 +1,29 @@ + array( + '%.*%' => array( + 'test_url' => 'https://en.wikipedia.org/wiki/Grace_Hopper', + 'body' => array( + '//div[@id="bodyContent"]', + ), + 'strip' => array( + "//div[@id='toc']", + "//div[@id='catlinks']", + "//div[@id='jump-to-nav']", + "//div[@class='thumbcaption']//div[@class='magnify']", + "//table[@class='navbox']", + "//table[contains(@class, 'infobox')]", + "//div[@class='dablink']", + "//div[@id='contentSub']", + "//div[@id='siteSub']", + "//table[@id='persondata']", + "//table[contains(@class, 'metadata')]", + "//*[contains(@class, 'noprint')]", + "//*[contains(@class, 'printfooter')]", + "//*[contains(@class, 'editsection')]", + "//*[contains(@class, 'error')]", + "//span[@title='pronunciation:']", + ), + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.wired.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.wired.com.php new file mode 100644 index 0000000000..952b09ac30 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.wired.com.php @@ -0,0 +1,44 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.wired.com/gamelife/2013/09/ouya-free-the-games/', + 'body' => array( + '//div[@data-js="gallerySlides"]', + '//div[starts-with(@class,"post")]', + ), + 'strip' => array( + '//h1', + '//nav', + '//button', + '//figure[starts-with(@class,"rad-slide")]', + '//figure[starts-with(@class,"end-slate")]', + '//div[contains(@class,"mobile-")]', + '//div[starts-with(@class,"mob-gallery-launcher")]', + '//div[contains(@id,"mobile-")]', + '//span[contains(@class,"slide-count")]', + '//div[contains(@class,"show-ipad")]', + '//img[contains(@id,"-hero-bg")]', + '//div[@data-js="overlayWrap"]', + '//ul[contains(@class,"metadata")]', + '//div[@class="opening center"]', + '//p[contains(@class="byline-mob"]', + '//div[@id="o-gallery"]', + '//div[starts-with(@class,"sm-col")]', + '//div[contains(@class,"pad-b-huge")]', + '//a[contains(@class,"visually-hidden")]', + '//*[@class="social"]', + '//i', + '//div[@data-js="mobGalleryAd"]', + '//div[contains(@class,"footer")]', + '//div[contains(@data-js,"fader")]', + '//div[@id="sharing"]', + '//div[contains(@id,"related")]', + '//div[@id="most-pop"]', + '//ul[@id="article-tags"]', + '//style', + '//section[contains(@class,"footer")]' + ), + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.wsj.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.wsj.com.php new file mode 100644 index 0000000000..f6e6cc1290 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.wsj.com.php @@ -0,0 +1,15 @@ + array( + '%.*%' => array( + 'test_url' => 'http://online.wsj.com/article/SB10001424127887324108204579023143974408428.html', + 'body' => array( + '//div[@class="articlePage"]', + ), + 'strip' => array( + '//*[@id="articleThumbnail_2"]', + '//*[@class="socialByline"]', + ) + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/01net.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/01net.com.php new file mode 100644 index 0000000000..6d144f059f --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/01net.com.php @@ -0,0 +1,18 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.01net.com/editorial/624550/twitter-rachete-madbits-un-specialiste-francais-de-lanalyse-dimages/', + 'body' => array( + '//div[@class="article_ventre_box"]', + ), + 'strip' => array( + '//link', + '//*[contains(@class, "article_navigation")]', + '//h1', + '//*[contains(@class, "article_toolbarMain")]', + '//*[contains(@class, "article_imagehaute_box")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/abstrusegoose.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/abstrusegoose.com.php new file mode 100644 index 0000000000..752d0413de --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/abstrusegoose.com.php @@ -0,0 +1,8 @@ + array( + '%.*%' => array( + '%alt="(.+)" title="(.+)" */>%' => '/>
$1
$2', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/adventuregamers.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/adventuregamers.com.php new file mode 100644 index 0000000000..98d384e6a4 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/adventuregamers.com.php @@ -0,0 +1,23 @@ + array( + '%^/news.*%' => array( + 'test_url' => 'http://www.adventuregamers.com/news/view/31079', + 'body' => array( + '//div[@class="bodytext"]', + ) + ), + '%^/videos.*%' => array( + 'test_url' => 'http://www.adventuregamers.com/videos/view/31056', + 'body' => array( + '//iframe', + ) + ), + '%^/articles.*%' => array( + 'test_url' => 'http://www.adventuregamers.com/articles/view/31049', + 'body' => array( + '//div[@class="cleft"]', + ) + ) + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/alainonline.net.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/alainonline.net.php new file mode 100644 index 0000000000..f440b234f6 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/alainonline.net.php @@ -0,0 +1,14 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.alainonline.net/news_details.php?lang=arabic&sid=18907', + 'body' => array( + '//div[@class="news_details"]', + ), + 'strip' => array( + '//div[@class="news_details"]/div/div[last()]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/aljazeera.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/aljazeera.com.php new file mode 100644 index 0000000000..c02eb2192f --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/aljazeera.com.php @@ -0,0 +1,25 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.aljazeera.com/news/2015/09/xi-jinping-seattle-china-150922230118373.html', + 'body' => array( + '//article[@id="main-story"]', + ), + 'strip' => array( + '//script', + '//header', + '//ul', + '//section[contains(@class,"profile")]', + '//a[@target="_self"]', + '//div[contains(@id,"_2")]', + '//div[contains(@id,"_3")]', + '//img[@class="viewMode"]', + '//table[contains(@class,"in-article-item")]', + '//div[@data-embed-type="Brightcove"]', + '//div[@class="QuoteContainer"]', + '//div[@class="BottomByLine"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/allafrica.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/allafrica.com.php new file mode 100644 index 0000000000..e8a506d44a --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/allafrica.com.php @@ -0,0 +1,20 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.aljazeera.com/news/2015/09/xi-jinping-seattle-china-150922230118373.html', + 'body' => array( + '//div[@class="story-body"]', + ), + 'strip' => array( + '//p[@class="kindofstory"]', + '//cite[@class="byline"]', + '//div[@class="useful-top"]', + '//div[contains(@class,"related-topics")]', + '//links', + '//sharebar', + '//related-topics', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/allgemeine-zeitung.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/allgemeine-zeitung.de.php new file mode 100644 index 0000000000..8ede99b13d --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/allgemeine-zeitung.de.php @@ -0,0 +1,23 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.allgemeine-zeitung.de/lokales/polizei/mainz-gonsenheim-unbekannte-rauben-esso-tankstelle-in-kurt-schumacher-strasse-aus_14913147.htm', + 'body' => array( + '//div[contains(@class, "article")][1]', + ), + 'strip' => array( + '//read/h1', + '//*[@id="t-map"]', + '//*[contains(@class, "modules")]', + '//*[contains(@class, "adsense")]', + '//*[contains(@class, "linkbox")]', + '//*[contains(@class, "info")]', + '//*[@class="skip"]', + '//*[@class="funcs"]', + '//span[@class="nd address"]', + '//a[contains(@href, "abo-und-services")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/amazingsuperpowers.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/amazingsuperpowers.com.php new file mode 100644 index 0000000000..3214c62ae3 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/amazingsuperpowers.com.php @@ -0,0 +1,8 @@ + array( + '%.*%' => array( + '%title="(.+)" */>%' => '/>
$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/anythingcomic.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/anythingcomic.com.php new file mode 100644 index 0000000000..51247f7624 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/anythingcomic.com.php @@ -0,0 +1,13 @@ + array( + '%.*%' => array( + 'body' => array( + '//img[@id="comic_image"]', + '//div[@class="comment-wrapper"][position()=1]', + ), + 'strip' => array(), + 'test_url' => 'http://www.anythingcomic.com/comics/2108929/stress-free/', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ap.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ap.org.php new file mode 100644 index 0000000000..5bb2bb6c5f --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ap.org.php @@ -0,0 +1,13 @@ + array( + '%.*%' => array( + 'test_url' => 'http://hosted.ap.org/dynamic/stories/A/AS_CHINA_GAO_ZHISHENG?SITE=AP&SECTION=HOME&TEMPLATE=DEFAULT', + 'body' => array( + '//img[@class="ap-smallphoto-img"]', + '//span[@class="entry-content"]', + ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/areadvd.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/areadvd.de.php new file mode 100644 index 0000000000..fc56922027 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/areadvd.de.php @@ -0,0 +1,10 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.areadvd.de/news/daily-deals-angebote-bei-lautsprecher-teufel-3/', + 'body' => array('//div[contains(@class,"entry")]'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/arstechnica.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/arstechnica.com.php new file mode 100644 index 0000000000..55e01ce322 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/arstechnica.com.php @@ -0,0 +1,25 @@ + array( + '%.*%' => array( + 'test_url' => 'http://arstechnica.com/tech-policy/2015/09/judge-warners-2m-happy-birthday-copyright-is-bogus/', + 'body' => array( + '//article', + ), + 'strip' => array( + '//h4[@class="post-upperdek"]', + '//h1', + '//ul[@class="lSPager lSGallery"]', + '//div[@class="lSAction"]', + '//section[@class="post-meta"]', + '//figcaption', + '//aside', + '//div[@class="gallery-image-credit"]', + '//section[@class="article-author"]', + '//*[contains(@id,"social-")]', + '//div[contains(@id,"footer")]', + ), + ), + ), +); + diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/awkwardzombie.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/awkwardzombie.com.php new file mode 100644 index 0000000000..5ab7051427 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/awkwardzombie.com.php @@ -0,0 +1,10 @@ + array( + '%/index.php.*comic=.*%' => array( + 'test_url' => 'http://www.awkwardzombie.com/index.php?comic=041315', + 'body' => array('//*[@id="comic"]/img'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/backchannel.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/backchannel.com.php new file mode 100644 index 0000000000..bc5932a280 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/backchannel.com.php @@ -0,0 +1,18 @@ + array( + '%.*%' => array( + 'test_url' => 'https://medium.com/lessons-learned/917b8b63ae3e', + 'body' => array( + '//div[contains(@class,"section-inner")]', + ), + 'strip' => array( + '//div[contains(@class,"metabar")]', + '//img[contains(@class,"thumbnail")]', + '//h1', + '//blockquote', + '//p[contains(@class,"graf-after--h4")]' + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bangkokpost.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bangkokpost.com.php new file mode 100644 index 0000000000..165515bb2e --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bangkokpost.com.php @@ -0,0 +1,19 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.bangkokpost.com/news/politics/704204/new-us-ambassador-arrives-in-bangkok', + 'body' => array( + '//article/div[@class="articleContents"]', + ), + 'strip' => array( + '//h2', + '//h4', + '//div[@class="text-size"]', + '//div[@class="relate-story"]', + '//div[@class="text-ads"]', + '//ul', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bgr.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bgr.com.php new file mode 100644 index 0000000000..7507a2f165 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bgr.com.php @@ -0,0 +1,15 @@ + array( + '%.*%' => array( + 'test_url' => 'http://bgr.com/2015/09/27/iphone-6s-waterproof-testing/', + 'body' => array( + '//img[contains(@class,"img")]', + '//div[@class="text-column"]', + ), + 'strip' => array( + '//strong', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bigfootjustice.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bigfootjustice.com.php new file mode 100644 index 0000000000..d06ed12419 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bigfootjustice.com.php @@ -0,0 +1,8 @@ + array( + '%.*%' => array( + '%-150x150%' => '', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bigpicture.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bigpicture.ru.php new file mode 100644 index 0000000000..55c40894ca --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bigpicture.ru.php @@ -0,0 +1,31 @@ + array( + '%.*%' => array( + 'test_url' => 'http://bigpicture.ru/?p=556658', + 'body' => array( + '//div[@class="article container"]', + ), + 'strip' => array( + '//script', + '//form', + '//style', + '//h1', + '//*[@class="wp-smiley"]', + '//div[@class="ipmd"]', + '//div[@class="tags"]', + '//div[@class="social-button"]', + '//div[@class="bottom-share"]', + '//div[@class="raccoonbox"]', + '//div[@class="yndadvert"]', + '//div[@class="we-recommend"]', + '//div[@class="relap-bigpicture_ru-wrapper"]', + '//div[@id="mmail"]', + '//div[@id="mobile-ads-cut"]', + '//div[@id="liquidstorm-alt-html"]', + '//div[contains(@class, "post-tags")]', + '//*[contains(text(),"Смотрите также")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bizjournals.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bizjournals.com.php new file mode 100644 index 0000000000..d1cc3da96d --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bizjournals.com.php @@ -0,0 +1,12 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.bizjournals.com/milwaukee/news/2015/09/30/bucks-will-hike-prices-on-best-seats-at-new-arena.html', + 'body' => array( + '//figure/div/a/img', + '//p[@class="content__segment"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/biztimes.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/biztimes.com.php new file mode 100644 index 0000000000..d21aa98c2b --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/biztimes.com.php @@ -0,0 +1,22 @@ + array( + '%.*%' => array( + 'test_url' => 'https://www.biztimes.com/2017/02/10/settlement-would-revive-fowler-lake-condo-project-in-oconomowoc/', + 'body' => array( + '//h2/span[@class="subhead"]', + '//div[contains(@class,"article-content")]', + ), + 'strip' => array( + '//script', + '//div[contains(@class,"mobile-article-content")]', + '//div[contains(@class,"sharedaddy")]', + '//div[contains(@class,"author-details")]', + '//div[@class="row ad"]', + '//div[contains(@class,"relatedposts")]', + '//div[@class="col-lg-12"]', + '//div[contains(@class,"widget")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bleepingcomputer.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bleepingcomputer.com.php new file mode 100644 index 0000000000..7b74060093 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bleepingcomputer.com.php @@ -0,0 +1,15 @@ + array( + '%.*%' => array( + 'test_url' => 'https://www.bleepingcomputer.com/news/google/chromes-sandbox-feature-infringes-on-three-patents-so-google-must-now-pay-20m/', + 'body' => array( + '//div[@class="article_section"]', + ), + 'strip' => array( + '//*[@itemprop="headline"]', + '//div[@class="cz-news-story-title-section"]' + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/blog.fefe.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/blog.fefe.de.php new file mode 100644 index 0000000000..39c88ae473 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/blog.fefe.de.php @@ -0,0 +1,13 @@ + array( + '%.*%' => array( + 'test_url' => 'http://blog.fefe.de/?ts=ad706a73', + 'body' => array( + '/html/body/ul', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/blog.mapillary.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/blog.mapillary.com.php new file mode 100644 index 0000000000..ce016510ed --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/blog.mapillary.com.php @@ -0,0 +1,11 @@ + array( + '%.*%' => array( + 'test_url' => 'http://blog.mapillary.com/update/2015/08/26/traffic-sign-updates.html', + 'body' => array( + '//div[contains(@class, "blog-post__content")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/brewers.mlb.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/brewers.mlb.com.php new file mode 100644 index 0000000000..be406faf7a --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/brewers.mlb.com.php @@ -0,0 +1,22 @@ + array( + '%.*%' => array( + 'test_url' => 'http://m.brewers.mlb.com/news/article/161364798', + 'body' => array( + '//article[contains(@class,"article")]', + ), + 'strip' => array( + '//div[contains(@class,"ad-slot")]', + '//h1', + '//span[@class="timestamp"]', + '//div[contains(@class,"contributor-bottom")]', + '//div[contains(@class,"video")]', + '//ul[contains(@class,"social")]', + '//p[@class="tagline"]', + '//div[contains(@class,"social")]', + '//div[@class="button-wrap"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/buenosairesherald.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/buenosairesherald.com.php new file mode 100644 index 0000000000..4e73e79fe4 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/buenosairesherald.com.php @@ -0,0 +1,17 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.buenosairesherald.com/article/199344/manzur-named-next-governor-of-tucum%C3%A1n', + 'body' => array( + '//div[@style="float:none"]', + ), + 'strip' => array( + '//div[contains(@class, "bz_alias_short_desc_container"]', + '//td[@id="bz_show_bug_column_1"]', + '//table[@id="attachment_table"]', + '//table[@class="bz_comment_table"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bunicomic.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bunicomic.com.php new file mode 100644 index 0000000000..ad83e4366d --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bunicomic.com.php @@ -0,0 +1,13 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.bunicomic.com/comic/buni-623/', + 'body' => array( + '//div[@class="comic-table"]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/buttersafe.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/buttersafe.com.php new file mode 100644 index 0000000000..1f313cd0a9 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/buttersafe.com.php @@ -0,0 +1,13 @@ + array( + '%.*%' => array( + 'test_url' => 'http://buttersafe.com/2015/04/21/the-incredible-flexible-man/', + 'body' => array( + '//div[@id="comic"]', + '//div[@class="post-comic"]', + ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cad-comic.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cad-comic.com.php new file mode 100644 index 0000000000..a631c97fe0 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cad-comic.com.php @@ -0,0 +1,12 @@ + array( + '%/cad/.+%' => array( + 'test_url' => 'http://www.cad-comic.com/cad/20150417', + 'body' => array( + '//*[@id="content"]/img', + ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/chaoslife.findchaos.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/chaoslife.findchaos.com.php new file mode 100644 index 0000000000..ea6191e850 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/chaoslife.findchaos.com.php @@ -0,0 +1,10 @@ + array( + '%.*%' => array( + 'test_url' => 'http://chaoslife.findchaos.com/pets-in-the-wild', + 'body' => array('//div[@id="comic"]'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/chinafile.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/chinafile.com.php new file mode 100644 index 0000000000..450117b3b8 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/chinafile.com.php @@ -0,0 +1,18 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.chinafile.com/books/shanghai-faithful?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+chinafile%2FAll+%28ChinaFile%29', + 'body' => array( + '//div[contains(@class,"pane-featured-photo-panel-pane-1")]', + '//div[contains(@class,"video-above-fold")]', + '//div[@class="sc-media"]', + '//div[contains(@class,"field-name-body")]', + ), + 'strip' => array( + '//div[contains(@class,"cboxes")]', + '//div[contains(@class,"l-middle")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cliquerefresh.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cliquerefresh.com.php new file mode 100644 index 0000000000..9dcc7e5438 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cliquerefresh.com.php @@ -0,0 +1,10 @@ + array( + '%/comic.*%' => array( + 'test_url' => 'http://cliquerefresh.com/comic/078-stating-the-obvious/', + 'body' => array('//div[@class="comicImg"]/img | //div[@class="comicImg"]/a/img'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cnet.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cnet.com.php new file mode 100644 index 0000000000..60767a53f9 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cnet.com.php @@ -0,0 +1,37 @@ + array( + '%^/products.*%' => array( + 'test_url' => 'http://www.cnet.com/products/fibaro-flood-sensor/#ftag=CADf328eec', + 'body' => array( + '//li[contains(@class,"slide first"] || //figure[contains(@class,(promoFigure))]', + '//div[@class="quickInfo"]', + '//div[@class="col-6 ratings"]', + '//div[@id="editorReview"]', + ), + 'strip' => array( + '//script', + '//a[@class="clickToEnlarge"]', + '//div[@section="topSharebar"]', + '//div[contains(@class,"related")]', + '//div[contains(@class,"ad-")]', + '//div[@section="shortcodeGallery"]', + ), + ), + '%.*%' => array( + 'test_url' => 'http://cnet.com.feedsportal.com/c/34938/f/645093/s/4a340866/sc/28/l/0L0Scnet0N0Cnews0Cman0Eclaims0Eonline0Epsychic0Emade0Ehim0Ebuy0E10Emillion0Epowerball0Ewinning0Eticket0C0Tftag0FCAD590Aa51e/story01.htm', + 'body' => array( + '//p[@itemprop="description"]', + '//div[@itemprop="articleBody"]', + ), + 'strip' => array( + '//script', + '//a[@class="clickToEnlarge"]', + '//div[@section="topSharebar"]', + '//div[contains(@class,"related")]', + '//div[contains(@class,"ad-")]', + '//div[@section="shortcodeGallery"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/consomac.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/consomac.fr.php new file mode 100644 index 0000000000..9209f9cb49 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/consomac.fr.php @@ -0,0 +1,13 @@ + array( + '%.*%' => array( + 'test_url' => 'http://consomac.fr/news-2430-l-iphone-6-toujours-un-secret-bien-garde.html', + 'body' => array( + '//div[contains(@id, "newscontent")]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cowbirdsinlove.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cowbirdsinlove.com.php new file mode 100644 index 0000000000..3214c62ae3 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cowbirdsinlove.com.php @@ -0,0 +1,8 @@ + array( + '%.*%' => array( + '%title="(.+)" */>%' => '/>
$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/crash.net.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/crash.net.php new file mode 100644 index 0000000000..9d9b35a668 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/crash.net.php @@ -0,0 +1,28 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.crash.net/motogp/interview/247550/1/exclusive-andrea-dovizioso-interview.html', + 'body' => array( + '//div[@id="content"]', + ), + 'strip' => array( + '//script', + '//style', + '//*[@title="Social Networking"]', + '//*[@class="crash-ad2"]', + '//*[@class="clearfix"]', + '//*[@class="crash-ad2"]', + '//*[contains(@id, "divCB"]', + '//*[@class="pnlComment"]', + '//*[@class="comments-tabs"]', + '//*[contains(@class, "ad-twocol"]', + '//*[@class="stories-list"]', + '//*[contains(@class, "btn")]', + '//*[@class="content"]', + '//h3', + ), + ), + ), +); + diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/csmonitor.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/csmonitor.com.php new file mode 100644 index 0000000000..481e4b0977 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/csmonitor.com.php @@ -0,0 +1,19 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.csmonitor.com/USA/Politics/2015/0925/John-Boehner-steps-down-Self-sacrificing-but-will-it-lead-to-better-government', + 'body' => array( + '//h2[@id="summary"]', + '//div[@class="flex-video youtube"]', + '//div[contains(@class,"eza-body")]', + ), + 'strip' => array( + '//span[@id="breadcrumb"]', + '//div[@id="byline-wrapper"]', + '//div[@class="injection"]', + '//*[contains(@class,"promo_link")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dailyjs.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dailyjs.com.php new file mode 100644 index 0000000000..20eb1d750d --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dailyjs.com.php @@ -0,0 +1,19 @@ + array( + '%.*%' => array( + 'test_url' => 'http://dailyjs.com/2014/08/07/p5js/', + 'body' => array( + '//div[@id="post"]', + ), + 'strip' => array( + '//h2[@class="post"]', + '//div[@class="meta"]', + '//*[contains(@class, "addthis_toolbox")]', + '//*[contains(@class, "addthis_default_style")]', + '//*[@class="navigation small"]', + '//*[@id="related"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dailyreporter.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dailyreporter.com.php new file mode 100644 index 0000000000..db3fc0e121 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dailyreporter.com.php @@ -0,0 +1,15 @@ + array( + '%.*%' => array( + 'test_url' => 'http://dailyreporter.com/2016/01/09/us-supreme-court-case-could-weaken-government-workers-unions/', + 'body' => array( + '//div[contains(@class, "entry-content")]', + ), + 'strip' => array( + '//div[@class="dmcss_login_form"]', + '//*[contains(@class, "sharedaddy")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dailytech.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dailytech.com.php new file mode 100644 index 0000000000..5d1df4a992 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dailytech.com.php @@ -0,0 +1,13 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.dailytech.com/Apples+First+Fixes+to+iOS+9+Land+w+iOS++901+Release/article37495.htm', + 'body' => array( + '//div[@class="NewsBodyImage"]', + '//span[@id="lblSummary"]', + '//span[@id="lblBody"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/degroupnews.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/degroupnews.com.php new file mode 100644 index 0000000000..91f5c56ef1 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/degroupnews.com.php @@ -0,0 +1,14 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.degroupnews.com/medias/vodsvod/amazon-concurrence-la-chromecast-de-google-avec-fire-tv-stick', + 'body' => array( + '//div[@class="contenu"]', + ), + 'strip' => array( + '//div[contains(@class, "a2a")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/derstandard.at.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/derstandard.at.php new file mode 100644 index 0000000000..7e95a51f07 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/derstandard.at.php @@ -0,0 +1,14 @@ + array( + '%.*%' => array( + 'test_url' => 'http://derstandard.at/2000010267354/The-Witcher-3-Hohe-Hardware-Anforderungen-fuer-PC-Spieler?ref=rss', + 'body' => array( + '//div[@class="copytext"]', + '//ul[@id="media-list"]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dilbert.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dilbert.com.php new file mode 100644 index 0000000000..b8e9b3d6e5 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dilbert.com.php @@ -0,0 +1,11 @@ + array( + '%.*%' => array( + 'body' => array( + '//img[@class="img-responsive img-comic"]', + ), + 'test_url' => 'http://dilbert.com/strip/2016-01-28', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/discovermagazine.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/discovermagazine.com.php new file mode 100644 index 0000000000..ae0dfe7195 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/discovermagazine.com.php @@ -0,0 +1,26 @@ + array( + '%.*%' => array( + 'test_url' => 'http://blogs.discovermagazine.com/neuroskeptic/2017/01/25/publishers-jeffrey-beall/', + 'body' => array( + '//div[@class="contentWell"]', + ), + 'strip' => array( + '//h1', + '//div[@class="breadcrumbs"]', + '//div[@class="mobile"]', + '//div[@class="fromIssue"]', + '//div[contains(@class,"belowDeck")]', + '//div[@class="meta"]', + '//div[@class="shareIcons"]', + '//div[@class="categories"]', + '//div[@class="navigation"]', + '//div[@class="heading"]', + '//div[contains(@id,"-ad")]', + '//div[@class="relatedArticles"]', + '//div[@id="disqus_thread"]' + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/distrowatch.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/distrowatch.com.php new file mode 100644 index 0000000000..aefc8f8110 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/distrowatch.com.php @@ -0,0 +1,13 @@ + array( + '%.*%' => array( + 'test_url' => 'http://distrowatch.com/?newsid=08355', + 'body' => array( + '//td[@class="NewsText"][1]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dozodomo.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dozodomo.com.php new file mode 100644 index 0000000000..e116695740 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dozodomo.com.php @@ -0,0 +1,15 @@ + array( + '%.*%' => array( + 'test_url' => 'http://dozodomo.com/bento/2014/03/04/lart-des-maki-de-takayo-kiyota/', + 'body' => array( + '//div[@class="joke"]', + '//div[@class="story-cover"]', + '//div[@class="story-content"]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/drawingboardcomic.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/drawingboardcomic.com.php new file mode 100644 index 0000000000..cd30f2e0ce --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/drawingboardcomic.com.php @@ -0,0 +1,15 @@ + array( + '%.*%' => array( + 'body' => array('//img[@id="comicimage"]'), + 'strip' => array(), + 'test_url' => 'http://drawingboardcomic.com/index.php?comic=208', + ), + ), + 'filter' => array( + '%.*%' => array( + '%title="(.+)" */>%' => '/>
$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/e-w-e.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/e-w-e.ru.php new file mode 100644 index 0000000000..8139cc9ad8 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/e-w-e.ru.php @@ -0,0 +1,22 @@ + array( + '%.*%' => array( + 'test_url' => 'http://e-w-e.ru/16-prekrasnyx-izobretenij-zhenshhin/', + 'body' => array( + '//div[contains(@class, "post_text")]', + ), + 'strip' => array( + '//script', + '//form', + '//style', + '//*[@class="views_post"]', + '//*[@class="adman_mobile"]', + '//*[@class="adman_desctop"]', + '//*[contains(@rel, "nofollow")]', + '//*[contains(@class, "wp-smiley")]', + '//*[contains(text(),"Источник:")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/economist.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/economist.com.php new file mode 100644 index 0000000000..522032fd02 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/economist.com.php @@ -0,0 +1,25 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.economist.com/blogs/buttonwood/2017/02/mixed-signals?fsrc=rss', + 'body' => array( + '//article', + ), + 'strip' => array( + '//span[@class="blog-post__siblings-list-header "]', + '//h1', + '//aside', + '//div[@class="blog-post__asideable-wrapper"]', + '//div[@class="share_inline_header"]', + '//div[@id="column-right"]', + '//div[contains(@class,"blog-post__siblings-list-aside")]', + '//div[@class="video-player__wrapper"]', + '//div[@class="blog-post__bottom-panel"]', + '//div[contains(@class,"latest-updates-panel__container")]', + '//div[contains(@class,"blog-post__asideable-content")]', + '//div[@aria-label="Advertisement"]' + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/encyclopedie.naheulbeuk.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/encyclopedie.naheulbeuk.com.php new file mode 100644 index 0000000000..19bcbdefb6 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/encyclopedie.naheulbeuk.com.php @@ -0,0 +1,13 @@ + array( + '%.*%' => array( + 'test_url' => 'http://encyclopedie.naheulbeuk.com/article.php3?id_article=352', + 'body' => array( + '//td//h1[@class="titre-texte"]', + '//td//div[@class="surtitre"]', + '//td//div[@class="texte"]', + ), + ) + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/endlessorigami.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/endlessorigami.com.php new file mode 100644 index 0000000000..d06ed12419 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/endlessorigami.com.php @@ -0,0 +1,8 @@ + array( + '%.*%' => array( + '%-150x150%' => '', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/engadget.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/engadget.com.php new file mode 100644 index 0000000000..cf9e448560 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/engadget.com.php @@ -0,0 +1,10 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.engadget.com/2015/04/20/dark-matter-discovery/?ncid=rss_truncated', + 'body' => array('//div[@id="page_body"]/div[@class="container@m-"]'), + 'strip' => array('//aside[@role="banner"]'), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/escapistmagazine.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/escapistmagazine.com.php new file mode 100644 index 0000000000..e86b59cb6f --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/escapistmagazine.com.php @@ -0,0 +1,45 @@ + array( + '%/articles/view/comicsandcosplay/comics/critical-miss.*%' => array( + 'body' => array('//*[@class="body"]/span/img | //div[@class="folder_nav_links"]/following::p'), + 'test_url' => 'http://www.escapistmagazine.com/articles/view/comicsandcosplay/comics/critical-miss/13776-Critical-Miss-on-Framerates?utm_source=rss&utm_medium=rss&utm_campaign=articles', + 'strip' => array(), + ), + '%/articles/view/comicsandcosplay/comics/namegame.*%' => array( + 'body' => array('//*[@class="body"]/span/p/img[@height != "120"]'), + 'test_url' => 'http://www.escapistmagazine.com/articles/view/comicsandcosplay/comics/namegame/9759-Leaving-the-Nest?utm_source=rss&utm_medium=rss&utm_campaign=articles', + 'strip' => array(), + ), + '%/articles/view/comicsandcosplay/comics/stolen-pixels.*%' => array( + 'body' => array('//*[@class="body"]/span/p[2]/img'), + 'test_url' => 'http://www.escapistmagazine.com/articles/view/comicsandcosplay/comics/stolen-pixels/8866-Stolen-Pixels-258-Where-the-Boys-Are?utm_source=rss&utm_medium=rss&utm_campaign=articles', + 'strip' => array(), + ), + '%/articles/view/comicsandcosplay/comics/bumhugparade.*%' => array( + 'body' => array('//*[@class="body"]/span/p[2]/img'), + 'test_url' => 'http://www.escapistmagazine.com/articles/view/comicsandcosplay/comics/bumhugparade/8262-Bumhug-Parade-13?utm_source=rss&utm_medium=rss&utm_campaign=articles', + 'strip' => array(), + ), + '%/articles/view/comicsandcosplay.*/comics/escapistradiotheater%' => array( + 'body' => array('//*[@class="body"]/span/p[2]/img'), + 'test_url' => 'http://www.escapistmagazine.com/articles/view/comicsandcosplay/comics/escapistradiotheater/8265-The-Escapist-Radio-Theater-13?utm_source=rss&utm_medium=rss&utm_campaign=articles', + 'strip' => array(), + ), + '%/articles/view/comicsandcosplay/comics/paused.*%' => array( + 'body' => array('//*[@class="body"]/span/p[2]/img | //*[@class="body"]/span/div/img'), + 'test_url' => 'http://www.escapistmagazine.com/articles/view/comicsandcosplay/comics/paused/8263-Paused-16?utm_source=rss&utm_medium=rss&utm_campaign=articles', + 'strip' => array(), + ), + '%/articles/view/comicsandcosplay/comics/fraughtwithperil.*%' => array( + 'body' => array('//*[@class="body"]'), + 'test_url' => 'http://www.escapistmagazine.com/articles/view/comicsandcosplay/comics/fraughtwithperil/12166-The-Escapist-Presents-Escapist-Comics-Critical-Miss-B-lyeh-Fhlop?utm_source=rss&utm_medium=rss&utm_campaign=articles', + 'strip' => array(), + ), + '%/articles/view/video-games/columns/.*%' => array( + 'body' => array('//*[@id="article_content"]'), + 'test_url' => 'http://www.escapistmagazine.com/articles/view/video-games/columns/experienced-points/13971-What-50-Shades-and-Batman-Have-in-Common.2', + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/espn.go.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/espn.go.com.php new file mode 100644 index 0000000000..76a20f74f5 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/espn.go.com.php @@ -0,0 +1,11 @@ + array( + '%.*%' => array( + 'test_url' => 'http://espn.go.com/nfl/story/_/id/13388208/jason-whitlock-chip-kelly-controversy', + 'body' => array( + '//p', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/exocomics.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/exocomics.com.php new file mode 100644 index 0000000000..5adc59f83c --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/exocomics.com.php @@ -0,0 +1,15 @@ + array( + '%.*%' => array( + 'body' => array('//a[@class="comic"]/img'), + 'strip' => array(), + 'test_url' => 'http://www.exocomics.com/379', + ), + ), + 'filter' => array( + '%.*%' => array( + '%title="(.+)" */>%' => '/>
$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/explosm.net.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/explosm.net.php new file mode 100644 index 0000000000..3fdf02c050 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/explosm.net.php @@ -0,0 +1,13 @@ + array( + '%.*%' => array( + 'test_url' => 'http://explosm.net/comics/3803/', + 'body' => array( + '//div[@id="comic-container"]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/extrafabulouscomics.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/extrafabulouscomics.com.php new file mode 100644 index 0000000000..12697ccbf5 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/extrafabulouscomics.com.php @@ -0,0 +1,8 @@ + array( + '%.*%' => array( + '%-150x150%' => '', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/factroom.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/factroom.ru.php new file mode 100644 index 0000000000..a572061d07 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/factroom.ru.php @@ -0,0 +1,27 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.factroom.ru/life/20-facts-about-oil', + 'body' => array( + '//div[@class="post"]', + ), + 'strip' => array( + '//script', + '//form', + '//style', + '//h1', + '//div[@id="yandex_ad2"]', + '//*[@class="jp-relatedposts"]', + '//div[contains(@class, "likely-desktop")]', + '//div[contains(@class, "likely-mobile")]', + '//p[last()]', + '//div[contains(@class, "facebook")]', + '//div[contains(@class, "desktop-underpost-direct")]', + '//div[contains(@class, "source-box")]', + '//div[contains(@class, "under-likely-desktop")]', + '//div[contains(@class, "mobile-down-post")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fastcodesign.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fastcodesign.com.php new file mode 100644 index 0000000000..74e70a8620 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fastcodesign.com.php @@ -0,0 +1,13 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.fastcodesign.com/3026548/exposure/peek-inside-the-worlds-forbidden-subway-tunnels', + 'body' => array( + '//article[contains(@class, "body prose")]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fastcoexist.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fastcoexist.com.php new file mode 100644 index 0000000000..6916f280a1 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fastcoexist.com.php @@ -0,0 +1,13 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.fastcoexist.com/3026114/take-a-seat-on-this-gates-funded-future-toilet-that-will-change-how-we-think-about-poop', + 'body' => array( + '//article[contains(@class, "body prose")]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fastcompany.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fastcompany.com.php new file mode 100644 index 0000000000..e0869a2974 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fastcompany.com.php @@ -0,0 +1,13 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.fastcompany.com/3026712/fast-feed/elon-musk-an-apple-tesla-merger-is-very-unlikely', + 'body' => array( + '//article[contains(@class, "body prose")]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ffworld.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ffworld.com.php new file mode 100644 index 0000000000..20a47b2d19 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ffworld.com.php @@ -0,0 +1,13 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.ffworld.com/?rub=news&page=voir&id=2709', + 'body' => array( + '//div[@class="news_body"]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/foreignpolicy.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/foreignpolicy.com.php new file mode 100644 index 0000000000..3cbcddc43f --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/foreignpolicy.com.php @@ -0,0 +1,21 @@ + array( + '%.*%' => array( + 'test_url' => 'http://foreignpolicy.com/2016/01/09/networking-giant-pulls-nsa-linked-code-exploited-by-hackers/', + 'body' => array( + '//article', + ), + 'strip' => array( + '//div[@id="post-category"]', + '//div[@id="desktop-right"]', + '//h1', + '//section[@class="article-meta"]', + '//div[@class="side-panel-wrapper"]', + '//*[contains(@class, "share-")]', + '//*[contains(@id, "taboola-")]', + '//div[@class="comments"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fossbytes.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fossbytes.com.php new file mode 100644 index 0000000000..6ce47256bf --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fossbytes.com.php @@ -0,0 +1,18 @@ + array( + '%.*%' => array( + 'test_url' => 'http://fossbytes.com/fbi-hacked-1000-computers-to-shut-down-largest-child-pornography-site-on-the-dark-web/', + 'body' => array( + '//div[@class="entry-inner"]', + ), + 'strip' => array( + '//*[@class="at-above-post addthis_default_style addthis_toolbox at-wordpress-hide"]', + '//*[@class="at-below-post addthis_default_style addthis_toolbox at-wordpress-hide"]', + '//*[@class="at-below-post-recommended addthis_default_style addthis_toolbox at-wordpress-hide"]', + '//*[@class="code-block code-block-12 ai-desktop"]', + '//*[@class="code-block code-block-13 ai-tablet-phone"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fototelegraf.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fototelegraf.ru.php new file mode 100644 index 0000000000..ca2f85aaf8 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fototelegraf.ru.php @@ -0,0 +1,19 @@ + array( + '%.*%' => array( + 'test_url' => 'http://fototelegraf.ru/?p=348232', + 'body' => array( + '//div[@class="post-content"]', + ), + 'strip' => array( + '//script', + '//form', + '//style', + '//div[@class="imageButtonsBlock"]', + '//div[@class="adOnPostBtwImg"]', + '//div[contains(@class, "post-tags")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fowllanguagecomics.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fowllanguagecomics.com.php new file mode 100644 index 0000000000..3f62f07132 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fowllanguagecomics.com.php @@ -0,0 +1,10 @@ + array( + '%.*%' => array( + 'body' => array('//*[@id="comic"] | //*[@class="post-image"]'), + 'strip' => array(), + 'test_url' => 'http://www.fowllanguagecomics.com/comic/working-out/', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/geek.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/geek.com.php new file mode 100644 index 0000000000..d9ccecc253 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/geek.com.php @@ -0,0 +1,17 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.geek.com/news/the-11-best-ways-to-eat-eggs-1634076/', + 'body' => array( + '//div[@class="articleinfo"]/figure', + '//div[@class="articleinfo"]/article', + '//span[@class="by"]', + ), + 'strip' => array( + '//span[@class="red"]', + '//div[@class="on-target"]' + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/geektimes.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/geektimes.ru.php new file mode 100644 index 0000000000..1954138659 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/geektimes.ru.php @@ -0,0 +1,12 @@ + array( + '%.*%' => array( + 'test_url' => 'https://geektimes.ru/post/289151/', + 'body' => array( + "//div[contains(concat(' ',normalize-space(@class),' '),' content ')]" + ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/gerbilwithajetpack.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/gerbilwithajetpack.com.php new file mode 100644 index 0000000000..44013b3b15 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/gerbilwithajetpack.com.php @@ -0,0 +1,12 @@ + array( + '%.*%' => array( + 'body' => array( + '//div[@id="comic-1"]', + '//div[@class="entry"]', + ), + 'test_url' => 'http://gerbilwithajetpack.com/passing-the-digital-buck/', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/giantitp.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/giantitp.com.php new file mode 100644 index 0000000000..d9c3ae5dcf --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/giantitp.com.php @@ -0,0 +1,12 @@ + array( + '%/comics/oots.*%' => array( + 'test_url' => 'http://www.giantitp.com/comics/oots0989.html', + 'body' => array( + '//td[@align="center"]/img', + ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/github.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/github.com.php new file mode 100644 index 0000000000..726634f995 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/github.com.php @@ -0,0 +1,14 @@ + array( + '%.*%' => array( + 'test_url' => 'https://github.com/audreyr/favicon-cheat-sheet', + 'body' => array( + '//article[contains(@class, "entry-content")]', + ), + 'strip' => array( + '//h1', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/gocomics.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/gocomics.com.php new file mode 100644 index 0000000000..32960f0e16 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/gocomics.com.php @@ -0,0 +1,12 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.gocomics.com/pearlsbeforeswine/2015/05/30', + 'body' => array( + '//div[1]/p[1]/a[1]/img', + ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/golem.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/golem.de.php new file mode 100644 index 0000000000..84224830be --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/golem.de.php @@ -0,0 +1,20 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.golem.de/news/breko-telekom-verzoegert-gezielt-den-vectoring-ausbau-1311-102974.html', + 'body' => array( + '//header[@class="cluster-header"]', + '//header[@class="paged-cluster-header"]', + '//div[@class="formatted"]', + ), + 'next_page' => array( + '//a[@id="atoc_next"]' + ), + 'strip' => array( + '//header[@class="cluster-header"]/a', + '//div[@id="iqadtile4"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/gorabbit.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/gorabbit.ru.php new file mode 100644 index 0000000000..4e4324812f --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/gorabbit.ru.php @@ -0,0 +1,19 @@ + array( + '%.*%' => array( + 'test_url' => 'http://gorabbit.ru/article/10-oshchushcheniy-za-rulem-kogda-tolko-poluchil-voditelskie-prava', + 'body' => array( + '//div[@class="detail_text"]', + ), + 'strip' => array( + '//script', + '//form', + '//style', + '//div[@class="socials"]', + '//div[@id="cr_1"]', + '//div[@class="related_items"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/habrahabr.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/habrahabr.ru.php new file mode 100644 index 0000000000..3f1ec165b1 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/habrahabr.ru.php @@ -0,0 +1,12 @@ + array( + '%.*%' => array( + 'test_url' => 'https://habrahabr.ru/company/pentestit/blog/328606/', + 'body' => array( + "//div[contains(concat(' ',normalize-space(@class),' '),' content ')]" + ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/happletea.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/happletea.com.php new file mode 100644 index 0000000000..75b0b83d4f --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/happletea.com.php @@ -0,0 +1,18 @@ + array( + '%.*%' => array( + 'body' => array( + '//div[@id="comic"]', + '//div[@class="entry"]', + ), + 'strip' => array('//div[@class="ssba"]'), + 'test_url' => 'http://www.happletea.com/comic/mans-best-friend/', + ), + ), + 'filter' => array( + '%.*%' => array( + '%title="(.+)" */>%' => '/>
$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/hardware.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/hardware.fr.php new file mode 100644 index 0000000000..56aec4f455 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/hardware.fr.php @@ -0,0 +1,11 @@ + array( + '%^/news.*%' => array( + 'test_url' => 'http://www.hardware.fr/news/14760/intel-lance-nouveaux-ssd-nand-3d.html', + 'body' => array( + '//div[@class="content_actualite"]/div[@class="md"]', + ) + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/heise.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/heise.de.php new file mode 100644 index 0000000000..2055b3bcc7 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/heise.de.php @@ -0,0 +1,12 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.heise.de/security/meldung/BND-300-Millionen-Euro-fuer-Fruehwarnsystem-gegen-Cyber-Attacken-2192237.html', + 'body' => array( + '//div[@class="meldung_wrapper"]', + '//div[@class="artikel_content"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/hotshowlife.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/hotshowlife.com.php new file mode 100644 index 0000000000..faf01f3df4 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/hotshowlife.com.php @@ -0,0 +1,23 @@ + array( + '%.*%' => array( + 'test_url' => 'https://hotshowlife.com/top-10-chempionov-produktov-po-szhiganiyu-kalorij/', + 'body' => array( + '//div[@class="entry-content"]', + ), + 'strip' => array( + '//script', + '//form', + '//style', + '//div[@class="ads2"]', + '//div[@class="mistape_caption"]', + '//div[contains(@class, "et_social_media_hidden")]', + '//div[contains(@class, "et_social_inline_bottom")]', + '//div[contains(@class, "avatar")]', + '//ul[contains(@class, "entry-tags")]', + '//div[contains(@class, "entry-meta")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/huffingtonpost.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/huffingtonpost.com.php new file mode 100644 index 0000000000..b52b07b575 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/huffingtonpost.com.php @@ -0,0 +1,13 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.huffingtonpost.com/2014/02/20/centscere-social-media-syracuse_n_4823848.html', + 'body' => array( + '//article[@class="content")]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/imogenquest.net.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/imogenquest.net.php new file mode 100644 index 0000000000..3214c62ae3 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/imogenquest.net.php @@ -0,0 +1,8 @@ + array( + '%.*%' => array( + '%title="(.+)" */>%' => '/>
$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/indiehaven.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/indiehaven.com.php new file mode 100644 index 0000000000..a40ce694f2 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/indiehaven.com.php @@ -0,0 +1,11 @@ + array( + '%.*%' => array( + 'test_url' => 'http://indiehaven.com/no-mans-sky-is-a-solo-space-adventure-and-im-ok-with-that/', + 'body' => array( + '//section[contains(@class, "entry-content")]', + ) + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ing.dk.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ing.dk.php new file mode 100644 index 0000000000..5a021a08fa --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ing.dk.php @@ -0,0 +1,12 @@ + array( + '%.*%' => array( + 'test_url' => 'http://ing.dk/artikel/smart-husisolering-og-styring-skal-mindske-japans-energikrise-164517', + 'body' => array( + '//section[contains(@class, "teaser")]', + '//section[contains(@class, "body")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/invisiblebread.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/invisiblebread.com.php new file mode 100644 index 0000000000..90f875972f --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/invisiblebread.com.php @@ -0,0 +1,8 @@ + array( + '%.*%' => array( + '%()%' => '$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ir.amd.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ir.amd.com.php new file mode 100644 index 0000000000..af99fe99b6 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ir.amd.com.php @@ -0,0 +1,10 @@ + array( + '%.*%' => array( + 'body' => array('//span[@class="ccbnTxt"]'), + 'strip' => array(), + 'test_url' => 'http://ir.amd.com/phoenix.zhtml?c=74093&p=RssLanding&cat=news&id=2055819', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/japantimes.co.jp.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/japantimes.co.jp.php new file mode 100644 index 0000000000..9959441d81 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/japantimes.co.jp.php @@ -0,0 +1,21 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.japantimes.co.jp/news/2015/09/27/world/social-issues-world/pope-meets-sex-abuse-victims-philadelphia-promises-accountability/', + 'body' => array( + '//article[@role="main"]', + ), + 'strip' => array( + '//script', + '//header', + '//div[contains(@class, "meta")]', + '//div[@class="clearfix"]', + '//div[@class="OUTBRAIN"]', + '//ul[@id="content_footer_menu"]', + '//div[@class="article_footer_ad"]', + '//div[@id="disqus_thread"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/japantoday.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/japantoday.com.php new file mode 100644 index 0000000000..22485d699c --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/japantoday.com.php @@ -0,0 +1,15 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.japantoday.com/category/politics/view/japan-u-s-to-sign-new-base-environment-pact', + 'body' => array( + '//div[@id="article_container"]', + ), + 'strip' => array( + '//h2', + '//div[@id="article_info"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/journaldugeek.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/journaldugeek.com.php new file mode 100644 index 0000000000..876b269878 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/journaldugeek.com.php @@ -0,0 +1,11 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www./2014/05/20/le-playstation-now-arrive-en-beta-fermee-aux-etats-unis/', + 'body' => array( + '//div[@class="post-content"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/jsonline.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/jsonline.com.php new file mode 100644 index 0000000000..5895256e5d --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/jsonline.com.php @@ -0,0 +1,37 @@ + array( + '%.%/picture-gallery/%' => array( + 'test_url' => 'http://www.jsonline.com/picture-gallery/news/local/milwaukee/2017/02/22/photos-aclu-sues-milwaukee-police-over-profiling-stop-and-frisk/98250836/', + 'body' => array( + '//div[@class="priority-asset-gallery galleries standalone hasendslate"]', + ), + 'strip' => array( + '//div[@class="buy-photo-btn"]', + '//div[@class="gallery-thumbs thumbs pag-thumbs")]', + ), + ), + '%.*%' => array( + 'test_url' => 'http://www.jsonline.com/news/usandworld/as-many-as-a-million-expected-for-popes-last-mass-in-us-b99585180z1-329688131.html', + 'body' => array( + '//div[@itemprop="articleBody"]', + ), + 'strip' => array( + '//h1', + '//iframe', + '//span[@class="mycapture-small-btn mycapture-btn-with-text mycapture-expandable-photo-btn-small js-mycapture-btn-small"]', + '//div[@class="close-wrap"]', + '//div[contains(@class,"ui-video-wrapper")]', + '//div[contains(@class,"media-mob")]', + '//div[contains(@class,"left-mob")]', + '//div[contains(@class,"nerdbox")]', + '//p/span', + '//div[contains(@class,"oembed-asset")]', + '//*[contains(@class,"share")]', + '//div[contains(@class,"gallery-asset")]', + '//div[contains(@class,"oembed-asset")]', + '//div[@class="article-print-url"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/justcoolidea.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/justcoolidea.ru.php new file mode 100644 index 0000000000..089ff29c86 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/justcoolidea.ru.php @@ -0,0 +1,19 @@ + array( + '%.*%' => array( + 'test_url' => 'http://justcoolidea.ru/idealnyj-sad-samodelnye-proekty-dlya-berezhlivogo-domovladeltsa/', + 'body' => array( + '//section[@class="entry-content"]', + ), + 'strip' => array( + '//script', + '//form', + '//style', + '//*[contains(@class, "essb_links")]', + '//*[contains(@rel, "nofollow")]', + '//*[contains(@class, "ads")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/kanpai.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/kanpai.fr.php new file mode 100644 index 0000000000..c3a1abc41c --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/kanpai.fr.php @@ -0,0 +1,13 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.kanpai.fr/japon/comment-donner-lheure-en-japonais.html', + 'body' => array( + '//div[@class="single-left"]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/karriere.jobfinder.dk.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/karriere.jobfinder.dk.php new file mode 100644 index 0000000000..25d6dfa39e --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/karriere.jobfinder.dk.php @@ -0,0 +1,12 @@ + array( + '%.*%' => array( + 'test_url' => 'http://karriere.jobfinder.dk/artikel/dansk-professor-skal-lede-smart-grid-forskning-20-millioner-dollars-763', + 'body' => array( + '//section[contains(@class, "teaser")]', + '//section[contains(@class, "body")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/kodi.tv.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/kodi.tv.php new file mode 100644 index 0000000000..439fc9075d --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/kodi.tv.php @@ -0,0 +1,11 @@ + array( + '%.*%' => array( + 'test_url' => 'https://kodi.tv/article/andwere-baaaaack', + 'body' => array( + '//div[@class="l-region--content"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/koreaherald.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/koreaherald.com.php new file mode 100644 index 0000000000..9651056072 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/koreaherald.com.php @@ -0,0 +1,11 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.koreaherald.com/view.php?ud=20150926000018', + 'body' => array( + '//div[@id="articleText"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/koreatimes.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/koreatimes.php new file mode 100644 index 0000000000..f274b4a9d2 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/koreatimes.php @@ -0,0 +1,14 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.koreatimes.co.kr/www/news/nation/2015/12/116_192409.html', + 'body' => array( + '//div[@id="p"]', + ), + 'strip' => array( + '//div[@id="webtalks_btn_listenDiv"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lastplacecomics.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lastplacecomics.com.php new file mode 100644 index 0000000000..12697ccbf5 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lastplacecomics.com.php @@ -0,0 +1,8 @@ + array( + '%.*%' => array( + '%-150x150%' => '', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/legorafi.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/legorafi.fr.php new file mode 100644 index 0000000000..e6aae46bd3 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/legorafi.fr.php @@ -0,0 +1,22 @@ + array( + '%.*%' => array( + 'test_url' => array( + 'http://www.legorafi.fr/2016/12/16/gorafi-magazine-bravo-vous-avez-bientot-presque-survecu-a-2016/', + 'http://www.legorafi.fr/2016/12/15/manuel-valls-promet-quune-fois-elu-il-debarrassera-la-france-de-manuel-valls/', + ), + 'body' => array( + '//section[@id="banner_magazine"]', + '//figure[@class="main_picture"]', + '//div[@class="content"]', + ), + 'strip' => array( + '//figcaption', + '//div[@class="sharebox"]', + '//div[@class="tags"]', + '//section[@class="taboola_article"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lejapon.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lejapon.fr.php new file mode 100644 index 0000000000..8f2b293249 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lejapon.fr.php @@ -0,0 +1,17 @@ + array( + '%.*%' => array( + 'test_url' => 'http://lejapon.fr/guide-voyage-japon/5223/tokyo-sous-la-neige.htm', + 'body' => array( + '//div[@class="entry"]', + ), + 'strip' => array( + '//*[contains(@class, "addthis_toolbox")]', + '//*[contains(@class, "addthis_default_style")]', + '//*[@class="navigation small"]', + '//*[@id="related"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lesjoiesducode.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lesjoiesducode.fr.php new file mode 100644 index 0000000000..369206abd7 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lesjoiesducode.fr.php @@ -0,0 +1,13 @@ + array( + '%.*%' => array( + 'test_url' => 'http://lesjoiesducode.fr/post/75576211207/quand-lappli-ne-fonctionne-plus-sans-aucune-raison', + 'body' => array( + '//div[@class="blog-post-content"]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lfg.co.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lfg.co.php new file mode 100644 index 0000000000..d978a5fc70 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lfg.co.php @@ -0,0 +1,12 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.lfg.co/page/871/?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+LookingForGroup+%28Looking+For+Group%29&utm_content=FeedBurner', + 'body' => array( + '//*[@id="comic"]/img | //*[@class="content"]', + ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lifehacker.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lifehacker.com.php new file mode 100644 index 0000000000..b9a6933880 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lifehacker.com.php @@ -0,0 +1,18 @@ + array( + '%.*%' => array( + 'test_url' => 'http://lifehacker.com/bring-water-bottle-caps-into-concerts-to-protect-your-d-1269334973', + 'body' => array( + '//div[contains(@class, "row")/img', + '//div[contains(@class, "content-column")]', + ), + 'strip' => array( + '//*[contains(@class, "meta")]', + '//span[contains(@class, "icon")]', + '//h1', + '//aside', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lifehacker.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lifehacker.ru.php new file mode 100644 index 0000000000..bc140f67f7 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lifehacker.ru.php @@ -0,0 +1,22 @@ + array( + '%.*%' => array( + 'test_url' => 'http://lifehacker.ru/2016/03/03/polymail/', + 'body' => array( + '//div[@class="post-content"]', + ), + 'strip' => array( + '//script', + '//form', + '//style', + '//*[@class="wp-thumbnail-caption"]', + '//*[contains(@class, "social-likes")]', + '//*[@class="jp-relatedposts"]', + '//*[contains(@class, "wpappbox")]', + '//*[contains(@class, "icon__image")]', + '//div[@id="hypercomments_widget"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/linux.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/linux.org.php new file mode 100644 index 0000000000..2520d0d006 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/linux.org.php @@ -0,0 +1,14 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.linux.org/threads/lua-the-scripting-interpreter.8352/', + 'body' => array( + '//div[@class="messageContent"]', + ), + 'strip' => array( + '//aside', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/linux.org.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/linux.org.ru.php new file mode 100644 index 0000000000..7fa02497bd --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/linux.org.ru.php @@ -0,0 +1,13 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.linux.org/threads/lua-the-scripting-interpreter.8352/', + 'body' => array( + '//div[@itemprop="articleBody"]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/linuxinsider.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/linuxinsider.com.php new file mode 100644 index 0000000000..4e0a4cc1ce --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/linuxinsider.com.php @@ -0,0 +1,20 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.linuxinsider.com/story/82526.html?rss=1', + 'body' => array( + '//div[@id="story"]', + ), + 'strip' => array( + '//script', + '//h1', + '//div[@id="story-toolbox1"]', + '//div[@id="story-byline"]', + '//div[@id="story"]/p', + '//div[@class="story-advertisement"]', + '//iframe', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lists.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lists.php new file mode 100644 index 0000000000..c7051a20ab --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lists.php @@ -0,0 +1,13 @@ + array( + '%.*%' => array( + 'test_url' => 'http://lists.freebsd.org/pipermail/freebsd-announce/2013-September/001504.html', + 'body' => array( + '//pre', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/loadingartist.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/loadingartist.com.php new file mode 100644 index 0000000000..d06ed12419 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/loadingartist.com.php @@ -0,0 +1,8 @@ + array( + '%.*%' => array( + '%-150x150%' => '', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/loldwell.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/loldwell.com.php new file mode 100644 index 0000000000..d358e156dd --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/loldwell.com.php @@ -0,0 +1,10 @@ + array( + '%.*%' => array( + 'test_url' => 'http://loldwell.com/?comic=food-math-101', + 'body' => array('//*[@id="comic"]'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lukesurl.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lukesurl.com.php new file mode 100644 index 0000000000..816233dd38 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lukesurl.com.php @@ -0,0 +1,15 @@ + array( + '%.*%' => array( + 'body' => array('//div[@id="comic"]//img'), + 'strip' => array(), + 'test_url' => 'http://www.lukesurl.com/archives/comic/665-3-of-clubs', + ), + ), + 'filter' => array( + '%.*%' => array( + '%title="(.+)" */>%' => '/>
$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/macg.co.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/macg.co.php new file mode 100644 index 0000000000..bbe6dbcdc0 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/macg.co.php @@ -0,0 +1,13 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.macg.co//logiciels/2014/05/feedly-sameliore-un-petit-peu-sur-mac-82205', + 'body' => array( + '//div[contains(@class, "field-name-body")]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/marc.info.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/marc.info.php new file mode 100644 index 0000000000..5f582a6d70 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/marc.info.php @@ -0,0 +1,13 @@ + array( + '%.*%' => array( + 'test_url' => 'http://marc.info/?l=openbsd-misc&m=141987113202061&w=2', + 'body' => array( + '//pre', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/marriedtothesea.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/marriedtothesea.com.php new file mode 100644 index 0000000000..469640df6e --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/marriedtothesea.com.php @@ -0,0 +1,12 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.marriedtothesea.com/index.php?date=052915', + 'body' => array( + '//div[@align]/a/img', + ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/marycagle.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/marycagle.com.php new file mode 100644 index 0000000000..b8665e35cd --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/marycagle.com.php @@ -0,0 +1,13 @@ + array( + '%.*%' => array( + 'body' => array( + '//img[@id="cc-comic"]', + '//div[@class="cc-newsbody"]', + ), + 'strip' => array(), + 'test_url' => 'http://www.marycagle.com/letsspeakenglish/74-grim-reality/', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/maximumble.thebookofbiff.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/maximumble.thebookofbiff.com.php new file mode 100644 index 0000000000..888005465c --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/maximumble.thebookofbiff.com.php @@ -0,0 +1,10 @@ + array( + '%.*%' => array( + 'test_url' => 'http://maximumble.thebookofbiff.com/2015/04/20/1084-change/', + 'body' => array('//div[@id="comic"]/div/a/img'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/medium.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/medium.com.php new file mode 100644 index 0000000000..e20860e0ff --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/medium.com.php @@ -0,0 +1,19 @@ + array( + '%.*%' => array( + 'test_url' => 'https://medium.com/lessons-learned/917b8b63ae3e', + 'body' => array( + '//div[@class="section-content"]', + ), + 'strip' => array( + '//div[contains(@class,"metabar")]', + '//img[contains(@class,"thumbnail")]', + '//h1', + '//blockquote', + '//div[@class="aspectRatioPlaceholder-fill"]', + '//footer' + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mercworks.net.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mercworks.net.php new file mode 100644 index 0000000000..c7a27dea4c --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mercworks.net.php @@ -0,0 +1,17 @@ + array( + '%.*%' => array( + 'body' => array('//div[@id="comic"]', + '//div[contains(@class,"entry-content")]', + ), + 'strip' => array(), + 'test_url' => 'http://mercworks.net/comicland/healthy-choice/', + ), + ), + 'filter' => array( + '%.*%' => array( + '%title="(.+)" */>%' => '/>
$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/metronieuws.nl.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/metronieuws.nl.php new file mode 100644 index 0000000000..5011169fc9 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/metronieuws.nl.php @@ -0,0 +1,10 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.metronieuws.nl/sport/2015/04/broer-fellaini-zorgde-bijna-voor-paniek-bij-mourinho', + 'body' => array('//div[contains(@class,"article-top")]/div[contains(@class,"image-component")] | //div[@class="article-full-width"]/div[1]'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/milwaukeenns.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/milwaukeenns.php new file mode 100644 index 0000000000..ddb29a56be --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/milwaukeenns.php @@ -0,0 +1,14 @@ + array( + '%.*%' => array( + 'test_url' => 'http://milwaukeenns.org/2016/01/08/united-way-grant-enables-sdc-to-restore-free-tax-assistance-program/', + 'body' => array( + '//div[@class="pf-content"]', + ), + 'strip' => array( + '//div[@class="printfriendly"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mokepon.smackjeeves.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mokepon.smackjeeves.com.php new file mode 100644 index 0000000000..1ddcd407a6 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mokepon.smackjeeves.com.php @@ -0,0 +1,10 @@ + array( + '%.*%' => array( + 'test_url' => 'http://mokepon.smackjeeves.com/comics/2120096/chapter-9-page-68/', + 'body' => array('//*[@id="comic_area_inner"]/img | //*[@id="comic_area_inner"]/a/img'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/monandroid.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/monandroid.com.php new file mode 100644 index 0000000000..f87560e2ef --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/monandroid.com.php @@ -0,0 +1,13 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.monandroid.com/blog/tutoriel-avance-activer-le-stockage-fusionne-sur-android-6-marshamallow-t12.html', + 'body' => array( + '//div[@class="blog-post-body"]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/monwindows.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/monwindows.com.php new file mode 100644 index 0000000000..b2b24d7406 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/monwindows.com.php @@ -0,0 +1,13 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.monwindows.com/tout-savoir-sur-le-centre-d-action-de-windows-phone-8-1-t40574.html', + 'body' => array( + '//div[@class="blog-post-body"]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/moya-planeta.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/moya-planeta.ru.php new file mode 100644 index 0000000000..dd84284494 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/moya-planeta.ru.php @@ -0,0 +1,21 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.moya-planeta.ru/travel/view/chto_yaponcu_horosho_russkomu_ne_ponyat_20432/', + 'body' => array( + '//div[@class="full_object"]', + ), + 'strip' => array( + '//div[@class="full_object_panel object_panel"]', + '//div[@class="full_object_panel_geo object_panel"]', + '//div[@class="full_object_title"]', + '//div[@class="full_object_social_likes"]', + '//div[@class="full_object_planeta_likes"]', + '//div[@class="full_object_go2comments"]', + '//div[@id="yandex_ad_R-163191-3"]', + '//div[@class="full_object_shop_article_recommend"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mrlovenstein.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mrlovenstein.com.php new file mode 100644 index 0000000000..b971091f40 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mrlovenstein.com.php @@ -0,0 +1,9 @@ + array( + '%.*%' => array( + '%alt="(.+)" */>%' => '/>
$1', + '%\.png%' => '_rollover.png', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/muckrock.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/muckrock.com.php new file mode 100644 index 0000000000..9e354a36d6 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/muckrock.com.php @@ -0,0 +1,20 @@ + array( + '%.*%' => array( + 'test_url' => 'https://www.muckrock.com/news/archives/2016/jan/13/5-concerns-private-prisons/', + 'body' => array( + '//div[@class="content"]', + ), + 'strip' => array( + '//div[@class="newsletter-widget"]', + '//div[@class="contributors"]', + '//time', + '//h1', + '//div[@class="secondary"]', + '//aside', + '//div[@class="articles__related"]' + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mynorthshorenow.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mynorthshorenow.com.php new file mode 100644 index 0000000000..b6309157d7 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mynorthshorenow.com.php @@ -0,0 +1,27 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.mynorthshorenow.com/story/news/local/fox-point/2017/04/04/fox-point-building-board-approves-dunwood-commons-project/99875570/', + 'body' => array( + '//div[@itemprop="articleBody"]', + ), + 'strip' => array( + '//h1', + '//iframe', + '//span[@class="mycapture-small-btn mycapture-btn-with-text mycapture-expandable-photo-btn-small js-mycapture-btn-small"]', + '//div[@class="close-wrap"]', + '//div[contains(@class,"ui-video-wrapper")]', + '//div[contains(@class,"media-mob")]', + '//div[contains(@class,"left-mob")]', + '//div[contains(@class,"nerdbox")]', + '//p/span', + '//div[contains(@class,"oembed-asset")]', + '//*[contains(@class,"share")]', + '//div[contains(@class,"gallery-asset")]', + '//div[contains(@class,"oembed-asset")]', + '//div[@class="article-print-url"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nakedCapitalism.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nakedCapitalism.php new file mode 100644 index 0000000000..ec2d5fd50c --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nakedCapitalism.php @@ -0,0 +1,11 @@ + array( + '%.*%' => array( + 'test_url' => 'http://feedproxy.google.com/~r/NakedCapitalism/~3/JOBxEHxN8ZI/mark-blyth-liberalism-undermined-democracy-failure-democratic-party.html', + 'body' => array( + '//div[@class="pf-content"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nasa.gov.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nasa.gov.php new file mode 100644 index 0000000000..c6692d071e --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nasa.gov.php @@ -0,0 +1,14 @@ + array( + '%.*%' => array( + 'test_url' => 'https://www.nasa.gov/image-feature/jpl/pia20514/coy-dione', + 'body' => array( + '//div[@class="article-body"]', + ), + 'strip' => array( + '//div[@class="title-bar"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nat-geo.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nat-geo.ru.php new file mode 100644 index 0000000000..1a42d997dc --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nat-geo.ru.php @@ -0,0 +1,11 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.nat-geo.ru/fact/868093-knidos-antichnyy-naukograd/', + 'body' => array( + '//div[@class="article-inner-text"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nationaljournal.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nationaljournal.com.php new file mode 100644 index 0000000000..5e612bef16 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nationaljournal.com.php @@ -0,0 +1,15 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.nationaljournal.com/s/354962/south-carolina-evangelicals-outstrip-establishment?mref=home_top_main', + 'body' => array( + '//div[@class="section-body"]', + ), + 'strip' => array( + '//*[contains(@class, "-related")]', + '//*[contains(@class, "social")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nature.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nature.com.php new file mode 100644 index 0000000000..6b9e87f45a --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nature.com.php @@ -0,0 +1,13 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.nature.com/doifinder/10.1038/nature.2015.18340', + 'body' => array( + '//div[contains(@class,"main-content")]', + ), + 'strip' => array(), + ), + ), +); + diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nba.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nba.com.php new file mode 100644 index 0000000000..c8ea926f13 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nba.com.php @@ -0,0 +1,15 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.nba.com/2015/news/09/25/knicks-jackson-to-spend-more-time-around-coaching-staff.ap/index.html?rss=true', + 'body' => array( + '//div[@class="paragraphs"]', + ), + 'strip' => array( + '//div[@id="nbaArticleSocialWrapper_bot"]', + '//h5', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nedroid.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nedroid.com.php new file mode 100644 index 0000000000..3214c62ae3 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nedroid.com.php @@ -0,0 +1,8 @@ + array( + '%.*%' => array( + '%title="(.+)" */>%' => '/>
$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/networkworld.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/networkworld.com.php new file mode 100644 index 0000000000..185243541d --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/networkworld.com.php @@ -0,0 +1,20 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.networkworld.com/article/3020585/security/the-incident-response-fab-five.html', + 'body' => array( + '//figure/img[@class="hero-img"]', + '//section[@class="deck"]', + '//div[@itemprop="articleBody"] | //div[@itemprop="reviewBody"]', + '//div[@class="carousel-inside-crop"]', + ), + 'strip' => array( + '//script', + '//aside', + '//div[@class="credit"]', + '//div[@class="view-large"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/neustadt-ticker.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/neustadt-ticker.de.php new file mode 100644 index 0000000000..e0c0d19d35 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/neustadt-ticker.de.php @@ -0,0 +1,15 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.neustadt-ticker.de/41302/alltag/kultur/demo-auf-der-boehmischen', + 'body' => array( + '//div[@class="entry-content"]', + ), + 'strip' => array( + '//*[contains(@class, "sharedaddy")]', + '//*[contains(@class, "yarpp-related")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nextinpact.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nextinpact.com.php new file mode 100644 index 0000000000..29dd9d6b9a --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nextinpact.com.php @@ -0,0 +1,18 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.nextinpact.com/news/101122-3d-nand-intel-lance-six-nouvelles-gammes-ssd-pour-tous-usages.htm', + 'body' => array( + '//div[@class="container_article"]', + ), + 'strip' => array( + '//div[@class="infos_article"]', + '//div[@id="actu_auteur"]', + '//div[@id="soutenir_journaliste"]', + '//section[@id="bandeau_abonnez_vous"]', + '//br' + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/niceteethcomic.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/niceteethcomic.com.php new file mode 100644 index 0000000000..f41e44385d --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/niceteethcomic.com.php @@ -0,0 +1,10 @@ + array( + '%/archives.*%' => array( + 'test_url' => 'http://niceteethcomic.com/archives/page119/', + 'body' => array('//*[@class="comicpane"]/a/img'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nichtlustig.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nichtlustig.de.php new file mode 100644 index 0000000000..4d083f98ab --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nichtlustig.de.php @@ -0,0 +1,8 @@ + array( + '%.*%' => array( + '%.*static.nichtlustig.de/comics/full/(\\d+).*%s' => '', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/oglaf.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/oglaf.com.php new file mode 100644 index 0000000000..8b2b5b65ee --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/oglaf.com.php @@ -0,0 +1,19 @@ + array( + '%.*%' => array( + 'body' => array( + '//img[@id="strip"]', + '//a/div[@id="nx"]/..', + ), + 'strip' => array(), + 'test_url' => 'http://oglaf.com/slodging/', + ), + ), + 'filter' => array( + '%.*%' => array( + '%alt="(.+)" title="(.+)" */>%' => '/>
$1
$2
', + '%%' => 'Next page', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/onhax.net.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/onhax.net.php new file mode 100644 index 0000000000..213849d876 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/onhax.net.php @@ -0,0 +1,15 @@ + array( + '%.*%' => array( + 'test_url' => 'http://onhax.net/process-lasso-8-9-1-4-pro-key-portable-is-here-latest', + 'body' => array( + '//div[@class="postcontent"]', + ), + 'strip' => array( + '//*[@class="sharedaddy sd-sharing-enabled"]', + '//*[@class="yarpp-related"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/onmilwaukee.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/onmilwaukee.php new file mode 100644 index 0000000000..f66ac4b8e4 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/onmilwaukee.php @@ -0,0 +1,24 @@ + array( + '%.*%' => array( + 'test_url' => 'http://onmilwaukee.com/movies/articles/downerspelunking.html', + 'body' => array( + '//article[contains(@class, "show")]', + ), + 'strip' => array( + '//h1', + '//div[contains(@class,"-ad")]', + '//div[contains(@class,"_ad")]', + '//div[@id="pub_wrapper"]', + '//div[contains(@class,"share_tools")]', + '//div[@class="clearfix"]', + '//div[contains(@class,"image_control")]', + '//section[@class="ribboned"]', + '//div[contains(@class,"sidebar")]', + '//aside[@class="article_tag_list"]', + '//section[contains(@id,"more_posts")]' + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/openculture.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/openculture.com.php new file mode 100644 index 0000000000..84f2beede0 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/openculture.com.php @@ -0,0 +1,11 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.openculture.com/2017/03/are-we-living-inside-a-computer-simulation-watch-the-simulation-argument.html', + 'body' => array( + '//div[@class="entry"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/opennet.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/opennet.ru.php new file mode 100644 index 0000000000..1fb7722b1f --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/opennet.ru.php @@ -0,0 +1,13 @@ + array( + '%.*%' => array( + 'test_url' => 'https://www.opennet.ru/opennews/art.shtml?num=46549', + 'body' => array( + '//*[@id="r_memo"]', + ), + 'strip' => array( + ), + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/openrightsgroup.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/openrightsgroup.org.php new file mode 100644 index 0000000000..94139a725d --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/openrightsgroup.org.php @@ -0,0 +1,20 @@ + array( + '%.*%' => array( + 'test_url' => 'https://www.openrightsgroup.org/blog/2014/3-days-to-go-till-orgcon2014', + 'body' => array( + '//div[contains(@class, "content")]/div', + ), + 'strip' => array( + '//h2[1]', + '//div[@class="info"]', + '//div[@class="tags"]', + '//div[@class="comments"]', + '//div[@class="breadcrumbs"]', + '//h1[@class="pageTitle"]', + '//p[@class="bookmarkThis"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/opensource.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/opensource.com.php new file mode 100644 index 0000000000..60f3577bad --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/opensource.com.php @@ -0,0 +1,22 @@ + array( + '%.*%' => array( + 'test_url' => 'http://opensource.com/life/15/10/how-internet-things-will-change-way-we-think', + 'body' => array( + '//div[@id="article-template"]', + ), + 'strip' => array( + '//div[contains(@class,"os-article__sidebar")]', + '//div[@class="panel-pane pane-node-title"]', + '//div[@class="panel-pane pane-os-article-byline"]', + '//ul', + '//div[contains(@class,"-license")]', + '//div[contains(@class,"-tags")]', + '//div[@class="panel-pane pane-os-article-byline"]', + '//div[@class="os-article__content-below"]', + '//div[@id="comments"]' + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/optipess.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/optipess.com.php new file mode 100644 index 0000000000..3214c62ae3 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/optipess.com.php @@ -0,0 +1,8 @@ + array( + '%.*%' => array( + '%title="(.+)" */>%' => '/>
$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/osnews.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/osnews.com.php new file mode 100644 index 0000000000..1d1396c804 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/osnews.com.php @@ -0,0 +1,11 @@ + array( + '%.*%' => array( + 'test_url' => 'http://osnews.com/story/28863/Google_said_to_be_under_US_antitrust_scrutiny_over_Android', + 'body' => array( + '//div[@class="newscontent1"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/pastebin.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/pastebin.com.php new file mode 100644 index 0000000000..b20bf41c4f --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/pastebin.com.php @@ -0,0 +1,13 @@ + array( + '%.*%' => array( + 'test_url' => 'http://pastebin.com/ed1pP9Ak', + 'body' => array( + '//div[@class="text"]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/peebleslab.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/peebleslab.com.php new file mode 100644 index 0000000000..ce4891d17a --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/peebleslab.com.php @@ -0,0 +1,9 @@ + array( + '%.*%' => array( + // the extra space is required to strip the title cleanly + '%title="(.+) " */>%' => '/>
$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/penny-arcade.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/penny-arcade.com.php new file mode 100644 index 0000000000..dd39983bdb --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/penny-arcade.com.php @@ -0,0 +1,21 @@ + array( + '%/news/.*%' => array( + 'test_url' => 'http://penny-arcade.com/news/post/2015/04/15/101-part-two', + 'body' => array( + '//*[@class="postBody"]/*', + ), + 'strip' => array( + ), + ), + '%/comic/.*%' => array( + 'test_url' => 'http://penny-arcade.com/comic/2015/04/15', + 'body' => array( + '//*[@id="comicFrame"]/a/img', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/pixelbeat.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/pixelbeat.org.php new file mode 100644 index 0000000000..fa9052eaee --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/pixelbeat.org.php @@ -0,0 +1,12 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.pixelbeat.org/programming/sigpipe_handling.html#1425573246', + 'body' => array( + '//div[@class="contentText"]', + ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/plus.google.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/plus.google.com.php new file mode 100644 index 0000000000..5e48a6cf27 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/plus.google.com.php @@ -0,0 +1,11 @@ + array( + '%.*%' => array( + 'test_url' => 'https://plus.google.com/+LarryPage/posts/Lh8SKC6sED1', + 'body' => array( + '//div[@role="article"]/div[contains(@class, "eE")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/popstrip.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/popstrip.com.php new file mode 100644 index 0000000000..801a2814d1 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/popstrip.com.php @@ -0,0 +1,8 @@ + array( + '%.*%' => array( + '%( '$1$2$1bonus.png"/>', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/publicpolicyforum.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/publicpolicyforum.org.php new file mode 100644 index 0000000000..5dc8be885d --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/publicpolicyforum.org.php @@ -0,0 +1,15 @@ + array( + '%.*%' => array( + 'test_url' => 'https://publicpolicyforum.org/blog/going-extra-mile', + 'body' => array( + '//div[contains(@class,"field-name-post-date")]', + '//div[contains(@class,"field-name-body")]', + ), + 'strip' => array( + '//img[@src="http://publicpolicyforum.org/sites/default/files/logo3.jpg"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/publy.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/publy.ru.php new file mode 100644 index 0000000000..bcfeeb99e4 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/publy.ru.php @@ -0,0 +1,24 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.publy.ru/post/19988', + 'body' => array( + '//div[@class="singlepost"]', + ), + 'strip' => array( + '//script', + '//form', + '//style', + '//*[@class="featured"]', + '//*[@class="toc_white no_bullets"]', + '//*[@class="toc_title"]', + '//*[@class="pba"]', + '//*[@class="comments"]', + '//*[contains(@class, "g-single")]', + '//*[@class="ts-fab-wrapper"]', + '//*[contains(@class, "wp_rp_wrap")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/putaindecode.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/putaindecode.fr.php new file mode 100644 index 0000000000..9fa5568c3c --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/putaindecode.fr.php @@ -0,0 +1,16 @@ + array( + '%.*%' => array( + 'test_url' => 'http://putaindecode.fr/posts/js/etat-lieux-js-modulaire-front/', + 'body' => array( + '//*[@class="putainde-Post-md"]', + ), + 'strip' => array( + '//*[contains(@class, "inlineimg")]', + '//*[contains(@class, "comment-respond")]', + '//header', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/recode.net.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/recode.net.php new file mode 100644 index 0000000000..343cd12fe0 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/recode.net.php @@ -0,0 +1,20 @@ + array( + '%.*%' => array( + 'test_url' => 'http://recode.net/2015/09/26/big-tech-rolls-out-red-carpet-for-indian-prime-minister-lobbies-behind-closed-doors/', + 'body' => array( + '//img[contains(@class,"attachment-large")]', + '//div[contains(@class,"postarea")]', + '//li[@class,"author"]', + ), + 'strip' => array( + '//script', + '//div[contains(@class,"sharedaddy")]', + '//div[@class="post-send-off"]', + '//div[@class="large-12 columns"]', + '//div[contains(@class,"inner-related-article")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/retractionwatch.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/retractionwatch.com.php new file mode 100644 index 0000000000..b97c73eda4 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/retractionwatch.com.php @@ -0,0 +1,18 @@ + array( + '%.*%' => array( + 'test_url' => 'http://retractionwatch.com/2015/11/12/psychologist-jens-forster-settles-case-by-agreeing-to-2-retractions/', + 'body' => array( + '//*[@class="main"]', + '//*[@class="entry-content"]', + ), + 'strip' => array( + '//*[contains(@class, "sharedaddy")]', + '//*[contains(@class, "jp-relatedposts")]', + '//p[@class="p1"]', + ) + ) + ) +); + diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/rockpapershotgun.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/rockpapershotgun.com.php new file mode 100644 index 0000000000..71110786dc --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/rockpapershotgun.com.php @@ -0,0 +1,11 @@ + array( + '%.*%' => array( + 'test_url' => 'https://www.rockpapershotgun.com/2016/08/26/the-divisions-expansions-delayed-to-improve-the-game/', + 'body' => array( + '//div[@class="entry"]', + ) + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/rue89.nouvelobs.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/rue89.nouvelobs.com.php new file mode 100644 index 0000000000..cb9116a34e --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/rue89.nouvelobs.com.php @@ -0,0 +1,13 @@ + array( + '%.*%' => array( + 'test_url' => 'http://rue89.feedsportal.com/c/33822/f/608948/s/30999fa0/sc/24/l/0L0Srue890N0C20A130C0A80C30A0Cfaisait0Eboris0Eboillon0Eex0Esarko0Eboy0E350A0E0A0A0A0Eeuros0Egare0Enord0E245315/story01.htm', + 'body' => array( + '//*[@id="article"]/div[contains(@class, "content")]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/rugbyrama.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/rugbyrama.fr.php new file mode 100644 index 0000000000..9915c234d2 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/rugbyrama.fr.php @@ -0,0 +1,20 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.rugbyrama.fr/rugby/top-14/2015-2016/top-14-hayman-coupe-du-monde-finale-2012-lutte.-voici-levan-chilachava-toulon_sto5283863/story.shtml', + 'body' => array( + '//div[@class="storyfull__content"]', + ), + 'strip' => array( + '//script', + '//form', + '//style', + '//*[@class="share-buttons"]', + '//*[@class="ad"]', + '//*[@class="hide-desktop"]', + '//*[@id="tracking_img"]', + ) + ) + ) +); \ No newline at end of file diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/satwcomic.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/satwcomic.com.php new file mode 100644 index 0000000000..d63fc11ccc --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/satwcomic.com.php @@ -0,0 +1,13 @@ + array( + '%.*%' => array( + 'test_url' => 'http://satwcomic.com/day-at-the-beach', + 'body' => array( + '//div[@class="container"]/center/a/img', + '//span[@itemprop="articleBody"]', + ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/scrumalliance.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/scrumalliance.org.php new file mode 100644 index 0000000000..7835fd9ef8 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/scrumalliance.org.php @@ -0,0 +1,12 @@ + array( + '%.*%' => array( + 'test_url' => 'https://www.scrumalliance.org/community/articles/2015/march/an-introduction-to-agile-project-intake?feed=articles', + 'body' => array( + '//div[@class="article_content"]', + ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/securityfocus.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/securityfocus.com.php new file mode 100644 index 0000000000..0104514888 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/securityfocus.com.php @@ -0,0 +1,17 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.securityfocus.com/archive/1/540139', + 'body' => array( + '//div[@id="vulnerability"]', + '//div[@class="comments_reply"]', + ), + 'strip' => array( + '//span[@class="title"]', + '//div[@id="logo_new"]', + '//div[@id="bannerAd"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/sentfromthemoon.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/sentfromthemoon.com.php new file mode 100644 index 0000000000..f4354179a5 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/sentfromthemoon.com.php @@ -0,0 +1,18 @@ + array( + '%.*%' => array( + 'body' => array( + '//div[@class="comicpane"]/a/img', + '//div[@class="entry"]', + ), + 'strip' => array(), + 'test_url' => 'http://sentfromthemoon.com/archives/1417', + ), + ), + 'filter' => array( + '%.*%' => array( + '%title="(.+)" */>%' => '/>
$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/sitepoint.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/sitepoint.com.php new file mode 100644 index 0000000000..ab0eb7d4b2 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/sitepoint.com.php @@ -0,0 +1,13 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.sitepoint.com/creating-hello-world-app-swift/', + 'body' => array( + '//section[@class="article_body"]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/slashdot.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/slashdot.org.php new file mode 100644 index 0000000000..89ced8b6f2 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/slashdot.org.php @@ -0,0 +1,11 @@ + array( + '%.*%' => array( + 'test_url' => 'http://science.slashdot.org/story/15/04/20/0528253/pull-top-can-tabs-at-50-reach-historic-archaeological-status', + 'body' => array( + '//article/div[@class="body"] | //article[@class="layout-article"]/div[@class="elips"]', ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smallhousebliss.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smallhousebliss.com.php new file mode 100644 index 0000000000..8c13c44c40 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smallhousebliss.com.php @@ -0,0 +1,19 @@ + array( + '%.*%' => array( + 'test_url' => 'http://smallhousebliss.com/2013/08/29/house-g-by-lode-architecture/', + 'body' => array( + '//div[@class="post-content"]', + ), + 'strip' => array( + '//*[contains(@class, "gallery")]', + '//*[contains(@class, "share")]', + '//*[contains(@class, "wpcnt")]', + '//*[contains(@class, "meta")]', + '//*[contains(@class, "postitle")]', + '//*[@id="nav-below"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smarthomewelt.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smarthomewelt.de.php new file mode 100644 index 0000000000..7463abc789 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smarthomewelt.de.php @@ -0,0 +1,10 @@ + array( + '%.*%' => array( + 'test_url' => 'http://smarthomewelt.de/apple-tv-amazon-echo-smart-home/', + 'body' => array('//div[@class="entry-inner"]/p | //div[@class="entry-inner"]/div[contains(@class,"wp-caption")]'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smashingmagazine.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smashingmagazine.com.php new file mode 100644 index 0000000000..cbe10726d2 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smashingmagazine.com.php @@ -0,0 +1,10 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.smashingmagazine.com/2015/04/17/using-sketch-for-responsive-web-design-case-study/', + 'body' => array('//article[contains(@class,"post")]/p'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smbc-comics.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smbc-comics.com.php new file mode 100644 index 0000000000..42262dc96c --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smbc-comics.com.php @@ -0,0 +1,14 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.smbc-comics.com/comic/the-troll-toll', + 'body' => array( + '//div[@id="cc-comicbody"]', + '//div[@id="aftercomic"]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/snopes.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/snopes.com.php new file mode 100644 index 0000000000..b0fe655701 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/snopes.com.php @@ -0,0 +1,22 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.snopes.com/bacca-brides-on-tour/', + 'body' => array( + '//article', + ), + 'strip' => array( + '//span[@itemprop="author"]', + '//div[contains(@class,"author-")]', + '//h1', + '//style', + '//div[contains(@class,"socialShares")]', + '//div[contains(@class,"ad-unit")]', + '//aside', + '//div[contains(@class,"boomtrain")]', + '//footer' + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/soundandvision.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/soundandvision.com.php new file mode 100644 index 0000000000..6448bb056b --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/soundandvision.com.php @@ -0,0 +1,21 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.soundandvision.com/content/james-guthrie-mixing-roger-waters-and-pink-floyd-51', + 'body' => array( + '//div[@id="left"]', + ), + 'strip' => array( + '//div[@class="meta"]', + '//div[@class="ratingsbox"]', + '//h1', + '//h2', + '//addthis', + '//comment-links', + '//div[@class="book-navigation"]', + '//div[@class="comment-links"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/spiegel.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/spiegel.de.php new file mode 100644 index 0000000000..e2e3db0006 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/spiegel.de.php @@ -0,0 +1,11 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.spiegel.de/politik/ausland/afrika-angola-geht-gegen-islam-vor-und-schliesst-moscheen-a-935788.html', + 'body' => array( + '//div[contains(@class, "article-section")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/stereophile.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/stereophile.com.php new file mode 100644 index 0000000000..8e410be4ba --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/stereophile.com.php @@ -0,0 +1,11 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.stereophile.com/content/2015-rocky-mountain-audio-fest-starts-friday', + 'body' => array( + '//div[@class="content clear-block"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/stupidfox.net.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/stupidfox.net.php new file mode 100644 index 0000000000..61182d729b --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/stupidfox.net.php @@ -0,0 +1,13 @@ + array( + '%.*%' => array( + 'test_url' => 'http://stupidfox.net/134-sleepy-time', + 'body' => array( + '//div[@class="comicmid"]/center/a/img', + '//div[@class="stand_high"]', + ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/subtraction.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/subtraction.com.php new file mode 100644 index 0000000000..6d744277e8 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/subtraction.com.php @@ -0,0 +1,15 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.subtraction.com/2015/06/06/time-lapse-video-of-one-world-trade-center/', + 'body' => array('//article/div[@class="entry-content"]'), + 'strip' => array(), + ), + ), + 'filter' => array( + '%.*%' => array( + '%\+%' => '', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/sz.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/sz.de.php new file mode 100644 index 0000000000..90bde5a984 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/sz.de.php @@ -0,0 +1,10 @@ + array( + '%.*%' => array( + 'test_url' => 'http://sz.de/1.2443161', + 'body' => array('//article[@id="sitecontent"]/section[@class="topenrichment"]//img | //article[@id="sitecontent"]/section[@class="body"]/section[@class="authors"]/preceding-sibling::*[not(contains(@class, "ad"))]'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/takprosto.cc.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/takprosto.cc.php new file mode 100644 index 0000000000..624ef9078f --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/takprosto.cc.php @@ -0,0 +1,21 @@ + array( + '%.*%' => array( + 'test_url' => 'http://takprosto.cc/kokteyl-dlya-pohudeniya-v-domashnih-usloviyah/', + 'body' => array( + '//div[contains(@class, "entry-contentt")]', + ), + 'strip' => array( + '//script', + '//form', + '//style', + '//*[@class="views_post"]', + '//*[contains(@class, "mailchimp-box")]', + '//*[contains(@class, "essb_links")]', + '//*[contains(@rel, "nofollow")]', + '//*[contains(@class, "ads")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/techcrunch.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/techcrunch.com.php new file mode 100644 index 0000000000..d6e620f69a --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/techcrunch.com.php @@ -0,0 +1,15 @@ + array( + '%.*%' => array( + 'test_url' => 'http://techcrunch.com/2013/08/31/indias-visa-maze/', + 'body' => array( + '//div[contains(@class, "media-container")]', + '//div[@class="body-copy"]', + ), + 'strip' => array( + '//*[contains(@class, "module-crunchbase")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/the-ebook-reader.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/the-ebook-reader.com.php new file mode 100644 index 0000000000..3b01eb9690 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/the-ebook-reader.com.php @@ -0,0 +1,15 @@ + array( + '%.*%' => array( + 'test_url' => 'http://blog.the-ebook-reader.com/2015/09/25/kobo-glo-hd-and-kobo-touch-2-0-covers-and-cases-roundup/', + 'body' => array( + '//div[@class="entry"]', + ), + 'strip' => array( + '//div[@id="share"]', + '//div[contains(@class,"ItemCenter")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theatlantic.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theatlantic.com.php new file mode 100644 index 0000000000..bfad4ab22e --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theatlantic.com.php @@ -0,0 +1,23 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.theatlantic.com/politics/archive/2015/09/what-does-it-mean-to-lament-the-poor-inside-panem/407317/', + 'body' => array( + '//picture[@class="img"]', + '//figure/figcaption/span', + '//div/p[@itemprop="description"]', + '//div[@class="article-body"]', + '//ul[@class="photos"]', + ), + 'strip' => array( + '//aside[@class="callout"]', + '//span[@class="credit"]', + '//figcaption[@class="credit"]', + '//aside[contains(@class,"partner-box")]', + '//div[contains(@class,"ad")]', + '//a[contains(@class,"social-icon")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theawkwardyeti.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theawkwardyeti.com.php new file mode 100644 index 0000000000..fd4f3d501e --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theawkwardyeti.com.php @@ -0,0 +1,12 @@ + array( + '%/comic/.*%' => array( + 'test_url' => 'http://theawkwardyeti.com/comic/things-to-do/', + 'body' => array( + '//div[@id="comic"]' + ), + 'strip' => array() + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thecodinglove.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thecodinglove.com.php new file mode 100644 index 0000000000..c6ec5bf550 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thecodinglove.com.php @@ -0,0 +1,10 @@ + array( + '%.*%' => array( + 'test_url' => 'http://thecodinglove.com/post/116897934767', + 'body' => array('//div[@class="bodytype"]'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thedoghousediaries.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thedoghousediaries.com.php new file mode 100644 index 0000000000..d2f840d789 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thedoghousediaries.com.php @@ -0,0 +1,18 @@ + array( + '%.*%' => array( + 'body' => array( + '//div[@class="comicpane"]/a/img', + '//div[@class="entry"]', + ), + 'strip' => array(), + 'test_url' => 'http://thedoghousediaries.com/6023', + ), + ), + 'filter' => array( + '%.*%' => array( + '%title="(.+)" */>%' => '/>
$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thegamercat.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thegamercat.com.php new file mode 100644 index 0000000000..f9b4637bb4 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thegamercat.com.php @@ -0,0 +1,10 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.thegamercat.com/comic/just-no/', + 'body' => array('//div[@id="comic"] | //div[@class="post-content"]/div[@class="entry"]/p'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thehindu.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thehindu.com.php new file mode 100644 index 0000000000..1e6735ba44 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thehindu.com.php @@ -0,0 +1,19 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.thehindu.com/sci-tech/science/why-is-the-shape-of-cells-in-a-honeycomb-always-hexagonal/article7692306.ece?utm_source=RSS_Feed&utm_medium=RSS&utm_campaign=RSS_Syndication', + 'body' => array( + '//div/img[@class="main-image"]', + '//div[@class="photo-caption"]', + '//div[@class="articleLead"]', + '//p', + '//span[@class="upper"]', + ), + 'strip' => array( + '//div[@id="articleKeywords"]', + '//div[@class="photo-source"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thelocal.se.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thelocal.se.php new file mode 100644 index 0000000000..c3ec250c85 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thelocal.se.php @@ -0,0 +1,17 @@ + array( + '%.*%' => array( + 'test_url' => 'www.thelocal.se/20161219/this-swede-can-memorize-hundreds-of-numbers-in-only-five-minutes', + 'body' => array( + '//div[@id="article-photo"]', + '//div[@id="article-description"]', + '//div[@id="article-body"]', + ), + 'strip' => array( + '//div[@id="article-info-middle"]', + ) + ) + ) +); + diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/themerepublic.net.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/themerepublic.net.php new file mode 100644 index 0000000000..bc47b27823 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/themerepublic.net.php @@ -0,0 +1,10 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.themerepublic.net/2015/04/david-lopez-pitoko.html?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+blogspot%2FDngUJ+%28Theme+Republic%29&utm_content=FeedBurner', + 'body' => array('//*[@class="post-body"]'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/themoscowtimes.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/themoscowtimes.com.php new file mode 100644 index 0000000000..0f5bf75e2e --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/themoscowtimes.com.php @@ -0,0 +1,18 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.themoscowtimes.com/business/article/535500.html', + 'body' => array( + '//div[@class="article_main_img"]', + '//div[@class="article_text"]', + ), + 'strip' => array( + '//div[@class="articlebottom"]', + '//p/b', + '//p/a[contains(@href, "/article.php?id=")]', + '//div[@class="disqus_wrap"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thenewslens.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thenewslens.com.php new file mode 100644 index 0000000000..7538170752 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thenewslens.com.php @@ -0,0 +1,21 @@ + array( + '%.*%' => array( + 'test_url' => 'http://international.thenewslens.com/post/255032/', + 'body' => array( + '//div[@class="article-section"]', + ), + 'strip' => array( + '//div[contains(@class,"ad-")]', + '//div[@class="article-title-box"]', + '//div[@class="function-box"]', + '//p/span', + '//aside', + '//footer', + '//div[@class="article-infoBot-box"]', + '//div[contains(@class,"standard-container")]' + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theodd1sout.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theodd1sout.com.php new file mode 100644 index 0000000000..d06ed12419 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theodd1sout.com.php @@ -0,0 +1,8 @@ + array( + '%.*%' => array( + '%-150x150%' => '', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theonion.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theonion.com.php new file mode 100644 index 0000000000..acbfd36b6d --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theonion.com.php @@ -0,0 +1,12 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.theonion.com/article/wild-eyed-jim-harbaugh-informs-players-they-must-k-51397?utm_medium=RSS&utm_campaign=feeds', + 'body' => array( + '//div[@class="content-masthead"]/figure/div/noscript/img', + '//div[@class="content-text"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thestandard.com.hk.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thestandard.com.hk.php new file mode 100644 index 0000000000..1163b34589 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thestandard.com.hk.php @@ -0,0 +1,22 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.thestandard.com.hk/breaking_news_detail.asp?id=67156', + 'body' => array( + '//table/tr/td/span[@class="bodyCopy"]', + ), + 'strip' => array( + '//script', + '//br', + '//map[@name="gif_bar"]', + '//img[contains(@usemap,"gif_bar")]', + '//a', + '//span[@class="bodyHeadline"]', + '//i', + '//b', + '//table', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/threepanelsoul.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/threepanelsoul.com.php new file mode 100644 index 0000000000..4af6196ed4 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/threepanelsoul.com.php @@ -0,0 +1,11 @@ + array( + '%.*%' => array( + 'body' => array( + '//img[@id="cc-comic"]', + ), + 'test_url' => 'http://www.threepanelsoul.com/comic/uncloaking', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/timesofindia.indiatimes.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/timesofindia.indiatimes.com.php new file mode 100644 index 0000000000..192468063b --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/timesofindia.indiatimes.com.php @@ -0,0 +1,14 @@ + array( + '%.*%' => array( + 'test_url' => 'http://timesofindia.indiatimes.com/city/mangaluru/Adani-UPCL-to-release-CSR-grant-of-Rs-3-74-crore-to-YellurGram-Panchayat/articleshow/50512116.cms', + 'body' => array( + '//div[@class="article_content clearfix"]', + '//section[@class="highlight clearfix"]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/travel-dealz.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/travel-dealz.de.php new file mode 100644 index 0000000000..4ee4fcdc3c --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/travel-dealz.de.php @@ -0,0 +1,15 @@ + array( + '%^/blog.*%' => array( + 'test_url' => 'http://travel-dealz.de/blog/venere-gutschein/', + 'body' => array('//div[@class="post-entry"]'), + 'strip' => array( + '//*[@id="jp-relatedposts"]', + '//*[@class="post-meta"]', + '//*[@class="post-data"]', + '//*[@id="author-meta"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/treehugger.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/treehugger.com.php new file mode 100644 index 0000000000..55eb7e015b --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/treehugger.com.php @@ -0,0 +1,14 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.treehugger.com/uncategorized/top-ten-posts-week-bunnies-2.html', + 'body' => array( + '//div[contains(@class, "promo-image")]', + '//div[contains(@id, "entry-body")]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/treelobsters.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/treelobsters.com.php new file mode 100644 index 0000000000..3214c62ae3 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/treelobsters.com.php @@ -0,0 +1,8 @@ + array( + '%.*%' => array( + '%title="(.+)" */>%' => '/>
$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/twogag.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/twogag.com.php new file mode 100644 index 0000000000..79f4f62e38 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/twogag.com.php @@ -0,0 +1,8 @@ + array( + '%.*%' => array( + '%http://www.twogag.com/comics-rss/([^.]+)\\.jpg%' => 'http://www.twogag.com/comics/$1.jpg', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/twokinds.keenspot.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/twokinds.keenspot.com.php new file mode 100644 index 0000000000..3428fcb4cb --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/twokinds.keenspot.com.php @@ -0,0 +1,10 @@ + array( + '%.*%' => array( + 'test_url' => 'http://twokinds.keenspot.com/archive.php?p=0', + 'body' => array('//*[@class="comic"]/div/a/img | //*[@class="comic"]/div/img | //*[@id="cg_img"]/img | //*[@id="cg_img"]/a/img'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/undeadly.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/undeadly.org.php new file mode 100644 index 0000000000..8b15900a9a --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/undeadly.org.php @@ -0,0 +1,14 @@ + array( + '%.*%' => array( + 'test_url' => 'http://undeadly.org/cgi?action=article&sid=20141101181155', + 'body' => array( + '/html/body/table[3]/tbody/tr/td[1]/table[2]/tr/td[1]', + ), + 'strip' => array( + '//font', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/upi.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/upi.com.php new file mode 100644 index 0000000000..ec8d1a1a00 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/upi.com.php @@ -0,0 +1,15 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.upi.com/Top_News/US/2015/09/26/Tech-giants-Hollywood-stars-among-guests-at-state-dinner-for-Chinas-Xi-Jinping/4541443281006/', + 'body' => array( + '//div[@class="img"]', + '//div/article[@itemprop="articleBody"]', + ), + 'strip' => array( + '//div[@align="center"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/usatoday.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/usatoday.com.php new file mode 100644 index 0000000000..edd6aa4495 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/usatoday.com.php @@ -0,0 +1,27 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.usatoday.com/story/life/music/2017/02/13/things-you-should-know-happened-grammy-awards-2017/97833734/', + 'body' => array( + '//div[@itemprop="articleBody"]', + ), + 'strip' => array( + '//script', + '//h1', + '//iframe', + '//span[@class="mycapture-small-btn mycapture-btn-with-text mycapture-expandable-photo-btn-small js-mycapture-btn-small"]', + '//div[@class="close-wrap"]', + '//div[contains(@class,"ui-video-wrapper")]', + '//div[contains(@class,"media-mob")]', + '//div[contains(@class,"left-mob")]', + '//div[contains(@class,"nerdbox")]', + '//div[contains(@class,"oembed-asset")]', + '//*[contains(@class,"share")]', + '//div[contains(@class,"gallery-asset")]', + '//div[contains(@class,"oembed-asset")]', + '//div[@class="article-print-url"]' + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/version2.dk.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/version2.dk.php new file mode 100644 index 0000000000..a6d49f2e7c --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/version2.dk.php @@ -0,0 +1,12 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.version2.dk/artikel/surface-pro-2-fungerer-bedre-til-arbejde-end-fornoejelse-55195', + 'body' => array( + '//section[contains(@class, "teaser")]', + '//section[contains(@class, "body")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/vgcats.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/vgcats.com.php new file mode 100644 index 0000000000..b2830a375a --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/vgcats.com.php @@ -0,0 +1,15 @@ + array( + '%/comics.*%' => array( + 'test_url' => 'http://www.vgcats.com/comics/?strip_id=358', + 'body' => array('//*[@align="center"]/img'), + 'strip' => array(), + ), + '%/super.*%' => array( + 'test_url' => 'http://www.vgcats.com/super/?strip_id=84', + 'body' => array('//*[@align="center"]/p/img'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/vuxml.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/vuxml.org.php new file mode 100644 index 0000000000..b9bef7a41a --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/vuxml.org.php @@ -0,0 +1,17 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.vuxml.org/freebsd/a5f160fa-deee-11e4-99f8-080027ef73ec.html', + 'body' => array( + '//body', + ), + 'strip' => array( + '//h1', + '//div[@class="blurb"]', + '//hr', + '//p[@class="copyright"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/wausaudailyherald.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/wausaudailyherald.com.php new file mode 100644 index 0000000000..58aceeaf40 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/wausaudailyherald.com.php @@ -0,0 +1,27 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.wausaudailyherald.com/story/news/2017/04/01/hundreds-gather-remember-attorney-killed-shooting-spree/99826062/?from=global&sessionKey=&autologin=', + 'body' => array( + '//div[@itemprop="articleBody"]', + ), + 'strip' => array( + '//h1', + '//iframe', + '//span[@class="mycapture-small-btn mycapture-btn-with-text mycapture-expandable-photo-btn-small js-mycapture-btn-small"]', + '//div[@class="close-wrap"]', + '//div[contains(@class,"ui-video-wrapper")]', + '//div[contains(@class,"media-mob")]', + '//div[contains(@class,"left-mob")]', + '//div[contains(@class,"nerdbox")]', + '//p/span', + '//div[contains(@class,"oembed-asset")]', + '//*[contains(@class,"share")]', + '//div[contains(@class,"gallery-asset")]', + '//div[contains(@class,"oembed-asset")]', + '//div[@class="article-print-url"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.bbc.co.uk.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.bbc.co.uk.php new file mode 100644 index 0000000000..98fc368a03 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.bbc.co.uk.php @@ -0,0 +1,33 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.bbc.co.uk/news/world-middle-east-23911833', + 'body' => array( + '//div[@class="story-body__inner"] | //div[@class="article"]', + '//div[@class="indPost"]', + ), + 'strip' => array( + '//form', + '//div[@id="headline"]', + '//*[@class="warning"]', + '//span[@class="off-screen"]', + '//span[@class="story-image-copyright"]', + '//ul[@class="story-body__unordered-list"]', + '//div[@class="ad_wrapper"]', + '//div[@id="article-sidebar"]', + '//div[@class="data-table-outer"]', + '//*[@class="story-date"]', + '//*[@class="story-header"]', + '//figure[contains(@class,"has-caption")]', + '//*[@class="story-related"]', + '//*[contains(@class, "byline")]', + '//p[contains(@class, "media-message")]', + '//*[contains(@class, "story-feature")]', + '//*[@id="video-carousel-container"]', + '//*[@id="also-related-links"]', + '//*[contains(@class, "share") or contains(@class, "hidden") or contains(@class, "hyper")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.bdgest.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.bdgest.com.php new file mode 100644 index 0000000000..41ef68d451 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.bdgest.com.php @@ -0,0 +1,15 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.bdgest.com/chronique-6027-BD-Adrastee-Tome-2.html', + 'body' => array( + '//*[contains(@class, "chronique")]', + ), + 'strip' => array( + '//*[contains(@class, "post-review")]', + '//*[contains(@class, "footer-review")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.bgr.in.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.bgr.in.php new file mode 100644 index 0000000000..63ca069ee6 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.bgr.in.php @@ -0,0 +1,23 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.bgr.in/news/xiaomi-redmi-3-with-13-megapixel-camera-snapdragon-616-launched-price-specifications-and-features/', + 'body' => array( + '//div[@class="article-content"]', + ), + 'strip' => array( + '//*[@class="article-meta"]', + '//*[@class="contentAdsense300"]', + '//*[@class="iwpl-social-hide"]', + '//iframe[@class="iframeads"]', + '//*[@class="disqus_thread"]', + '//*[@class="outb-mobile OUTBRAIN"]', + '//*[@class="wdt_smart_alerts"]', + '//*[@class="footnote"]', + '//*[@id="gadget-widget"]', + '//header[@class="article-title entry-header"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.businessweek.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.businessweek.com.php new file mode 100644 index 0000000000..0acc44ec5b --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.businessweek.com.php @@ -0,0 +1,15 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.businessweek.com/articles/2013-09-18/elon-musks-hyperloop-will-work-says-some-very-smart-software', + 'body' => array( + '//div[@id="lead_graphic"]', + '//div[@id="article_body"]', + ), + 'strip' => array( + '//*[contains(@class, "related_item")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.cnn.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.cnn.com.php new file mode 100644 index 0000000000..31d03ed925 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.cnn.com.php @@ -0,0 +1,24 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.cnn.com/2013/08/31/world/meast/syria-civil-war/index.html?hpt=hp_t1', + 'body' => array( + '//div[@class="cnn_strycntntlft"]', + ), + 'strip' => array( + '//div[@class="cnn_stryshrwdgtbtm"]', + '//div[@class="cnn_strybtmcntnt"]', + '//div[@class="cnn_strylftcntnt"]', + '//div[contains(@class, "cnnGalleryContainer")]', + '//div[contains(@class, "cnn_strylftcexpbx")]', + '//div[contains(@class, "articleGalleryNavContainer")]', + '//div[contains(@class, "cnnArticleGalleryCaptionControl")]', + '//div[contains(@class, "cnnArticleGalleryNavPrevNextDisabled")]', + '//div[contains(@class, "cnnArticleGalleryNavPrevNext")]', + '//div[contains(@class, "cnn_html_media_title_new")]', + '//div[contains(@id, "disqus")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.developpez.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.developpez.com.php new file mode 100644 index 0000000000..1535e437b6 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.developpez.com.php @@ -0,0 +1,21 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.developpez.com/actu/81757/Mozilla-annonce-la-disponibilite-de-Firefox-36-qui-passe-au-HTTP-2-et-permet-la-synchronisation-de-son-ecran-d-accueil/', + 'body' => array( + '//*[@itemprop="articleBody"]', + ), + 'strip' => array( + '//form', + '//div[@class="content"]/img', + '//a[last()]/following-sibling::*', + '//*[contains(@class,"actuTitle")]', + '//*[contains(@class,"date")]', + '//*[contains(@class,"inlineimg")]', + '//*[@id="signaler"]', + '//*[@id="signalerFrame"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.egscomics.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.egscomics.com.php new file mode 100644 index 0000000000..263f0755d3 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.egscomics.com.php @@ -0,0 +1,12 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.egscomics.com/index.php?id=1690', + 'title' => '/html/head/title', + 'body' => array( + '//img[@id="comic"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.fakingnews.firstpost.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.fakingnews.firstpost.com.php new file mode 100644 index 0000000000..c948c77bee --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.fakingnews.firstpost.com.php @@ -0,0 +1,17 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.fakingnews.firstpost.com/2016/01/engineering-student-creates-record-in-a-decade-becomes-the-first-to-completely-exhaust-ball-pen-refill/', + 'body' => array( + '//div[@class="entry"]', + ), + 'strip' => array( + '//*[@class="socialshare_bar"]', + '//*[@class="authorbox"]', + '//*[@class="cf5_rps"]', + '//*[@class="60563 fb-comments fb-social-plugin"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.forbes.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.forbes.com.php new file mode 100644 index 0000000000..fd16ed5749 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.forbes.com.php @@ -0,0 +1,20 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.forbes.com/sites/andygreenberg/2013/09/05/follow-the-bitcoins-how-we-got-busted-buying-drugs-on-silk-roads-black-market/', + 'body' => array( + '//div[@id="leftRail"]/div[contains(@class, body)]', + ), + 'strip' => array( + '//aside', + '//div[contains(@class, "entity_block")]', + '//div[contains(@class, "vestpocket") and not contains(@class, "body")]', + '//div[contains(@style, "display")]', + '//div[contains(@id, "comment")]', + '//div[contains(@class, "widget")]', + '//div[contains(@class, "pagination")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.franceculture.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.franceculture.fr.php new file mode 100644 index 0000000000..f7ec0d8db5 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.franceculture.fr.php @@ -0,0 +1,14 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.franceculture.fr/emission-culture-eco-la-finance-aime-toujours-la-france-2016-01-08', + 'body' => array( + '//div[@class="text-zone"]', + ), + 'strip' => array( + '//ul[@class="tags"]', + ), + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.futura-sciences.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.futura-sciences.com.php new file mode 100644 index 0000000000..ea94a0fb4b --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.futura-sciences.com.php @@ -0,0 +1,19 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.futura-sciences.com/magazines/espace/infos/actu/d/astronautique-curiosity-franchi-succes-dune-dingo-gap-52289/#xtor=RSS-8', + 'body' => array( + '//div[contains(@class, "content fiche-")]', + ), + 'strip' => array( + '//h1', + '//*[contains(@class, "content-date")]', + '//*[contains(@class, "diaporama")]', + '//*[contains(@class, "slider")]', + '//*[contains(@class, "cartouche")]', + '//*[contains(@class, "noprint")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.geekculture.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.geekculture.com.php new file mode 100644 index 0000000000..3d0b6c75ff --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.geekculture.com.php @@ -0,0 +1,13 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.geekculture.com/joyoftech/joyarchives/2180.html', + 'body' => array( + '//p[contains(@class,"Maintext")][2]/a/img[contains(@src,"joyimages")]', + ), + 'strip' => array(), + ), + ), +); + diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.howtogeek.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.howtogeek.com.php new file mode 100644 index 0000000000..6879e767b1 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.howtogeek.com.php @@ -0,0 +1,14 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.howtogeek.com/235283/what-is-a-wireless-hard-drive-and-should-i-get-one/', + 'body' => array( + '//div[@class="thecontent"]', + ), + 'strip' => array( + '//*[@class="relatedside"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.lepoint.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.lepoint.fr.php new file mode 100644 index 0000000000..dcb7e48401 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.lepoint.fr.php @@ -0,0 +1,18 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.lepoint.fr/c-est-arrive-aujourd-hui/19-septembre-1783-pour-la-premiere-fois-un-mouton-un-canard-et-un-coq-s-envoient-en-l-air-devant-louis-xvi-18-09-2012-1507704_494.php', + 'body' => array( + '//article', + ), + 'strip' => array( + '//*[contains(@class, "info_article")]', + '//*[contains(@class, "fildariane_titre")]', + '//*[contains(@class, "entete2_article")]', + '//*[contains(@class, "signature_article")]', + '//*[contains(@id, "share")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.lesnumeriques.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.lesnumeriques.com.php new file mode 100644 index 0000000000..0137e20970 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.lesnumeriques.com.php @@ -0,0 +1,25 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.lesnumeriques.com/blender/kitchenaid-diamond-5ksb1585-p27473/test.html', + 'body' => array( + '//*[@id="product-content"]', + '//*[@id="news-content"]', + '//*[@id="article-content"]', + ), + 'strip' => array( + '//form', + '//div[contains(@class, "price-v4"])', + '//div[contains(@class, "authors-and-date")]', + '//div[contains(@class, "mini-product")]', + '//div[@id="articles-related-authors"]', + '//div[@id="tags-socials"]', + '//div[@id="user-reviews"]', + '//div[@id="product-reviews"]', + '//div[@id="publication-breadcrumbs-and-date"]', + '//div[@id="publication-breadcrumbs-and-date"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.mac4ever.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.mac4ever.com.php new file mode 100644 index 0000000000..60bc1bd43c --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.mac4ever.com.php @@ -0,0 +1,13 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.mac4ever.com/actu/87392_video-quand-steve-jobs-et-bill-gates-jouaient-au-bachelor-avec-le-mac', + 'body' => array( + '//div[contains(@class, "news-news-content")]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.makeuseof.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.makeuseof.com.php new file mode 100644 index 0000000000..a274564a50 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.makeuseof.com.php @@ -0,0 +1,18 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.makeuseof.com/tag/having-problems-with-audio-in-windows-10-heres-a-likely-fix/', + 'body' => array( + '//div[@class="entry"]', + ), + 'strip' => array( + '//*[@class="new_sharebar"]', + '//*[@class="author"]', + '//*[@class="wdt_grouvi"]', + '//*[@class="wdt_smart_alerts"]', + '//*[@class="modal fade grouvi"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.monsieur-le-chien.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.monsieur-le-chien.fr.php new file mode 100644 index 0000000000..5f5e987ba4 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.monsieur-le-chien.fr.php @@ -0,0 +1,11 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.monsieur-le-chien.fr/index.php?planche=672', + 'body' => array( + '//img[starts-with(@src, "i/planches/")]', + ), + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.npr.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.npr.org.php new file mode 100644 index 0000000000..ecc0213a3b --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.npr.org.php @@ -0,0 +1,28 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.npr.org/blogs/thesalt/2013/09/17/223345977/auto-brewery-syndrome-apparently-you-can-make-beer-in-your-gut', + 'body' => array( + '//article[contains(@class,"story")]', + ), + 'strip' => array( + '//div[@class="story-tools"]', + '//h3[@class="slug"]', + '//div[@class="storytitle"]', + '//div[@id="story-meta"]', + '//a[@id="mainContent"]', + '//div[@class="credit-caption"]', + '//div[@class="enlarge_html"]', + '//button', + '//div[contains(@id,"pullquote")]', + '//div[contains(@class,"internallink")]', + '//div[contains(@class,"video")]', + '//div[@class="simplenodate"]', + '//div[contains(@class,"share-")]', + '//div[@class="tags"]', + '//aside' + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.numerama.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.numerama.com.php new file mode 100644 index 0000000000..fe4971c5db --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.numerama.com.php @@ -0,0 +1,15 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.numerama.com/sciences/125959-recherches-ladn-recompensees-nobel-de-chimie.html', + 'body' => array( + '//article', + ), + 'strip' => array( + '//footer', + '//section[@class="related-article"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.oneindia.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.oneindia.com.php new file mode 100644 index 0000000000..320c2147d0 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.oneindia.com.php @@ -0,0 +1,14 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.oneindia.com/india/b-luru-govt-likely-remove-word-eunuch-from-sec-36-a-karnataka-police-act-1981173.html', + 'body' => array( + '//div[@class="ecom-ad-content"]', + ), + 'strip' => array( + '//*[@id="view_cmtns"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.pseudo-sciences.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.pseudo-sciences.org.php new file mode 100644 index 0000000000..9e467edf53 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.pseudo-sciences.org.php @@ -0,0 +1,16 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.pseudo-sciences.org/spip.php?article2275', + 'body' => array( + '//div[@id="art_main"]', + ), + 'strip' => array( + '//div[@id="art_print"]', + '//div[@id="art_chapo"]', + '//img[@class="puce"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.sciencemag.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.sciencemag.org.php new file mode 100644 index 0000000000..ae7a93ac29 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.sciencemag.org.php @@ -0,0 +1,16 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.sciencemag.org/news/2016/01/could-bright-foamy-wak$', + 'body' => array( + '//div[@class="row--hero"]', + '//article[contains(@class,"primary")]', + ), + 'strip' => array( + '//header[@class="article__header"]', + '//footer[@class="article__foot"]', + ), + ), + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.slate.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.slate.fr.php new file mode 100644 index 0000000000..8c8dc893e7 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.slate.fr.php @@ -0,0 +1,19 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.slate.fr/monde/77034/allemagne-2013-couacs-campagne', + 'body' => array( + '//div[@class="article_content"]', + ), + 'strip' => array( + '//*[@id="slate_associated_bn"]', + '//*[@id="ligatus-article"]', + '//*[@id="article_sidebar"]', + '//div[contains(@id, "reseaux")]', + '//*[contains(@class, "smart") or contains(@class, "article_tags") or contains(@class, "article_reactions")]', + '//*[contains(@class, "OUTBRAIN") or contains(@class, "related_item") or contains(@class, "share")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.universfreebox.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.universfreebox.com.php new file mode 100644 index 0000000000..0747d0fbdd --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.universfreebox.com.php @@ -0,0 +1,15 @@ + array( + '%.*%' => array( + 'test_url' => 'http://www.universfreebox.com/article/24305/4G-Bouygues-Telecom-lance-une-vente-flash-sur-son-forfait-Sensation-3Go', + 'body' => array( + '//div[@id="corps_corps"]', + ), + 'strip' => array( + '//*[@id="formulaire"]', + '//*[@id="commentaire"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.zeit.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.zeit.de.php new file mode 100644 index 0000000000..316c265625 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.zeit.de.php @@ -0,0 +1,41 @@ + array( + '%^/zeit-magazin.*%' => array( + 'test_url' => 'http://www.zeit.de/zeit-magazin/2015/15/pegida-kathrin-oertel-lutz-bachmann', + 'body' => array( + '//article[@class="article"]', + ), + 'strip' => array( + '//header/div/h1', + '//header/div/div[@class="article__head__subtitle"]', + '//header/div/div[@class="article__column__author"]', + '//header/div/div[@class="article__column__author"]', + '//header/div/span[@class="article__head__meta-wrap"]', + '//form', + '//style', + '//div[contains(@class, "ad-tile")]', + '//div[@class="iqd-mobile-adplace"]', + '//div[@id="iq-artikelanker"]', + '//div[@id="js-social-services"]', + '//section[@id="js-comments"]', + '//aside', + ), + ), + '%.*%' => array( + 'test_url' => 'http://www.zeit.de/politik/ausland/2015-04/thessaloniki-krise-griechenland-yannis-boutaris/', + 'body' => array( + '//div[@class="article-body"]', + ), + 'strip' => array( + '//*[@class="articleheader"]', + '//*[@class="excerpt"]', + '//div[contains(@class, "ad")]', + '//div[@itemprop="video"]', + '//*[@class="articlemeta"]', + '//*[@class="articlemeta-clear"]', + '//*[@class="zol_inarticletools"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/xkcd.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/xkcd.com.php new file mode 100644 index 0000000000..8495726879 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/xkcd.com.php @@ -0,0 +1,8 @@ + array( + '%.*%' => array( + '%alt="(.+)" */>%' => '/>
$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ymatuhin.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ymatuhin.ru.php new file mode 100644 index 0000000000..9fd83f18c7 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ymatuhin.ru.php @@ -0,0 +1,21 @@ + array( + '%.*%' => array( + 'test_url' => 'https://ymatuhin.ru/tools/git-default-editor/', + 'body' => array( + '//section', + ), + 'strip' => array( + "//script", + "//style", + "//h1", + "//time", + "//aside", + "/html/body/section/ul", + "//amp-iframe", + "/html/body/section/h4" + ), + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/zdnet.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/zdnet.com.php new file mode 100644 index 0000000000..79b35ddba4 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/zdnet.com.php @@ -0,0 +1,23 @@ + array( + '%.*%' => array( + 'test_url' => 'http://zdnet.com.feedsportal.com/c/35462/f/675637/s/4a33c93e/sc/11/l/0L0Szdnet0N0Carticle0Cchina0Eus0Eagree0Eon0Ecybercrime0Ecooperation0Eamid0Econtinued0Etension0C0Tftag0FRSSbaffb68/story01.htm', + 'body' => array( + '//p[@class="summary"]', + '//div[contains(@class,"storyBody")]', + ), + 'strip' => array( + '//*[contains(@class,"ad-")]', + '//p/span', + '//script', + '//p[@class="summary"]', + '//div[contains(@class,"relatedContent")]', + '//div[contains(@class,"loader")]', + '//p[@class="photoDetails"]', + '//div[@class="thumbnailSlider"]', + '//div[@class="shortcodeGalleryWrapper"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/CandidateParser.php b/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/CandidateParser.php new file mode 100644 index 0000000000..6c53a289ab --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/CandidateParser.php @@ -0,0 +1,283 @@ +dom = XmlParser::getHtmlDocument(''.$html); + $this->xpath = new DOMXPath($this->dom); + } + + /** + * Get the relevant content with the list of potential attributes. + * + * @return string + */ + public function execute() + { + $content = $this->findContentWithCandidates(); + + if (strlen($content) < 200) { + $content = $this->findContentWithArticle(); + } + + if (strlen($content) < 50) { + $content = $this->findContentWithBody(); + } + + return $this->stripGarbage($content); + } + + /** + * Find content based on the list of tag candidates. + * + * @return string + */ + public function findContentWithCandidates() + { + foreach ($this->candidatesAttributes as $candidate) { + Logger::setMessage(get_called_class().': Try this candidate: "'.$candidate.'"'); + + $nodes = $this->xpath->query('//*[(contains(@class, "'.$candidate.'") or @id="'.$candidate.'") and not (contains(@class, "nav") or contains(@class, "page"))]'); + + if ($nodes !== false && $nodes->length > 0) { + Logger::setMessage(get_called_class().': Find candidate "'.$candidate.'"'); + + return $this->dom->saveXML($nodes->item(0)); + } + } + + return ''; + } + + /** + * Find
tag. + * + * @return string + */ + public function findContentWithArticle() + { + $nodes = $this->xpath->query('//article'); + + if ($nodes !== false && $nodes->length > 0) { + Logger::setMessage(get_called_class().': Find
tag'); + + return $this->dom->saveXML($nodes->item(0)); + } + + return ''; + } + + /** + * Find tag. + * + * @return string + */ + public function findContentWithBody() + { + $nodes = $this->xpath->query('//body'); + + if ($nodes !== false && $nodes->length > 0) { + Logger::setMessage(get_called_class().' Find '); + + return $this->dom->saveXML($nodes->item(0)); + } + + return ''; + } + + /** + * Strip useless tags. + * + * @param string $content + * + * @return string + */ + public function stripGarbage($content) + { + $dom = XmlParser::getDomDocument($content); + + if ($dom !== false) { + $xpath = new DOMXPath($dom); + + $this->stripTags($xpath); + $this->stripAttributes($dom, $xpath); + + $content = $dom->saveXML($dom->documentElement); + } + + return $content; + } + + /** + * Remove blacklisted tags. + * + * @param DOMXPath $xpath + */ + public function stripTags(DOMXPath $xpath) + { + foreach ($this->stripTags as $tag) { + $nodes = $xpath->query('//'.$tag); + + if ($nodes !== false && $nodes->length > 0) { + Logger::setMessage(get_called_class().': Strip tag: "'.$tag.'"'); + + foreach ($nodes as $node) { + $node->parentNode->removeChild($node); + } + } + } + } + + /** + * Remove blacklisted attributes. + * + * @param DomDocument $dom + * @param DOMXPath $xpath + */ + public function stripAttributes(DomDocument $dom, DOMXPath $xpath) + { + foreach ($this->stripAttributes as $attribute) { + $nodes = $xpath->query('//*[contains(@class, "'.$attribute.'") or contains(@id, "'.$attribute.'")]'); + + if ($nodes !== false && $nodes->length > 0) { + Logger::setMessage(get_called_class().': Strip attribute: "'.$attribute.'"'); + + foreach ($nodes as $node) { + if ($this->shouldRemove($dom, $node)) { + $node->parentNode->removeChild($node); + } + } + } + } + } + + /** + * Find link for next page of the article. + * + * @return string + */ + public function findNextLink() + { + return null; + } + + /** + * Return false if the node should not be removed. + * + * @param DomDocument $dom + * @param DomNode $node + * + * @return bool + */ + public function shouldRemove(DomDocument $dom, $node) + { + $document_length = strlen($dom->textContent); + $node_length = strlen($node->textContent); + + if ($document_length === 0) { + return true; + } + + $ratio = $node_length * 100 / $document_length; + + if ($ratio >= 90) { + Logger::setMessage(get_called_class().': Should not remove this node ('.$node->nodeName.') ratio: '.$ratio.'%'); + + return false; + } + + return true; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/ParserInterface.php b/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/ParserInterface.php new file mode 100644 index 0000000000..3ded4b1c47 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/ParserInterface.php @@ -0,0 +1,20 @@ +getRulesFileList($hostname); + + foreach ($this->getRulesFolders() as $folder) { + $rule = $this->loadRuleFile($folder, $files); + + if (!empty($rule)) { + return $rule; + } + } + } + + return array(); + } + + /** + * Get the list of possible rules file names for a given hostname. + * + * @param string $hostname Hostname + * + * @return array + */ + public function getRulesFileList($hostname) + { + $files = array($hostname); // subdomain.domain.tld + $parts = explode('.', $hostname); + $len = count($parts); + + if ($len > 2) { + $subdomain = array_shift($parts); + $files[] = implode('.', $parts); // domain.tld + $files[] = '.'.implode('.', $parts); // .domain.tld + $files[] = $subdomain; // subdomain + } elseif ($len === 2) { + $files[] = '.'.implode('.', $parts); // .domain.tld + $files[] = $parts[0]; // domain + } + + return $files; + } + + /** + * Load a rule file from the defined folder. + * + * @param string $folder Rule directory + * @param array $files List of possible file names + * + * @return array + */ + public function loadRuleFile($folder, array $files) + { + foreach ($files as $file) { + $filename = $folder.'/'.$file.'.php'; + if (file_exists($filename)) { + Logger::setMessage(get_called_class().' Load rule: '.$file); + + return include $filename; + } + } + + return array(); + } + + /** + * Get the list of folders that contains rules. + * + * @return array + */ + public function getRulesFolders() + { + $folders = array(); + + if ($this->config !== null && $this->config->getGrabberRulesFolder() !== null) { + $folders[] = $this->config->getGrabberRulesFolder(); + } + + $folders[] = __DIR__ . '/../Rules'; + + return $folders; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/RuleParser.php b/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/RuleParser.php new file mode 100644 index 0000000000..9beb59c16f --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/RuleParser.php @@ -0,0 +1,102 @@ +rules = $rules; + $this->dom = XmlParser::getHtmlDocument(''.$html); + $this->xpath = new DOMXPath($this->dom); + } + + /** + * Get the relevant content with predefined rules. + * + * @return string + */ + public function execute() + { + $this->stripTags(); + + return $this->findContent(); + } + + /** + * Remove HTML tags. + */ + public function stripTags() + { + if (isset($this->rules['strip']) && is_array($this->rules['strip'])) { + foreach ($this->rules['strip'] as $pattern) { + $nodes = $this->xpath->query($pattern); + + if ($nodes !== false && $nodes->length > 0) { + foreach ($nodes as $node) { + $node->parentNode->removeChild($node); + } + } + } + } + } + + /** + * Fetch content based on Xpath rules. + */ + public function findContent() + { + $content = ''; + if (isset($this->rules['body']) && is_array($this->rules['body'])) { + foreach ($this->rules['body'] as $pattern) { + $nodes = $this->xpath->query($pattern); + + if ($nodes !== false && $nodes->length > 0) { + foreach ($nodes as $node) { + $content .= $this->dom->saveXML($node); + } + } + } + } + + return $content; + } + + /** + * Fetch next link based on Xpath rules. + * + * @return string + */ + public function findNextLink() + { + if (isset($this->rules['next_page']) && is_array($this->rules['next_page'])) { + foreach ($this->rules['next_page'] as $pattern) { + $nodes = $this->xpath->query($pattern); + if ($nodes !== false && $nodes->length > 0) { + foreach ($nodes as $node) { + return $node->getAttribute('href'); + } + } + } + } + return null; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/Scraper.php b/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/Scraper.php new file mode 100644 index 0000000000..e5b9817fea --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/Scraper.php @@ -0,0 +1,279 @@ +enableCandidateParser = false; + return $this; + } + + /** + * Get encoding. + * + * @return string + */ + public function getEncoding() + { + return $this->encoding; + } + + /** + * Set encoding. + * + * @param string $encoding + * + * @return Scraper + */ + public function setEncoding($encoding) + { + $this->encoding = $encoding; + + return $this; + } + + /** + * Get URL to download. + * + * @return string + */ + public function getUrl() + { + return $this->url; + } + + /** + * Set URL to download. + * + * @param string $url URL + * + * @return Scraper + */ + public function setUrl($url) + { + $this->url = $url; + + return $this; + } + + /** + * Return true if the scraper found relevant content. + * + * @return bool + */ + public function hasRelevantContent() + { + return !empty($this->content); + } + + /** + * Get relevant content. + * + * @return string + */ + public function getRelevantContent() + { + return $this->content; + } + + /** + * Get raw content (unfiltered). + * + * @return string + */ + public function getRawContent() + { + return $this->html; + } + + /** + * Set raw content (unfiltered). + * + * @param string $html + * + * @return Scraper + */ + public function setRawContent($html) + { + $this->html = $html; + + return $this; + } + + /** + * Get filtered relevant content. + * + * @return string + */ + public function getFilteredContent() + { + $filter = Filter::html($this->content, $this->url); + $filter->setConfig($this->config); + + return $filter->execute(); + } + + /** + * Download the HTML content. + * + * @return bool + */ + public function download() + { + if (!empty($this->url)) { + + // Clear everything + $this->html = ''; + $this->content = ''; + $this->encoding = ''; + + try { + $client = Client::getInstance(); + $client->setConfig($this->config); + $client->setTimeout($this->config->getGrabberTimeout()); + $client->setUserAgent($this->config->getGrabberUserAgent()); + $client->execute($this->url); + + $this->url = $client->getUrl(); + $this->html = $client->getContent(); + $this->encoding = $client->getEncoding(); + + return true; + } catch (ClientException $e) { + Logger::setMessage(get_called_class().': '.$e->getMessage()); + } + } + + return false; + } + + /** + * Execute the scraper. + */ + public function execute($pageContent = '', $recursionDepth = 0) + { + $this->html = ''; + $this->encoding = ''; + $this->content = ''; + $this->download(); + $this->prepareHtml(); + + $parser = $this->getParser(); + + if ($parser !== null) { + $maxRecursions = $this->config->getMaxRecursions(); + if(!isset($maxRecursions)){ + $maxRecursions = 25; + } + $pageContent .= $parser->execute(); + // check if there is a link to next page and recursively get content (max 25 pages) + if((($nextLink = $parser->findNextLink()) !== null) && $recursionDepth < $maxRecursions){ + $nextLink = Url::resolve($nextLink,$this->url); + $this->setUrl($nextLink); + $this->execute($pageContent,$recursionDepth+1); + } + else{ + $this->content = $pageContent; + } + Logger::setMessage(get_called_class().': Content length: '.strlen($this->content).' bytes'); + } + } + + /** + * Get the parser. + * + * @return ParserInterface + */ + public function getParser() + { + $ruleLoader = new RuleLoader($this->config); + $rules = $ruleLoader->getRules($this->url); + + if (!empty($rules['grabber'])) { + Logger::setMessage(get_called_class().': Parse content with rules'); + + foreach ($rules['grabber'] as $pattern => $rule) { + $url = new Url($this->url); + $sub_url = $url->getFullPath(); + + if (preg_match($pattern, $sub_url)) { + Logger::setMessage(get_called_class().': Matched url '.$sub_url); + return new RuleParser($this->html, $rule); + } + } + } elseif ($this->enableCandidateParser) { + Logger::setMessage(get_called_class().': Parse content with candidates'); + } + + return new CandidateParser($this->html); + } + + /** + * Normalize encoding and strip head tag. + */ + public function prepareHtml() + { + $html_encoding = XmlParser::getEncodingFromMetaTag($this->html); + + $this->html = Encoding::convert($this->html, $html_encoding ?: $this->encoding); + $this->html = Filter::stripHeadTags($this->html); + + Logger::setMessage(get_called_class().': HTTP Encoding "'.$this->encoding.'" ; HTML Encoding "'.$html_encoding.'"'); + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/Subscription.php b/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/Subscription.php new file mode 100644 index 0000000000..12eccfd51c --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/Subscription.php @@ -0,0 +1,175 @@ +title = $title; + return $this; + } + + /** + * Get title + * + * @access public + * @return string + */ + public function getTitle() + { + return $this->title; + } + + /** + * Set feed URL + * + * @access public + * @param string $feedUrl + * @return Subscription + */ + public function setFeedUrl($feedUrl) + { + $this->feedUrl = $feedUrl; + return $this; + } + + /** + * Get feed URL + * + * @access public + * @return string + */ + public function getFeedUrl() + { + return $this->feedUrl; + } + + /** + * Set site URL + * + * @access public + * @param string $siteUrl + * @return Subscription + */ + public function setSiteUrl($siteUrl) + { + $this->siteUrl = $siteUrl; + return $this; + } + + /** + * Get site URL + * + * @access public + * @return string + */ + public function getSiteUrl() + { + return $this->siteUrl; + } + + /** + * Set category + * + * @access public + * @param string $category + * @return Subscription + */ + public function setCategory($category) + { + $this->category = $category; + return $this; + } + + /** + * Get category + * + * @access public + * @return string + */ + public function getCategory() + { + return $this->category; + } + + /** + * Set description + * + * @access public + * @param string $description + * @return Subscription + */ + public function setDescription($description) + { + $this->description = $description; + return $this; + } + + /** + * Get description + * + * @access public + * @return string + */ + public function getDescription() + { + return $this->description; + } + + /** + * Set type + * + * @access public + * @param string $type + * @return Subscription + */ + public function setType($type) + { + $this->type = $type; + return $this; + } + + /** + * Get type + * + * @access public + * @return string + */ + public function getType() + { + return $this->type; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionList.php b/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionList.php new file mode 100644 index 0000000000..b173f89bbb --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionList.php @@ -0,0 +1,75 @@ +title = $title; + return $this; + } + + /** + * Get title + * + * @access public + * @return string + */ + public function getTitle() + { + return $this->title; + } + + /** + * Add subscription + * + * @access public + * @param Subscription $subscription + * @return SubscriptionList + */ + public function addSubscription(Subscription $subscription) + { + $this->subscriptions[] = $subscription; + return $this; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionListBuilder.php b/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionListBuilder.php new file mode 100644 index 0000000000..838e4cb58d --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionListBuilder.php @@ -0,0 +1,204 @@ +subscriptionList = $subscriptionList; + } + + /** + * Get object instance + * + * @static + * @access public + * @param SubscriptionList $subscriptionList + * @return SubscriptionListBuilder + */ + public static function create(SubscriptionList $subscriptionList) + { + return new static($subscriptionList); + } + + /** + * Build OPML feed + * + * @access public + * @param string $filename + * @return string + */ + public function build($filename = '') + { + $this->document = new DomDocument('1.0', 'UTF-8'); + $this->document->formatOutput = true; + + $opmlElement = $this->document->createElement('opml'); + $opmlElement->setAttribute('version', '1.0'); + + $headElement = $this->document->createElement('head'); + + if ($this->subscriptionList->getTitle() !== '') { + $titleElement = $this->document->createElement('title'); + $titleElement->appendChild($this->document->createTextNode($this->subscriptionList->getTitle())); + $headElement->appendChild($titleElement); + } + + $opmlElement->appendChild($headElement); + $opmlElement->appendChild($this->buildBody()); + $this->document->appendChild($opmlElement); + + if ($filename !== '') { + $this->document->save($filename); + return ''; + } + + return $this->document->saveXML(); + } + + /** + * Return true if the list has categories + * + * @access public + * @return bool + */ + public function hasCategories() + { + foreach ($this->subscriptionList->subscriptions as $subscription) { + if ($subscription->getCategory() !== '') { + return true; + } + } + + return false; + } + + /** + * Build OPML body + * + * @access protected + * @return DOMElement + */ + protected function buildBody() + { + $bodyElement = $this->document->createElement('body'); + + if ($this->hasCategories()) { + $this->buildCategories($bodyElement); + return $bodyElement; + } + + foreach ($this->subscriptionList->subscriptions as $subscription) { + $bodyElement->appendChild($this->buildSubscription($subscription)); + } + + return $bodyElement; + } + + /** + * Build categories section + * + * @access protected + * @param DOMElement $bodyElement + */ + protected function buildCategories(DOMElement $bodyElement) + { + $categories = $this->groupByCategories(); + + foreach ($categories as $category => $subscriptions) { + $bodyElement->appendChild($this->buildCategory($category, $subscriptions)); + } + } + + /** + * Build category tag + * + * @access protected + * @param string $category + * @param array $subscriptions + * @return DOMElement + */ + protected function buildCategory($category, array $subscriptions) + { + $outlineElement = $this->document->createElement('outline'); + $outlineElement->setAttribute('text', $category); + + foreach ($subscriptions as $subscription) { + $outlineElement->appendChild($this->buildSubscription($subscription)); + } + + return $outlineElement; + } + + /** + * Build subscription entry + * + * @access public + * @param Subscription $subscription + * @return DOMElement + */ + protected function buildSubscription(Subscription $subscription) + { + $outlineElement = $this->document->createElement('outline'); + $outlineElement->setAttribute('type', $subscription->getType() ?: 'rss'); + $outlineElement->setAttribute('text', $subscription->getTitle() ?: $subscription->getFeedUrl()); + $outlineElement->setAttribute('xmlUrl', $subscription->getFeedUrl()); + + if ($subscription->getTitle() !== '') { + $outlineElement->setAttribute('title', $subscription->getTitle()); + } + + if ($subscription->getDescription() !== '') { + $outlineElement->setAttribute('description', $subscription->getDescription()); + } + + if ($subscription->getSiteUrl() !== '') { + $outlineElement->setAttribute('htmlUrl', $subscription->getSiteUrl()); + } + + return $outlineElement; + } + + /** + * Group subscriptions by category + * + * @access private + * @return array + */ + private function groupByCategories() + { + $categories = array(); + + foreach ($this->subscriptionList->subscriptions as $subscription) { + $categories[$subscription->getCategory()][] = $subscription; + } + + return $categories; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionListParser.php b/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionListParser.php new file mode 100644 index 0000000000..9085588c24 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionListParser.php @@ -0,0 +1,100 @@ +subscriptionList = new SubscriptionList(); + $this->data = trim($data); + } + + /** + * Get object instance + * + * @static + * @access public + * @param string $data + * @return SubscriptionListParser + */ + public static function create($data) + { + return new static($data); + } + + /** + * Parse a subscription list entry + * + * @access public + * @throws MalformedXmlException + * @return SubscriptionList + */ + public function parse() + { + $xml = XmlParser::getSimpleXml($this->data); + + if (! $xml || !isset($xml->head) || !isset($xml->body)) { + throw new MalformedXmlException('Unable to parse OPML file: invalid XML'); + } + + $this->parseTitle($xml->head); + $this->parseEntries($xml->body); + + return $this->subscriptionList; + } + + /** + * Parse title + * + * @access protected + * @param SimpleXMLElement $xml + */ + protected function parseTitle(SimpleXMLElement $xml) + { + $this->subscriptionList->setTitle((string) $xml->title); + } + + /** + * Parse entries + * + * @access protected + * @param SimpleXMLElement $body + */ + private function parseEntries(SimpleXMLElement $body) + { + foreach ($body->outline as $outlineElement) { + if (isset($outlineElement->outline)) { + $this->parseEntries($outlineElement); + } else { + $this->subscriptionList->subscriptions[] = SubscriptionParser::create($body, $outlineElement)->parse(); + } + } + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionParser.php b/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionParser.php new file mode 100644 index 0000000000..caff07c2d2 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionParser.php @@ -0,0 +1,142 @@ +parentElement = $parentElement; + $this->outlineElement = $outlineElement; + $this->subscription = new Subscription(); + } + + /** + * Get object instance + * + * @static + * @access public + * @param SimpleXMLElement $parentElement + * @param SimpleXMLElement $outlineElement + * @return SubscriptionParser + */ + public static function create(SimpleXMLElement $parentElement, SimpleXMLElement $outlineElement) + { + return new static($parentElement, $outlineElement); + } + + /** + * Parse subscription entry + * + * @access public + * @return Subscription + */ + public function parse() + { + $this->subscription->setCategory($this->findCategory()); + $this->subscription->setTitle($this->findTitle()); + $this->subscription->setFeedUrl($this->findFeedUrl()); + $this->subscription->setSiteUrl($this->findSiteUrl()); + $this->subscription->setType($this->findType()); + $this->subscription->setDescription($this->findDescription()); + + return $this->subscription; + } + + /** + * Find category. + * + * @access protected + * @return string + */ + protected function findCategory() + { + return isset($this->parentElement['text']) ? (string) $this->parentElement['text'] : ''; + } + + /** + * Find title. + * + * @access protected + * @return string + */ + protected function findTitle() + { + return isset($this->outlineElement['title']) ? (string) $this->outlineElement['title'] : (string) $this->outlineElement['text']; + } + + /** + * Find feed url. + * + * @access protected + * @return string + */ + protected function findFeedUrl() + { + return (string) $this->outlineElement['xmlUrl']; + } + + /** + * Find site url. + * + * @access protected + * @return string + */ + protected function findSiteUrl() + { + return isset($this->outlineElement['htmlUrl']) ? (string) $this->outlineElement['htmlUrl'] : $this->findFeedUrl(); + } + + /** + * Find type. + * + * @access protected + * @return string + */ + protected function findType() + { + return isset($this->outlineElement['version']) ? (string) $this->outlineElement['version'] : + isset($this->outlineElement['type']) ? (string) $this->outlineElement['type'] : 'rss'; + } + + /** + * Find description. + * + * @access protected + * @return string + */ + protected function findDescription() + { + return isset($this->outlineElement['description']) ? (string) $this->outlineElement['description'] : $this->findTitle(); + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/AtomFeedBuilder.php b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/AtomFeedBuilder.php new file mode 100644 index 0000000000..34f37800d0 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/AtomFeedBuilder.php @@ -0,0 +1,65 @@ +helper = new AtomHelper($this->getDocument()); + + $this->feedElement = $this->getDocument()->createElement('feed'); + $this->feedElement->setAttributeNodeNS(new DomAttr('xmlns', 'http://www.w3.org/2005/Atom')); + + $generator = $this->getDocument()->createElement('generator', 'PicoFeed'); + $generator->setAttribute('uri', 'https://github.com/miniflux/picoFeed'); + $this->feedElement->appendChild($generator); + + $this->helper + ->buildTitle($this->feedElement, $this->feedTitle) + ->buildId($this->feedElement, $this->feedUrl) + ->buildDate($this->feedElement, $this->feedDate) + ->buildLink($this->feedElement, $this->siteUrl) + ->buildLink($this->feedElement, $this->feedUrl, 'self', 'application/atom+xml') + ->buildAuthor($this->feedElement, $this->authorName, $this->authorEmail, $this->authorUrl) + ; + + foreach ($this->items as $item) { + $this->feedElement->appendChild($item->build()); + } + + $this->getDocument()->appendChild($this->feedElement); + + if ($filename !== '') { + $this->getDocument()->save($filename); + } + + return $this->getDocument()->saveXML(); + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/AtomHelper.php b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/AtomHelper.php new file mode 100644 index 0000000000..def6b0b9a8 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/AtomHelper.php @@ -0,0 +1,139 @@ +document = $document; + } + + /** + * Build node + * + * @access public + * @param DOMElement $element + * @param string $tag + * @param string $value + * @return AtomHelper + */ + public function buildNode(DOMElement $element, $tag, $value) + { + $node = $this->document->createElement($tag); + $node->appendChild($this->document->createTextNode($value)); + $element->appendChild($node); + return $this; + } + + /** + * Build title + * + * @access public + * @param DOMElement $element + * @param string $title + * @return AtomHelper + */ + public function buildTitle(DOMElement $element, $title) + { + return $this->buildNode($element, 'title', $title); + } + + /** + * Build id + * + * @access public + * @param DOMElement $element + * @param string $id + * @return AtomHelper + */ + public function buildId(DOMElement $element, $id) + { + return $this->buildNode($element, 'id', $id); + } + + /** + * Build date element + * + * @access public + * @param DOMElement $element + * @param DateTime $date + * @param string $type + * @return AtomHelper + */ + public function buildDate(DOMElement $element, DateTime $date, $type = 'updated') + { + return $this->buildNode($element, $type, $date->format(DateTime::ATOM)); + } + + /** + * Build link element + * + * @access public + * @param DOMElement $element + * @param string $url + * @param string $rel + * @param string $type + * @return AtomHelper + */ + public function buildLink(DOMElement $element, $url, $rel = 'alternate', $type = 'text/html') + { + $node = $this->document->createElement('link'); + $node->setAttribute('rel', $rel); + $node->setAttribute('type', $type); + $node->setAttribute('href', $url); + $element->appendChild($node); + + return $this; + } + + /** + * Build author element + * + * @access public + * @param DOMElement $element + * @param string $authorName + * @param string $authorEmail + * @param string $authorUrl + * @return AtomHelper + */ + public function buildAuthor(DOMElement $element, $authorName, $authorEmail, $authorUrl) + { + if (!empty($authorName)) { + $author = $this->document->createElement('author'); + $this->buildNode($author, 'name', $authorName); + + if (!empty($authorEmail)) { + $this->buildNode($author, 'email', $authorEmail); + } + + if (!empty($authorUrl)) { + $this->buildNode($author, 'uri', $authorUrl); + } + + $element->appendChild($author); + } + + return $this; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/AtomItemBuilder.php b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/AtomItemBuilder.php new file mode 100644 index 0000000000..dfdfe68d6b --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/AtomItemBuilder.php @@ -0,0 +1,63 @@ +itemElement = $this->feedBuilder->getDocument()->createElement('entry'); + $this->helper = new AtomHelper($this->feedBuilder->getDocument()); + + if (!empty($this->itemId)) { + $this->helper->buildId($this->itemElement, $this->itemId); + } else { + $this->helper->buildId($this->itemElement, $this->itemUrl); + } + + $this->helper + ->buildTitle($this->itemElement, $this->itemTitle) + ->buildLink($this->itemElement, $this->itemUrl) + ->buildDate($this->itemElement, $this->itemUpdatedDate, 'updated') + ->buildDate($this->itemElement, $this->itemPublishedDate, 'published') + ->buildAuthor($this->itemElement, $this->authorName, $this->authorEmail, $this->authorUrl) + ; + + if (!empty($this->itemSummary)) { + $this->helper->buildNode($this->itemElement, 'summary', $this->itemSummary); + } + + if (!empty($this->itemContent)) { + $node = $this->feedBuilder->getDocument()->createElement('content'); + $node->setAttribute('type', 'html'); + $node->appendChild($this->feedBuilder->getDocument()->createCDATASection($this->itemContent)); + $this->itemElement->appendChild($node); + } + + return $this->itemElement; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/FeedBuilder.php b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/FeedBuilder.php new file mode 100644 index 0000000000..cf9d024e15 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/FeedBuilder.php @@ -0,0 +1,185 @@ +document = new DomDocument('1.0', 'UTF-8'); + $this->document->formatOutput = true; + } + + /** + * Get new object instance + * + * @access public + * @return static + */ + public static function create() + { + return new static(); + } + + /** + * Add feed title + * + * @access public + * @param string $title + * @return $this + */ + public function withTitle($title) + { + $this->feedTitle = $title; + return $this; + } + + /** + * Add feed url + * + * @access public + * @param string $url + * @return $this + */ + public function withFeedUrl($url) + { + $this->feedUrl = $url; + return $this; + } + + /** + * Add website url + * + * @access public + * @param string $url + * @return $this + */ + public function withSiteUrl($url) + { + $this->siteUrl = $url; + return $this; + } + + /** + * Add feed date + * + * @access public + * @param DateTime $date + * @return $this + */ + public function withDate(DateTime $date) + { + $this->feedDate = $date; + return $this; + } + + /** + * Add feed author + * + * @access public + * @param string $name + * @param string $email + * @param string $url + * @return $this + */ + public function withAuthor($name, $email = '', $url ='') + { + $this->authorName = $name; + $this->authorEmail = $email; + $this->authorUrl = $url; + return $this; + } + + /** + * Add feed item + * + * @access public + * @param ItemBuilder $item + * @return $this + */ + public function withItem(ItemBuilder $item) + { + $this->items[] = $item; + return $this; + } + + /** + * Get DOM document + * + * @access public + * @return DOMDocument + */ + public function getDocument() + { + return $this->document; + } + + /** + * Build feed + * + * @abstract + * @access public + * @param string $filename + * @return string + */ + abstract public function build($filename = ''); +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/ItemBuilder.php b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/ItemBuilder.php new file mode 100644 index 0000000000..86985bc73b --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/ItemBuilder.php @@ -0,0 +1,209 @@ +feedBuilder = $feedBuilder; + } + + /** + * Get new object instance + * + * @access public + * @param FeedBuilder $feedBuilder + * @return static + */ + public static function create(FeedBuilder $feedBuilder) + { + return new static($feedBuilder); + } + + /** + * Add item title + * + * @access public + * @param string $title + * @return $this + */ + public function withTitle($title) + { + $this->itemTitle = $title; + return $this; + } + + /** + * Add item id + * + * @access public + * @param string $id + * @return $this + */ + public function withId($id) + { + $this->itemId = $id; + return $this; + } + + /** + * Add item url + * + * @access public + * @param string $url + * @return $this + */ + public function withUrl($url) + { + $this->itemUrl = $url; + return $this; + } + + /** + * Add item summary + * + * @access public + * @param string $summary + * @return $this + */ + public function withSummary($summary) + { + $this->itemSummary = $summary; + return $this; + } + + /** + * Add item content + * + * @access public + * @param string $content + * @return $this + */ + public function withContent($content) + { + $this->itemContent = $content; + return $this; + } + + /** + * Add item updated date + * + * @access public + * @param DateTime $date + * @return $this + */ + public function withUpdatedDate(DateTime $date) + { + $this->itemUpdatedDate = $date; + return $this; + } + + /** + * Add item published date + * + * @access public + * @param DateTime $date + * @return $this + */ + public function withPublishedDate(DateTime $date) + { + $this->itemPublishedDate = $date; + return $this; + } + + /** + * Add item author + * + * @access public + * @param string $name + * @param string $email + * @param string $url + * @return $this + */ + public function withAuthor($name, $email = '', $url ='') + { + $this->authorName = $name; + $this->authorEmail = $email; + $this->authorUrl = $url; + return $this; + } + + /** + * Build item + * + * @abstract + * @access public + * @return DOMElement + */ + abstract public function build(); +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20FeedBuilder.php b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20FeedBuilder.php new file mode 100644 index 0000000000..bc3f513588 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20FeedBuilder.php @@ -0,0 +1,76 @@ +helper = new Rss20Helper($this->getDocument()); + + $this->rssElement = $this->getDocument()->createElement('rss'); + $this->rssElement->setAttribute('version', '2.0'); + $this->rssElement->setAttributeNodeNS(new DomAttr('xmlns:content', 'http://purl.org/rss/1.0/modules/content/')); + $this->rssElement->setAttributeNodeNS(new DomAttr('xmlns:atom', 'http://www.w3.org/2005/Atom')); + + $this->channelElement = $this->getDocument()->createElement('channel'); + $this->helper + ->buildNode($this->channelElement, 'generator', 'PicoFeed (https://github.com/miniflux/picoFeed)') + ->buildTitle($this->channelElement, $this->feedTitle) + ->buildNode($this->channelElement, 'description', $this->feedTitle) + ->buildDate($this->channelElement, $this->feedDate) + ->buildAuthor($this->channelElement, 'webMaster', $this->authorName, $this->authorEmail) + ->buildLink($this->channelElement, $this->siteUrl) + ; + + $link = $this->getDocument()->createElement('atom:link'); + $link->setAttribute('href', $this->feedUrl); + $link->setAttribute('rel', 'self'); + $link->setAttribute('type', 'application/rss+xml'); + $this->channelElement->appendChild($link); + + foreach ($this->items as $item) { + $this->channelElement->appendChild($item->build()); + } + + $this->rssElement->appendChild($this->channelElement); + $this->getDocument()->appendChild($this->rssElement); + + if ($filename !== '') { + $this->getDocument()->save($filename); + } + + return $this->getDocument()->saveXML(); + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20Helper.php b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20Helper.php new file mode 100644 index 0000000000..72a19e56cf --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20Helper.php @@ -0,0 +1,115 @@ +document = $document; + } + + /** + * Build node + * + * @access public + * @param DOMElement $element + * @param string $tag + * @param string $value + * @return $this + */ + public function buildNode(DOMElement $element, $tag, $value) + { + $node = $this->document->createElement($tag); + $node->appendChild($this->document->createTextNode($value)); + $element->appendChild($node); + return $this; + } + + /** + * Build title + * + * @access public + * @param DOMElement $element + * @param string $title + * @return $this + */ + public function buildTitle(DOMElement $element, $title) + { + return $this->buildNode($element, 'title', $title); + } + + /** + * Build date element + * + * @access public + * @param DOMElement $element + * @param DateTime $date + * @param string $type + * @return $this + */ + public function buildDate(DOMElement $element, DateTime $date, $type = 'pubDate') + { + return $this->buildNode($element, $type, $date->format(DateTime::RSS)); + } + + /** + * Build link element + * + * @access public + * @param DOMElement $element + * @param string $url + * @return $this + */ + public function buildLink(DOMElement $element, $url) + { + return $this->buildNode($element, 'link', $url); + } + + /** + * Build author element + * + * @access public + * @param DOMElement $element + * @param string $tag + * @param string $authorName + * @param string $authorEmail + * @return $this + */ + public function buildAuthor(DOMElement $element, $tag, $authorName, $authorEmail) + { + if (!empty($authorName)) { + $value = ''; + + if (!empty($authorEmail)) { + $value .= $authorEmail.' ('.$authorName.')'; + } else { + $value = $authorName; + } + + $this->buildNode($element, $tag, $value); + } + + return $this; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20ItemBuilder.php b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20ItemBuilder.php new file mode 100644 index 0000000000..125dc6ac58 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20ItemBuilder.php @@ -0,0 +1,67 @@ +itemElement = $this->feedBuilder->getDocument()->createElement('item'); + $this->helper = new Rss20Helper($this->feedBuilder->getDocument()); + + if (!empty($this->itemId)) { + $guid = $this->feedBuilder->getDocument()->createElement('guid'); + $guid->setAttribute('isPermaLink', 'false'); + $guid->appendChild($this->feedBuilder->getDocument()->createTextNode($this->itemId)); + $this->itemElement->appendChild($guid); + } else { + $guid = $this->feedBuilder->getDocument()->createElement('guid'); + $guid->setAttribute('isPermaLink', 'true'); + $guid->appendChild($this->feedBuilder->getDocument()->createTextNode($this->itemUrl)); + $this->itemElement->appendChild($guid); + } + + $this->helper + ->buildTitle($this->itemElement, $this->itemTitle) + ->buildLink($this->itemElement, $this->itemUrl) + ->buildDate($this->itemElement, $this->itemPublishedDate) + ->buildAuthor($this->itemElement, 'author', $this->authorName, $this->authorEmail) + ; + + if (!empty($this->itemSummary)) { + $this->helper->buildNode($this->itemElement, 'description', $this->itemSummary); + } + + if (!empty($this->itemContent)) { + $node = $this->feedBuilder->getDocument()->createElement('content:encoded'); + $node->appendChild($this->feedBuilder->getDocument()->createCDATASection($this->itemContent)); + $this->itemElement->appendChild($node); + } + + return $this->itemElement; + } +} diff --git a/vendor/miniflux/picofeed/picofeed b/vendor/miniflux/picofeed/picofeed new file mode 100755 index 0000000000..8f35737aa6 --- /dev/null +++ b/vendor/miniflux/picofeed/picofeed @@ -0,0 +1,135 @@ +#!/usr/bin/env php +discover($url); + + $parser = $reader->getParser( + $resource->getUrl(), + $resource->getContent(), + $resource->getEncoding() + ); + + if ($disable_filtering) { + $parser->disableContentFiltering(); + } + + return $parser->execute(); + } + catch (PicoFeedException $e) { + echo 'Exception thrown ===> "'.$e->getMessage().'"'.PHP_EOL; + return false; + } +} + +function get_item($feed, $item_id) +{ + foreach ($feed->items as $item) { + if ($item->getId() === $item_id) { + echo $item; + echo "============= CONTENT ================\n"; + echo $item->getContent(); + echo "\n============= CONTENT ================\n"; + break; + } + } +} + +function dump_feed($url) +{ + $feed = get_feed($url); + echo $feed; +} + +function debug_feed($url) +{ + get_feed($url); + print_r(Logger::getMessages()); +} + +function dump_item($url, $item_id) +{ + $feed = get_feed($url); + + if ($feed !== false) { + get_item($feed, $item_id); + } +} + +function nofilter_item($url, $item_id) +{ + $feed = get_feed($url, true); + + if ($feed !== false) { + get_item($feed, $item_id); + } +} + +function grabber($url) +{ + $grabber = new Scraper(new Config); + $grabber->setUrl($url); + $grabber->execute(); + + print_r(Logger::getMessages()); + echo "============= CONTENT ================\n"; + echo $grabber->getRelevantContent().PHP_EOL; + echo "============= FILTERED ================\n"; + echo $grabber->getFilteredContent().PHP_EOL; +} + +function fetch_favicon($url) +{ + $favicon = new Favicon(); + echo $favicon->find($url) . PHP_EOL; +} + +// Parse command line arguments +if ($argc === 4) { + switch ($argv[1]) { + case 'item': + dump_item($argv[2], $argv[3]); + die; + case 'nofilter': + nofilter_item($argv[2], $argv[3]); + die; + } +} else if ($argc === 3) { + switch ($argv[1]) { + case 'feed': + dump_feed($argv[2]); + die; + case 'debug': + debug_feed($argv[2]); + die; + case 'grabber': + grabber($argv[2]); + die; + case 'favicon': + fetch_favicon($argv[2]); + die; + } +} + +printf("Usage:\n"); +printf("%s feed \n", $argv[0]); +printf("%s debug \n", $argv[0]); +printf("%s item \n", $argv[0]); +printf("%s nofilter \n", $argv[0]); +printf("%s grabber \n", $argv[0]); +printf("%s favicon \n", $argv[0]); diff --git a/vendor/paragonie/random_compat/CHANGELOG.md b/vendor/paragonie/random_compat/CHANGELOG.md new file mode 100644 index 0000000000..247deace66 --- /dev/null +++ b/vendor/paragonie/random_compat/CHANGELOG.md @@ -0,0 +1,260 @@ +### Version 2.0.2 - 2016-04-03 + +Added a consistency check (discovered by Taylor Hornby in his +[PHP encryption library](https://github.com/defuse/php-encryption)). It +wasn't likely causing any trouble for us. + +### Version 2.0.1 - 2016-03-18 + +Update comment in random.php + +### Version 2.0.0 - 2016-03-18 + +Due to downstream errors, the OpenSSL removal now belongs in version +2.0.0. + +### Version 1.3.1 - 2016-03-18 + +* Add more possible values to `open_baseir` check. + +### Version 1.3.0 - 2016-03-17 + +* Removed `openssl_random_pseudo_bytes()` entirely. If you are using + random_compat in PHP on a Unix-like OS but cannot access + `/dev/urandom`, version 1.3+ will throw an `Exception`. If you want to + trust OpenSSL, feel free to write your own fallback code. e.g. + + ```php + try { + $bytes = random_bytes(32); + } catch (Exception $ex) { + $strong = false; + $bytes = openssl_random_pseudo_bytes(32, $strong); + if (!$strong) { + throw $ex; + } + } + ``` + +### Version 1.2.2 - 2016-03-11 + +* To prevent applications from hanging, if `/dev/urandom` is not + accessible to PHP, skip mcrypt (which just fails before giving OpenSSL + a chance and was morally equivalent to not offering OpenSSL at all). + +### Version 1.2.1 - 2016-02-29 + +* PHP 5.6.10 - 5.6.12 will hang when mcrypt is used on Unix-based operating + systems ([PHP bug 69833](https://bugs.php.net/bug.php?id=69833)). If you are + running one of these versions, please upgrade (or make sure `/dev/urandom` is + readable) otherwise you're relying on OpenSSL. + +### Version 1.2.0 - 2016-02-05 + +* Whitespace and other cosmetic changes +* Added a changelog. +* We now ship with a command line utility to build a PHP Archive from the + command line. + + Every time we publish a new release, we will also upload a .phar + to Github. Our public key is signed by our GPG key. + +### Version 1.1.6 - 2016-01-29 + +* Eliminate `open_basedir` warnings by detecting this configuration setting. + (Thanks [@oucil](https://github.com/oucil) for reporting this.) +* Added install instructions to the README. +* Documentation cleanup (there is, in fact, no `MCRYPT_CREATE_IV` constant, I + meant to write `MCRYPT_DEV_URANDOM`) + +### Version 1.1.5 - 2016-01-06 + +Prevent fatal errors on platforms with older versions of libsodium. + +### Version 1.1.4 - 2015-12-10 + +Thanks [@narfbg](https://github.com/narfbg) for [critiquing the previous patch](https://github.com/paragonie/random_compat/issues/79#issuecomment-163590589) +and suggesting a fix. + +### Version 1.1.3 - 2015-12-09 + +The test for COM in disabled_classes is now case-insensitive. + +### Version 1.1.2 - 2015-12-09 + +Don't instantiate COM if it's a disabled class. Removes the E_WARNING on Windows. + +### Version 1.1.1 - 2015-11-30 + +Fix a performance issue with `/dev/urandom` buffering. + +### Version 1.1.0 - 2015-11-09 + +Fix performance issues with ancient versions of PHP on Windows, but dropped +support for PHP < 5.4.1 without mcrypt on Windows 7+ in the process. Since this + is a BC break, semver dictates a minor version bump. + +### Version 1.0.10 - 2015-10-23 + +* Avoid a performance killer with OpenSSL on Windows PHP 5.3.0 - 5.3.3 that was + affecting [WordPress users](https://core.trac.wordpress.org/ticket/34409). +* Use `$var = null` instead of `unset($var)` to avoid triggering the garbage + collector and slowing things down. + +### Version 1.0.9 - 2015-10-20 + +There is an outstanding issue `mcrypt_create_iv()` and PHP 7's `random_bytes()` +on Windows reported by [@nicolas-grekas](https://github.com/nicolas-grekas) caused by `proc_open()` and environment +variable handling (discovered by Appveyor when developing Symfony). + +Since the break is consistent, it's not our responsibility to fix it, but we +should fail the same way PHP 7 will (i.e. throw an `Exception` rather than raise +an error and then throw an `Exception`). + +### Version 1.0.8 - 2015-10-18 + +* Fix usability issues with Windows (`new COM('CAPICOM.Utilities.1')` is not + always available). +* You can now test all the possible drivers by running `phpunit.sh each` in the + `tests` directory. + +### Version 1.0.7 - 2015-10-16 + +Several large integer handling bugfixes were contributed by [@oittaa](https://github.com/oittaa). + +### Version 1.0.6 - 2015-10-15 + +Don't let the version number fool you, this was a pretty significant change. + +1. Added support for ext-libsodium, if it exists on the system. This is morally + equivalent to adding `getrandom(2)` support without having to expose the + syscall interface in PHP-land. +2. Relaxed open_basedir restrictions. In previous versions, if open_basedir was + set, PHP wouldn't even try to read from `/dev/urandom`. Now it will still do + so if you can. +3. Fixed integer casting inconsistencies between random_compat and PHP 7. +4. Handle edge cases where an integer overflow turns one of the parameters into + a float. + +One change that we discussed was making `random_bytes()` and `random_int()` +strict typed; meaning you could *only* pass integers to either function. While +most veteran programmers are probably only doing this already (we strongly +encourage it), it wouldn't be consistent with how these functions behave in PHP +7. Please use these functions responsibly. + +We've had even more of the PHP community involved in this release; the +contributors list has been updated. If I forgot anybody, I promise you it's not +because your contributions (either code or ideas) aren't valued, it's because +I'm a bit overloaded with information at the moment. Please let me know +immediately and I will correct my oversight. + +Thanks everyone for helping make random_compat better. + +### Version 1.0.5 - 2015-10-08 + +Got rid of the methods in the `Throwable` interface, which was causing problems +on PHP 5.2. While we would normally not care about 5.2 (since [5.4 and earlier are EOL'd](https://secure.php.net/supported-versions.php)), +we do want to encourage widespread adoption (e.g. [Wordpress](https://core.trac.wordpress.org/ticket/28633)). + +### Version 1.0.4 - 2015-10-02 + +Removed redundant `if()` checks, since `lib/random.php` is the entrypoint people +should use. + +### Version 1.0.3 - 2015-10-02 + +This release contains bug fixes contributed by the community. + +* Avoid a PHP Notice when PHP is running without the mbstring extension +* Use a compatible version of PHPUnit for testing on older versions of PHP + +Although none of these bugs were outright security-affecting, updating ASAP is +still strongly encouraged. + +### Version 1.0.2 - 2015-09-23 + +Less strict input validation on `random_int()` parameters. PHP 7's `random_int()` +accepts strings and floats that look like numbers, so we should too. + +Thanks [@dd32](https://github.com/@dd32) for correcting this oversight. + +### Version 1.0.1 - 2015-09-10 + +Instead of throwing an Exception immediately on insecure platforms, only do so +when `random_bytes()` is invoked. + +### Version 1.0.0 - 2015-09-07 + +Our API is now stable and forward-compatible with the CSPRNG features in PHP 7 +(as of 7.0.0 RC3). + +A lot of great people have contributed their time and expertise to make this +compatibility library possible. That this library has reached a stable release +is more a reflection on the community than it is on PIE. + +We are confident that random_compat will serve as the simplest and most secure +CSPRNG interface available for PHP5 projects. + +### Version 0.9.7 (pre-release) - 2015-09-01 + +An attempt to achieve compatibility with Error/TypeError in the RFC. + +This should be identical to 1.0.0 sans any last-minute changes or performance enhancements. + +### Version 0.9.6 (pre-release) - 2015-08-06 + +* Split the implementations into their own file (for ease of auditing) +* Corrected the file type check after `/dev/urandom` has been opened (thanks + [@narfbg](https://github.com/narfbg) and [@jedisct1](https://github.com/jedisct1)) + +### Version 0.9.5 (pre-release) - 2015-07-31 + +* Validate that `/dev/urandom` is a character device + * Reported by [@lokdnet](https://twitter.com/lokdnet) + * Investigated by [@narfbg](https://github.com/narfbg) and [frymaster](http://stackoverflow.com/users/1226810/frymaster) on [StackOverflow](http://stackoverflow.com/q/31631066/2224584) +* Remove support for `/dev/arandom` which is an old OpenBSD feature, thanks [@jedisct1](https://github.com/jedisct1) +* Prevent race conditions on the `filetype()` check, thanks [@jedisct1](https://github.com/jedisct1) +* Buffer file reads to 8 bytes (performance optimization; PHP defaults to 8192 bytes) + +### Version 0.9.4 (pre-release) - 2015-07-27 + +* Add logic to verify that `/dev/arandom` and `/dev/urandom` are actually devices. +* Some clean-up in the comments + +### Version 0.9.3 (pre-release) - 2015-07-22 + +Unless the Exceptions change to PHP 7 fails, this should be the last pre-release +version. If need be, we'll make one more pre-release version with compatible +behavior. + +Changes since 0.9.2: + +* Prioritize `/dev/arandom` and `/dev/urandom` over mcrypt. +[@oittaa](https://github.com/oittaa) removed the -1 and +1 juggling on `$range` calculations for `random_int()` +* Whitespace and comment clean-up, plus better variable names +* Actually put a description in the composer.json file... + +### Version 0.9.2 (pre-release) - 2015-07-16 + +* Consolidated `$range > PHP_INT_MAX` logic with `$range <= PHP_INT_MAX` (thanks + [@oittaa](https://github.com/oittaa) and [@CodesInChaos](https://github.com/CodesInChaos)) +* `tests/phpunit.sh` now also runs the tests with `mbstring.func_overload` and + `open_basedir` +* Style consistency, whitespace cleanup, more meaningful variable names + +### Version 0.9.1 (pre-release) - 2015-07-09 + +* Return random values on integer ranges > `PHP_INT_MAX` (thanks [@CodesInChaos](https://github.com/CodesInChaos)) +* Determined CSPRNG preference: + 1. `mcrypt_create_iv()` with `MCRYPT_DEV_URANDOM` + 2. `/dev/arandom` + 3. `/dev/urandom` + 4. `openssl_random_pseudo_bytes()` +* Optimized backend selection (thanks [@lt](https://github.com/lt)) +* Fix #3 (thanks [@scottchiefbaker](https://github.com/scottchiefbaker)) + +### Version 0.9.0 (pre-release) - 2015-07-07 + +This should be a sane polyfill for PHP 7's `random_bytes()` and `random_int()`. +We hesitate to call it production ready until it has received sufficient third +party review. \ No newline at end of file diff --git a/vendor/paragonie/random_compat/ERRATA.md b/vendor/paragonie/random_compat/ERRATA.md new file mode 100644 index 0000000000..0561630dd8 --- /dev/null +++ b/vendor/paragonie/random_compat/ERRATA.md @@ -0,0 +1,34 @@ +## Errata (Design Decisions) + +### Reasoning Behind the Order of Preferred Random Data Sources + +The order is: + + 1. `libsodium if available` + 2. `fread() /dev/urandom if available` + 3. `mcrypt_create_iv($bytes, MCRYPT_DEV_URANDOM)` + 4. `COM('CAPICOM.Utilities.1')->GetRandom()` + +If libsodium is available, we get random data from it. This is the preferred +method on all OSes, but libsodium is not very widely installed, so other +fallbacks are available. + +Next, we read `/dev/urandom` (if it exists). This is the preferred file to read +for random data for cryptographic purposes for BSD and Linux. + +Despite [strongly urging people not to use mcrypt in their projects](https://paragonie.com/blog/2015/05/if-you-re-typing-word-mcrypt-into-your-code-you-re-doing-it-wrong), +because libmcrypt is abandonware and the API puts too much responsibility on the +implementor, we prioritize `mcrypt_create_iv()` with `MCRYPT_DEV_URANDOM` above +the remaining implementations. + +The reason is simple: `mcrypt_create_iv()` is part of PHP's `ext/mcrypt` code, +and is not part `libmcrypt`. It actually does the right thing: + + * On Unix-based operating systems, it reads from `/dev/urandom`, which unlike `/dev/random` + is the sane and correct thing to do. + * On Windows, it reads from `CryptGenRandom`, which is an exclusively Windows + way to get random bytes. + +If we're on Windows and don't have access to `mcrypt`, we use `CAPICOM.Utilities.1`. + +As of random_compat 1.3, we no longer fall through to OpenSSL. diff --git a/vendor/paragonie/random_compat/LICENSE b/vendor/paragonie/random_compat/LICENSE new file mode 100644 index 0000000000..45c7017dfb --- /dev/null +++ b/vendor/paragonie/random_compat/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Paragon Initiative Enterprises + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/vendor/paragonie/random_compat/README.md b/vendor/paragonie/random_compat/README.md new file mode 100644 index 0000000000..80560862b5 --- /dev/null +++ b/vendor/paragonie/random_compat/README.md @@ -0,0 +1,176 @@ +# random_compat + +[![Build Status](https://travis-ci.org/paragonie/random_compat.svg?branch=master)](https://travis-ci.org/paragonie/random_compat) +[![Scrutinizer](https://scrutinizer-ci.com/g/paragonie/random_compat/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/paragonie/random_compat) + +PHP 5.x polyfill for `random_bytes()` and `random_int()` created and maintained +by [Paragon Initiative Enterprises](https://paragonie.com). + +Although this library *should* function in earlier versions of PHP, we will only +consider issues relevant to [supported PHP versions](https://secure.php.net/supported-versions.php). +**If you are using an unsupported version of PHP, please upgrade as soon as possible.** + +## Important + +Although this library has been examined by some security experts in the PHP +community, there will always be a chance that we overlooked something. Please +ask your favorite trusted hackers to hammer it for implementation errors and +bugs before even thinking about deploying it in production. + +**Do not use the master branch, use a [stable release](https://github.com/paragonie/random_compat/releases/latest).** + +For the background of this library, please refer to our blog post on +[Generating Random Integers and Strings in PHP](https://paragonie.com/blog/2015/07/how-safely-generate-random-strings-and-integers-in-php). + +### Usability Notice + +If PHP cannot safely generate random data, this library will throw an `Exception`. +It will never fall back to insecure random data. If this keeps happening, upgrade +to a newer version of PHP immediately. + +## Installing + +**With [Composer](https://getcomposer.org):** + + composer require paragonie/random_compat + +**Signed PHP Archive:** + +As of version 1.2.0, we also ship an ECDSA-signed PHP Archive with each stable +release on Github. + +1. Download [the `.phar`, `.phar.pubkey`, and `.phar.pubkey.asc`](https://github.com/paragonie/random_compat/releases/latest) files. +2. (**Recommended** but not required) Verify the PGP signature of `.phar.pubkey` + (contained within the `.asc` file) using the [PGP public key for Paragon Initiative Enterprises](https://paragonie.com/static/gpg-public-key.txt). +3. Extract both `.phar` and `.phar.pubkey` files to the same directory. +4. `require_once "/path/to/random_compat.phar";` +5. When a new version is released, you only need to replace the `.phar` file; + the `.pubkey` will not change (unless our signing key is ever compromised). + +**Manual Installation:** + +1. Download [a stable release](https://github.com/paragonie/random_compat/releases/latest). +2. Extract the files into your project. +3. `require_once "/path/to/random_compat/lib/random.php";` + +## Usage + +This library exposes the [CSPRNG functions added in PHP 7](https://secure.php.net/manual/en/ref.csprng.php) +for use in PHP 5 projects. Their behavior should be identical. + +### Generate a string of random bytes + +```php +try { + $string = random_bytes(32); +} catch (TypeError $e) { + // Well, it's an integer, so this IS unexpected. + die("An unexpected error has occurred"); +} catch (Error $e) { + // This is also unexpected because 32 is a reasonable integer. + die("An unexpected error has occurred"); +} catch (Exception $e) { + // If you get this message, the CSPRNG failed hard. + die("Could not generate a random string. Is our OS secure?"); +} + +var_dump(bin2hex($string)); +// string(64) "5787c41ae124b3b9363b7825104f8bc8cf27c4c3036573e5f0d4a91ad2eeac6f" +``` + +### Generate a random integer between two given integers (inclusive) + +```php +try { + $int = random_int(0,255); + +} catch (TypeError $e) { + // Well, it's an integer, so this IS unexpected. + die("An unexpected error has occurred"); +} catch (Error $e) { + // This is also unexpected because 0 and 255 are both reasonable integers. + die("An unexpected error has occurred"); +} catch (Exception $e) { + // If you get this message, the CSPRNG failed hard. + die("Could not generate a random string. Is our OS secure?"); +} + +var_dump($int); +// int(47) +``` + +### Exception handling + +When handling exceptions and errors you must account for differences between +PHP 5 and PHP7. + +The differences: + +* Catching `Error` works, so long as it is caught before `Exception`. +* Catching `Exception` has different behavior, without previously catching `Error`. +* There is *no* portable way to catch all errors/exceptions. + +#### Our recommendation + +**Always** catch `Error` before `Exception`. + +#### Example + +```php +try { + return random_int(1, $userInput); +} catch (TypeError $e) { + // This is okay, so long as `Error` is caught before `Exception`. + throw new Exception('Please enter a number!'); +} catch (Error $e) { + // This is required, if you do not need to do anything just rethrow. + throw $e; +} catch (Exception $e) { + // This is optional and maybe omitted if you do not want to handle errors + // during generation. + throw new InternalServerErrorException( + 'Oops, our server is bust and cannot generate any random data.', + 500, + $e + ); +} +``` + +## Contributors + +This project would not be anywhere near as excellent as it is today if it +weren't for the contributions of the following individuals: + +* [@AndrewCarterUK (Andrew Carter)](https://github.com/AndrewCarterUK) +* [@asgrim (James Titcumb)](https://github.com/asgrim) +* [@bcremer (Benjamin Cremer)](https://github.com/bcremer) +* [@CodesInChaos (Christian Winnerlein)](https://github.com/CodesInChaos) +* [@chriscct7 (Chris Christoff)](https://github.com/chriscct7) +* [@cs278 (Chris Smith)](https://github.com/cs278) +* [@cweagans (Cameron Eagans)](https://github.com/cweagans) +* [@dd32 (Dion Hulse)](https://github.com/dd32) +* [@geggleto (Glenn Eggleton)](https://github.com/geggleto) +* [@ircmaxell (Anthony Ferrara)](https://github.com/ircmaxell) +* [@jedisct1 (Frank Denis)](https://github.com/jedisct1) +* [@juliangut (Julián Gutiérrez)](https://github.com/juliangut) +* [@kelunik (Niklas Keller)](https://github.com/kelunik) +* [@lt (Leigh)](https://github.com/lt) +* [@MasonM (Mason Malone)](https://github.com/MasonM) +* [@mmeyer2k (Michael M)](https://github.com/mmeyer2k) +* [@narfbg (Andrey Andreev)](https://github.com/narfbg) +* [@nicolas-grekas (Nicolas Grekas)](https://github.com/nicolas-grekas) +* [@oittaa](https://github.com/oittaa) +* [@oucil (Kevin Farley)](https://github.com/oucil) +* [@redragonx (Stephen Chavez)](https://github.com/redragonx) +* [@rchouinard (Ryan Chouinard)](https://github.com/rchouinard) +* [@SammyK (Sammy Kaye Powers)](https://github.com/SammyK) +* [@scottchiefbaker (Scott Baker)](https://github.com/scottchiefbaker) +* [@skyosev (Stoyan Kyosev)](https://github.com/skyosev) +* [@stof (Christophe Coevoet)](https://github.com/stof) +* [@teohhanhui (Teoh Han Hui)](https://github.com/teohhanhui) +* [@tom-- (Tom Worster)](https://github.com/tom--) +* [@tsyr2ko](https://github.com/tsyr2ko) +* [@trowski (Aaron Piotrowski)](https://github.com/trowski) +* [@twistor (Chris Lepannen)](https://github.com/twistor) +* [@voku (Lars Moelleken)](https://github.com/voku) +* [@xabbuh (Christian Flothmann)](https://github.com/xabbuh) diff --git a/vendor/paragonie/random_compat/SECURITY.md b/vendor/paragonie/random_compat/SECURITY.md new file mode 100644 index 0000000000..8f731b38da --- /dev/null +++ b/vendor/paragonie/random_compat/SECURITY.md @@ -0,0 +1,108 @@ +# An Invitation to Security Researchers + +Every company says they take security "very seriously." Rather than bore anyone +with banal boilerplate, here are some quick answers followed by detailed +elaboration. If you have any questions about our policies, please email them to +`scott@paragonie.com`. + +## Quick Answers + +* There is no compulsion to disclose vulnerabilities privately, but we + appreciate a head's up. +* `security@paragonie.com` will get your reports to the right person. Our GPG + fingerprint, should you decide to encrypt your report, is + `7F52 D5C6 1D12 55C7 3136 2E82 6B97 A1C2 8264 04DA`. + +* **YES**, we will reward security researchers who disclose vulnerabilities in + our software. +* In most cases, **No Proof-of-Concept Required.** + +## How to Report a Security Bug to Paragon Initiative Enterprises + +### There is no compulsion to disclose privately. + +We believe vulnerability disclosure style is a personal choice and enjoy working +with a diverse community. We understand and appreciate the importance of Full +Disclosure in the history and practice of security research. + +We would *like* to know about high-severity bugs before they become public +knowledge, so we can fix them in a timely manner, but **we do not believe in +threatening researchers or trying to enforce vulnerability embargoes**. + +Ultimately, if you discover a security-affecting vulnerability, what you do with +it is your choice. We would like to work with people, and to celebrate and +reward their skill, experience, and dedication. We appreciate being informed of +our mistakes so we can learn from them and build a better product. Our goal is +to empower the community. + +### Where to Send Security Vulnerabilities + +Our security email address is `security@paragonie.com`. Also feel free to open a +new issue on Github if you want to disclose publicly. + +``` +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG + +mQENBFUgwRUBCADcIpqNwyYc5UmY/tpx1sF/rQ3knR1YNXYZThzFV+Gmqhp1fDH5 +qBs9foh1xwI6O7knWmQngnf/nBumI3x6xj7PuOdEZUh2FwCG/VWnglW8rKmoHzHA +ivjiu9SLnPIPAgHSHeh2XD7q3Ndm3nenbjAiRFNl2iXcwA2cTQp9Mmfw9vVcw0G0 +z1o0G3s8cC8ZS6flFySIervvfSRWj7A1acI5eE3+AH/qXJRdEJ+9J8OB65p1JMfk +6+fWgOB1XZxMpz70S0rW6IX38WDSRhEK2fXyZJAJjyt+YGuzjZySNSoQR/V6vNYn +syrNPCJ2i5CgZQxAkyBBcr7koV9RIhPRzct/ABEBAAG0IVNlY3VyaXR5IDxzZWN1 +cml0eUBwYXJhZ29uaWUuY29tPokBOQQTAQIAIwUCVSDBFQIbAwcLCQgHAwIBBhUI +AgkKCwQWAgMBAh4BAheAAAoJEGuXocKCZATat2YIAIoejNFEQ2c1iaOEtSuB7Pn/ +WLbsDsHNLDKOV+UnfaCjv/vL7D+5NMChFCi2frde/NQb2TsjqmIH+V+XbnJtlrXD +Vj7yvMVal+Jqjwj7v4eOEWcKVcFZk+9cfUgh7t92T2BMX58RpgZF0IQZ6Z1R3FfC +9Ub4X6ykW+te1q0/4CoRycniwmlQi6iGSr99LQ5pfJq2Qlmz/luTZ0UX0h575T7d +cp2T1sX/zFRk/fHeANWSksipdDBjAXR7NMnYZgw2HghEdFk/xRDY7K1NRWNZBf05 +WrMHmh6AIVJiWZvI175URxEe268hh+wThBhXQHMhFNJM1qPIuzb4WogxM3UUD7m5 +AQ0EVSDBFQEIALNkpzSuJsHAHh79sc0AYWztdUe2MzyofQbbOnOCpWZebYsC3EXU +335fIg59k0m6f+O7GmEZzzIv5v0i99GS1R8CJm6FvhGqtH8ZqmOGbc71WdJSiNVE +0kpQoJlVzRbig6ZyyjzrggbM1eh5OXOk5pw4+23FFEdw7JWU0HJS2o71r1hwp05Z +vy21kcUEobz/WWQQyGS0Neo7PJn+9KS6wOxXul/UE0jct/5f7KLMdWMJ1VgniQmm +hjvkHLPSICteqCI04RfcmMseW9gueHQXeUu1SNIvsWa2MhxjeBej3pDnrZWszKwy +gF45GO9/v4tkIXNMy5J1AtOyRgQ3IUMqp8EAEQEAAYkBHwQYAQIACQUCVSDBFQIb +DAAKCRBrl6HCgmQE2jnIB/4/xFz8InpM7eybnBOAir3uGcYfs3DOmaKn7qWVtGzv +rKpQPYnVtlU2i6Z5UO4c4jDLT/8Xm1UDz3Lxvqt4xCaDwJvBZexU5BMK8l5DvOzH +6o6P2L1UDu6BvmPXpVZz7/qUhOnyf8VQg/dAtYF4/ax19giNUpI5j5o5mX5w80Rx +qSXV9NdSL4fdjeG1g/xXv2luhoV53T1bsycI3wjk/x5tV+M2KVhZBvvuOm/zhJje +oLWp0saaESkGXIXqurj6gZoujJvSvzl0n9F9VwqMEizDUfrXgtD1siQGhP0sVC6q +ha+F/SAEJ0jEquM4TfKWWU2S5V5vgPPpIQSYRnhQW4b1 +=xJPW +-----END PGP PUBLIC KEY BLOCK----- +``` + +### We Will Reward Security Researchers + +**This process has not been formalized; nor have dollar amounts been +discussed.** + +However, if you report a valid security-affecting bug, we will compensate you +for the time spent finding the vulnerability and reward you for being a good +neighbor. + +#### What does a "valid" bug mean? + +There are two sides to this: + +1. Some have spammed projects with invalid bug reports hoping to collect + bounties for pressing a button and running an automated analysis tool. This + is not cool. +2. There is a potential for the developers of a project to declare all security + bug reports as invalid to save money. + +Our team members have an established history of reporting vulnerabilities to +large open source projects. **We aren't in the business of ripping people off.** +When in doubt, our policy is to err on the side of generosity. + +### No Proof-of-Concept Required + +We might ask for one if we feel we do not understand some of the details +pertaining to a specific vulnerability. We certainly appreciate them if you +include them in your report, but we believe **the burden lies with the developer +to prove their software *is* secure** rather than with the researcher to prove +that it isn't. + +In our experience, most bugs are simpler to fix than they are to exploit. + diff --git a/vendor/paragonie/random_compat/build-phar.sh b/vendor/paragonie/random_compat/build-phar.sh new file mode 100755 index 0000000000..b4a5ba31cc --- /dev/null +++ b/vendor/paragonie/random_compat/build-phar.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +basedir=$( dirname $( readlink -f ${BASH_SOURCE[0]} ) ) + +php -dphar.readonly=0 "$basedir/other/build_phar.php" $* \ No newline at end of file diff --git a/vendor/paragonie/random_compat/composer.json b/vendor/paragonie/random_compat/composer.json new file mode 100644 index 0000000000..d363f4c8cb --- /dev/null +++ b/vendor/paragonie/random_compat/composer.json @@ -0,0 +1,35 @@ +{ + "name": "paragonie/random_compat", + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "random", + "pseudorandom" + ], + "license": "MIT", + "type": "library", + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "support": { + "issues": "https://github.com/paragonie/random_compat/issues", + "email": "info@paragonie.com", + "source": "https://github.com/paragonie/random_compat" + }, + "require": { + "php": ">=5.2.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "autoload": { + "files": ["lib/random.php"] + } +} diff --git a/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey b/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey new file mode 100644 index 0000000000..eb50ebfcd6 --- /dev/null +++ b/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey @@ -0,0 +1,5 @@ +-----BEGIN PUBLIC KEY----- +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEEd+wCqJDrx5B4OldM0dQE0ZMX+lx1ZWm +pui0SUqD4G29L3NGsz9UhJ/0HjBdbnkhIK5xviT0X5vtjacF6ajgcCArbTB+ds+p ++h7Q084NuSuIpNb6YPfoUFgC/CL9kAoc +-----END PUBLIC KEY----- diff --git a/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey.asc b/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey.asc new file mode 100644 index 0000000000..6a1d7f3006 --- /dev/null +++ b/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey.asc @@ -0,0 +1,11 @@ +-----BEGIN PGP SIGNATURE----- +Version: GnuPG v2.0.22 (MingW32) + +iQEcBAABAgAGBQJWtW1hAAoJEGuXocKCZATaJf0H+wbZGgskK1dcRTsuVJl9IWip +QwGw/qIKI280SD6/ckoUMxKDCJiFuPR14zmqnS36k7N5UNPnpdTJTS8T11jttSpg +1LCmgpbEIpgaTah+cELDqFCav99fS+bEiAL5lWDAHBTE/XPjGVCqeehyPYref4IW +NDBIEsvnHPHPLsn6X5jq4+Yj5oUixgxaMPiR+bcO4Sh+RzOVB6i2D0upWfRXBFXA +NNnsg9/zjvoC7ZW73y9uSH+dPJTt/Vgfeiv52/v41XliyzbUyLalf02GNPY+9goV +JHG1ulEEBJOCiUD9cE1PUIJwHA/HqyhHIvV350YoEFiHl8iSwm7SiZu5kPjaq74= +=B6+8 +-----END PGP SIGNATURE----- diff --git a/vendor/paragonie/random_compat/lib/byte_safe_strings.php b/vendor/paragonie/random_compat/lib/byte_safe_strings.php new file mode 100644 index 0000000000..dec5d3062b --- /dev/null +++ b/vendor/paragonie/random_compat/lib/byte_safe_strings.php @@ -0,0 +1,181 @@ + RandomCompat_strlen($binary_string)) { + return false; + } + + return mb_substr($binary_string, $start, $length, '8bit'); + } + + } else { + + /** + * substr() implementation that isn't brittle to mbstring.func_overload + * + * This version just uses the default substr() + * + * @param string $binary_string + * @param int $start + * @param int $length (optional) + * + * @throws TypeError + * + * @return string + */ + function RandomCompat_substr($binary_string, $start, $length = null) + { + if (!is_string($binary_string)) { + throw new TypeError( + 'RandomCompat_substr(): First argument should be a string' + ); + } + + if (!is_int($start)) { + throw new TypeError( + 'RandomCompat_substr(): Second argument should be an integer' + ); + } + + if ($length !== null) { + if (!is_int($length)) { + throw new TypeError( + 'RandomCompat_substr(): Third argument should be an integer, or omitted' + ); + } + + return substr($binary_string, $start, $length); + } + + return substr($binary_string, $start); + } + } +} diff --git a/vendor/paragonie/random_compat/lib/cast_to_int.php b/vendor/paragonie/random_compat/lib/cast_to_int.php new file mode 100644 index 0000000000..f441c5d986 --- /dev/null +++ b/vendor/paragonie/random_compat/lib/cast_to_int.php @@ -0,0 +1,71 @@ + operators might accidentally let a float + * through. + * + * @param int|float $number The number we want to convert to an int + * @param boolean $fail_open Set to true to not throw an exception + * + * @return int (or float if $fail_open) + * + * @throws TypeError + */ + function RandomCompat_intval($number, $fail_open = false) + { + if (is_numeric($number)) { + $number += 0; + } + + if ( + is_float($number) + && + $number > ~PHP_INT_MAX + && + $number < PHP_INT_MAX + ) { + $number = (int) $number; + } + + if (is_int($number) || $fail_open) { + return $number; + } + + throw new TypeError( + 'Expected an integer.' + ); + } +} diff --git a/vendor/paragonie/random_compat/lib/error_polyfill.php b/vendor/paragonie/random_compat/lib/error_polyfill.php new file mode 100644 index 0000000000..57cfefdcd1 --- /dev/null +++ b/vendor/paragonie/random_compat/lib/error_polyfill.php @@ -0,0 +1,42 @@ +GetRandom() + * 5. openssl_random_pseudo_bytes() (absolute last resort) + * + * See ERRATA.md for our reasoning behind this particular order + */ + if (extension_loaded('libsodium')) { + // See random_bytes_libsodium.php + if (PHP_VERSION_ID >= 50300 && function_exists('\\Sodium\\randombytes_buf')) { + require_once $RandomCompatDIR.'/random_bytes_libsodium.php'; + } elseif (method_exists('Sodium', 'randombytes_buf')) { + require_once $RandomCompatDIR.'/random_bytes_libsodium_legacy.php'; + } + } + + /** + * Reading directly from /dev/urandom: + */ + if (DIRECTORY_SEPARATOR === '/') { + // DIRECTORY_SEPARATOR === '/' on Unix-like OSes -- this is a fast + // way to exclude Windows. + $RandomCompatUrandom = true; + $RandomCompat_basedir = ini_get('open_basedir'); + + if (!empty($RandomCompat_basedir)) { + $RandomCompat_open_basedir = explode( + PATH_SEPARATOR, + strtolower($RandomCompat_basedir) + ); + $RandomCompatUrandom = (array() !== array_intersect( + array('/dev', '/dev/', '/dev/urandom'), + $RandomCompat_open_basedir + )); + $RandomCompat_open_basedir = null; + } + + if ( + !function_exists('random_bytes') + && + $RandomCompatUrandom + && + @is_readable('/dev/urandom') + ) { + // Error suppression on is_readable() in case of an open_basedir + // or safe_mode failure. All we care about is whether or not we + // can read it at this point. If the PHP environment is going to + // panic over trying to see if the file can be read in the first + // place, that is not helpful to us here. + + // See random_bytes_dev_urandom.php + require_once $RandomCompatDIR.'/random_bytes_dev_urandom.php'; + } + // Unset variables after use + $RandomCompat_basedir = null; + } else { + $RandomCompatUrandom = false; + } + + /** + * mcrypt_create_iv() + */ + if ( + !function_exists('random_bytes') + && + PHP_VERSION_ID >= 50307 + && + extension_loaded('mcrypt') + && + (DIRECTORY_SEPARATOR !== '/' || $RandomCompatUrandom) + ) { + // Prevent this code from hanging indefinitely on non-Windows; + // see https://bugs.php.net/bug.php?id=69833 + if ( + DIRECTORY_SEPARATOR !== '/' || + (PHP_VERSION_ID <= 50609 || PHP_VERSION_ID >= 50613) + ) { + // See random_bytes_mcrypt.php + require_once $RandomCompatDIR.'/random_bytes_mcrypt.php'; + } + } + $RandomCompatUrandom = null; + + if ( + !function_exists('random_bytes') + && + extension_loaded('com_dotnet') + && + class_exists('COM') + ) { + $RandomCompat_disabled_classes = preg_split( + '#\s*,\s*#', + strtolower(ini_get('disable_classes')) + ); + + if (!in_array('com', $RandomCompat_disabled_classes)) { + try { + $RandomCompatCOMtest = new COM('CAPICOM.Utilities.1'); + if (method_exists($RandomCompatCOMtest, 'GetRandom')) { + // See random_bytes_com_dotnet.php + require_once $RandomCompatDIR.'/random_bytes_com_dotnet.php'; + } + } catch (com_exception $e) { + // Don't try to use it. + } + } + $RandomCompat_disabled_classes = null; + $RandomCompatCOMtest = null; + } + + /** + * throw new Exception + */ + if (!function_exists('random_bytes')) { + /** + * We don't have any more options, so let's throw an exception right now + * and hope the developer won't let it fail silently. + */ + function random_bytes($length) + { + throw new Exception( + 'There is no suitable CSPRNG installed on your system' + ); + } + } + } + + if (!function_exists('random_int')) { + require_once $RandomCompatDIR.'/random_int.php'; + } + + $RandomCompatDIR = null; +} diff --git a/vendor/paragonie/random_compat/lib/random_bytes_com_dotnet.php b/vendor/paragonie/random_compat/lib/random_bytes_com_dotnet.php new file mode 100644 index 0000000000..3422825492 --- /dev/null +++ b/vendor/paragonie/random_compat/lib/random_bytes_com_dotnet.php @@ -0,0 +1,81 @@ +GetRandom($bytes, 0)); + if (RandomCompat_strlen($buf) >= $bytes) { + /** + * Return our random entropy buffer here: + */ + return RandomCompat_substr($buf, 0, $bytes); + } + ++$execCount; + } while ($execCount < $bytes); + + /** + * If we reach here, PHP has failed us. + */ + throw new Exception( + 'Could not gather sufficient random data' + ); +} diff --git a/vendor/paragonie/random_compat/lib/random_bytes_dev_urandom.php b/vendor/paragonie/random_compat/lib/random_bytes_dev_urandom.php new file mode 100644 index 0000000000..db93b07578 --- /dev/null +++ b/vendor/paragonie/random_compat/lib/random_bytes_dev_urandom.php @@ -0,0 +1,148 @@ + 0); + + /** + * Is our result valid? + */ + if ($buf !== false) { + if (RandomCompat_strlen($buf) === $bytes) { + /** + * Return our random entropy buffer here: + */ + return $buf; + } + } + } + + /** + * If we reach here, PHP has failed us. + */ + throw new Exception( + 'Error reading from source device' + ); +} diff --git a/vendor/paragonie/random_compat/lib/random_bytes_libsodium.php b/vendor/paragonie/random_compat/lib/random_bytes_libsodium.php new file mode 100644 index 0000000000..f802d4e124 --- /dev/null +++ b/vendor/paragonie/random_compat/lib/random_bytes_libsodium.php @@ -0,0 +1,86 @@ + 2147483647) { + $buf = ''; + for ($i = 0; $i < $bytes; $i += 1073741824) { + $n = ($bytes - $i) > 1073741824 + ? 1073741824 + : $bytes - $i; + $buf .= \Sodium\randombytes_buf($n); + } + } else { + $buf = \Sodium\randombytes_buf($bytes); + } + + if ($buf !== false) { + if (RandomCompat_strlen($buf) === $bytes) { + return $buf; + } + } + + /** + * If we reach here, PHP has failed us. + */ + throw new Exception( + 'Could not gather sufficient random data' + ); +} diff --git a/vendor/paragonie/random_compat/lib/random_bytes_libsodium_legacy.php b/vendor/paragonie/random_compat/lib/random_bytes_libsodium_legacy.php new file mode 100644 index 0000000000..44fddbf6fc --- /dev/null +++ b/vendor/paragonie/random_compat/lib/random_bytes_libsodium_legacy.php @@ -0,0 +1,86 @@ + 2147483647) { + $buf = ''; + for ($i = 0; $i < $bytes; $i += 1073741824) { + $n = ($bytes - $i) > 1073741824 + ? 1073741824 + : $bytes - $i; + $buf .= Sodium::randombytes_buf($n); + } + } else { + $buf = Sodium::randombytes_buf($bytes); + } + + if ($buf !== false) { + if (RandomCompat_strlen($buf) === $bytes) { + return $buf; + } + } + + /** + * If we reach here, PHP has failed us. + */ + throw new Exception( + 'Could not gather sufficient random data' + ); +} diff --git a/vendor/paragonie/random_compat/lib/random_bytes_mcrypt.php b/vendor/paragonie/random_compat/lib/random_bytes_mcrypt.php new file mode 100644 index 0000000000..7ac9d9105f --- /dev/null +++ b/vendor/paragonie/random_compat/lib/random_bytes_mcrypt.php @@ -0,0 +1,76 @@ + operators might accidentally let a float + * through. + */ + + try { + $min = RandomCompat_intval($min); + } catch (TypeError $ex) { + throw new TypeError( + 'random_int(): $min must be an integer' + ); + } + + try { + $max = RandomCompat_intval($max); + } catch (TypeError $ex) { + throw new TypeError( + 'random_int(): $max must be an integer' + ); + } + + /** + * Now that we've verified our weak typing system has given us an integer, + * let's validate the logic then we can move forward with generating random + * integers along a given range. + */ + if ($min > $max) { + throw new Error( + 'Minimum value must be less than or equal to the maximum value' + ); + } + + if ($max === $min) { + return $min; + } + + /** + * Initialize variables to 0 + * + * We want to store: + * $bytes => the number of random bytes we need + * $mask => an integer bitmask (for use with the &) operator + * so we can minimize the number of discards + */ + $attempts = $bits = $bytes = $mask = $valueShift = 0; + + /** + * At this point, $range is a positive number greater than 0. It might + * overflow, however, if $max - $min > PHP_INT_MAX. PHP will cast it to + * a float and we will lose some precision. + */ + $range = $max - $min; + + /** + * Test for integer overflow: + */ + if (!is_int($range)) { + + /** + * Still safely calculate wider ranges. + * Provided by @CodesInChaos, @oittaa + * + * @ref https://gist.github.com/CodesInChaos/03f9ea0b58e8b2b8d435 + * + * We use ~0 as a mask in this case because it generates all 1s + * + * @ref https://eval.in/400356 (32-bit) + * @ref http://3v4l.org/XX9r5 (64-bit) + */ + $bytes = PHP_INT_SIZE; + $mask = ~0; + + } else { + + /** + * $bits is effectively ceil(log($range, 2)) without dealing with + * type juggling + */ + while ($range > 0) { + if ($bits % 8 === 0) { + ++$bytes; + } + ++$bits; + $range >>= 1; + $mask = $mask << 1 | 1; + } + $valueShift = $min; + } + + /** + * Now that we have our parameters set up, let's begin generating + * random integers until one falls between $min and $max + */ + do { + /** + * The rejection probability is at most 0.5, so this corresponds + * to a failure probability of 2^-128 for a working RNG + */ + if ($attempts > 128) { + throw new Exception( + 'random_int: RNG is broken - too many rejections' + ); + } + + /** + * Let's grab the necessary number of random bytes + */ + $randomByteString = random_bytes($bytes); + if ($randomByteString === false) { + throw new Exception( + 'Random number generator failure' + ); + } + + /** + * Let's turn $randomByteString into an integer + * + * This uses bitwise operators (<< and |) to build an integer + * out of the values extracted from ord() + * + * Example: [9F] | [6D] | [32] | [0C] => + * 159 + 27904 + 3276800 + 201326592 => + * 204631455 + */ + $val = 0; + for ($i = 0; $i < $bytes; ++$i) { + $val |= ord($randomByteString[$i]) << ($i * 8); + } + + /** + * Apply mask + */ + $val &= $mask; + $val += $valueShift; + + ++$attempts; + /** + * If $val overflows to a floating point number, + * ... or is larger than $max, + * ... or smaller than $min, + * then try again. + */ + } while (!is_int($val) || $val > $max || $val < $min); + + return (int) $val; +} diff --git a/vendor/paragonie/random_compat/other/build_phar.php b/vendor/paragonie/random_compat/other/build_phar.php new file mode 100644 index 0000000000..70ef4b2ed8 --- /dev/null +++ b/vendor/paragonie/random_compat/other/build_phar.php @@ -0,0 +1,57 @@ +buildFromDirectory(dirname(__DIR__).'/lib'); +rename( + dirname(__DIR__).'/lib/index.php', + dirname(__DIR__).'/lib/random.php' +); + +/** + * If we pass an (optional) path to a private key as a second argument, we will + * sign the Phar with OpenSSL. + * + * If you leave this out, it will produce an unsigned .phar! + */ +if ($argc > 1) { + if (!@is_readable($argv[1])) { + echo 'Could not read the private key file:', $argv[1], "\n"; + exit(255); + } + $pkeyFile = file_get_contents($argv[1]); + + $private = openssl_get_privatekey($pkeyFile); + if ($private !== false) { + $pkey = ''; + openssl_pkey_export($private, $pkey); + $phar->setSignatureAlgorithm(Phar::OPENSSL, $pkey); + + /** + * Save the corresponding public key to the file + */ + if (!@is_readable($dist.'/random_compat.phar.pubkey')) { + $details = openssl_pkey_get_details($private); + file_put_contents( + $dist.'/random_compat.phar.pubkey', + $details['key'] + ); + } + } else { + echo 'An error occurred reading the private key from OpenSSL.', "\n"; + exit(255); + } +} diff --git a/vendor/pimple/pimple/.gitignore b/vendor/pimple/pimple/.gitignore new file mode 100644 index 0000000000..c089b09520 --- /dev/null +++ b/vendor/pimple/pimple/.gitignore @@ -0,0 +1,3 @@ +phpunit.xml +composer.lock +/vendor/ diff --git a/vendor/pimple/pimple/.travis.yml b/vendor/pimple/pimple/.travis.yml new file mode 100644 index 0000000000..5f8bb7c9f3 --- /dev/null +++ b/vendor/pimple/pimple/.travis.yml @@ -0,0 +1,32 @@ +language: php + +env: + matrix: + - PIMPLE_EXT=no + - PIMPLE_EXT=yes + global: + - REPORT_EXIT_STATUS=1 + +php: + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - hhvm + +before_script: + - composer self-update + - COMPOSER_ROOT_VERSION=dev-master composer dump-autoload + - if [ "$PIMPLE_EXT" == "yes" ]; then sh -c "cd ext/pimple && phpize && ./configure && make && sudo make install"; fi + - if [ "$PIMPLE_EXT" == "yes" ]; then echo "extension=pimple.so" >> `php --ini | grep "Loaded Configuration" | sed -e "s|.*:\s*||"`; fi + +script: + - cd ext/pimple + - if [ "$PIMPLE_EXT" == "yes" ]; then yes n | make test | tee output ; grep -E 'Tests failed +. +0' output; fi + - cd ../.. + - phpunit + +matrix: + exclude: + - php: hhvm + env: PIMPLE_EXT=yes diff --git a/vendor/pimple/pimple/CHANGELOG b/vendor/pimple/pimple/CHANGELOG new file mode 100644 index 0000000000..cc679972ec --- /dev/null +++ b/vendor/pimple/pimple/CHANGELOG @@ -0,0 +1,35 @@ +* 3.0.2 (2015-09-11) + + * refactored the C extension + * minor non-significant changes + +* 3.0.1 (2015-07-30) + + * simplified some code + * fixed a segfault in the C extension + +* 3.0.0 (2014-07-24) + + * removed the Pimple class alias (use Pimple\Container instead) + +* 2.1.1 (2014-07-24) + + * fixed compiler warnings for the C extension + * fixed code when dealing with circular references + +* 2.1.0 (2014-06-24) + + * moved the Pimple to Pimple\Container (with a BC layer -- Pimple is now a + deprecated alias which will be removed in Pimple 3.0) + * added Pimple\ServiceProviderInterface (and Pimple::register()) + +* 2.0.0 (2014-02-10) + + * changed extend to automatically re-assign the extended service and keep it as shared or factory + (to keep BC, extend still returns the extended service) + * changed services to be shared by default (use factory() for factory + services) + +* 1.0.0 + + * initial version diff --git a/vendor/pimple/pimple/LICENSE b/vendor/pimple/pimple/LICENSE new file mode 100644 index 0000000000..d7949e2fb3 --- /dev/null +++ b/vendor/pimple/pimple/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2009-2015 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/pimple/pimple/README.rst b/vendor/pimple/pimple/README.rst new file mode 100644 index 0000000000..93fb35a89b --- /dev/null +++ b/vendor/pimple/pimple/README.rst @@ -0,0 +1,201 @@ +Pimple +====== + +.. caution:: + + This is the documentation for Pimple 3.x. If you are using Pimple 1.x, read + the `Pimple 1.x documentation`_. Reading the Pimple 1.x code is also a good + way to learn more about how to create a simple Dependency Injection + Container (recent versions of Pimple are more focused on performance). + +Pimple is a small Dependency Injection Container for PHP. + +Installation +------------ + +Before using Pimple in your project, add it to your ``composer.json`` file: + +.. code-block:: bash + + $ ./composer.phar require pimple/pimple ~3.0 + +Alternatively, Pimple is also available as a PHP C extension: + +.. code-block:: bash + + $ git clone https://github.com/silexphp/Pimple + $ cd Pimple/ext/pimple + $ phpize + $ ./configure + $ make + $ make install + +Usage +----- + +Creating a container is a matter of creating a ``Container`` instance: + +.. code-block:: php + + use Pimple\Container; + + $container = new Container(); + +As many other dependency injection containers, Pimple manages two different +kind of data: **services** and **parameters**. + +Defining Services +~~~~~~~~~~~~~~~~~ + +A service is an object that does something as part of a larger system. Examples +of services: a database connection, a templating engine, or a mailer. Almost +any **global** object can be a service. + +Services are defined by **anonymous functions** that return an instance of an +object: + +.. code-block:: php + + // define some services + $container['session_storage'] = function ($c) { + return new SessionStorage('SESSION_ID'); + }; + + $container['session'] = function ($c) { + return new Session($c['session_storage']); + }; + +Notice that the anonymous function has access to the current container +instance, allowing references to other services or parameters. + +As objects are only created when you get them, the order of the definitions +does not matter. + +Using the defined services is also very easy: + +.. code-block:: php + + // get the session object + $session = $container['session']; + + // the above call is roughly equivalent to the following code: + // $storage = new SessionStorage('SESSION_ID'); + // $session = new Session($storage); + +Defining Factory Services +~~~~~~~~~~~~~~~~~~~~~~~~~ + +By default, each time you get a service, Pimple returns the **same instance** +of it. If you want a different instance to be returned for all calls, wrap your +anonymous function with the ``factory()`` method + +.. code-block:: php + + $container['session'] = $container->factory(function ($c) { + return new Session($c['session_storage']); + }); + +Now, each call to ``$container['session']`` returns a new instance of the +session. + +Defining Parameters +~~~~~~~~~~~~~~~~~~~ + +Defining a parameter allows to ease the configuration of your container from +the outside and to store global values: + +.. code-block:: php + + // define some parameters + $container['cookie_name'] = 'SESSION_ID'; + $container['session_storage_class'] = 'SessionStorage'; + +If you change the ``session_storage`` service definition like below: + +.. code-block:: php + + $container['session_storage'] = function ($c) { + return new $c['session_storage_class']($c['cookie_name']); + }; + +You can now easily change the cookie name by overriding the +``session_storage_class`` parameter instead of redefining the service +definition. + +Protecting Parameters +~~~~~~~~~~~~~~~~~~~~~ + +Because Pimple sees anonymous functions as service definitions, you need to +wrap anonymous functions with the ``protect()`` method to store them as +parameters: + +.. code-block:: php + + $container['random_func'] = $container->protect(function () { + return rand(); + }); + +Modifying Services after Definition +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In some cases you may want to modify a service definition after it has been +defined. You can use the ``extend()`` method to define additional code to be +run on your service just after it is created: + +.. code-block:: php + + $container['session_storage'] = function ($c) { + return new $c['session_storage_class']($c['cookie_name']); + }; + + $container->extend('session_storage', function ($storage, $c) { + $storage->...(); + + return $storage; + }); + +The first argument is the name of the service to extend, the second a function +that gets access to the object instance and the container. + +Extending a Container +~~~~~~~~~~~~~~~~~~~~~ + +If you use the same libraries over and over, you might want to reuse some +services from one project to the next one; package your services into a +**provider** by implementing ``Pimple\ServiceProviderInterface``: + +.. code-block:: php + + use Pimple\Container; + + class FooProvider implements Pimple\ServiceProviderInterface + { + public function register(Container $pimple) + { + // register some services and parameters + // on $pimple + } + } + +Then, register the provider on a Container: + +.. code-block:: php + + $pimple->register(new FooProvider()); + +Fetching the Service Creation Function +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When you access an object, Pimple automatically calls the anonymous function +that you defined, which creates the service object for you. If you want to get +raw access to this function, you can use the ``raw()`` method: + +.. code-block:: php + + $container['session'] = function ($c) { + return new Session($c['session_storage']); + }; + + $sessionFunction = $container->raw('session'); + +.. _Pimple 1.x documentation: https://github.com/silexphp/Pimple/tree/1.1 diff --git a/vendor/pimple/pimple/composer.json b/vendor/pimple/pimple/composer.json new file mode 100644 index 0000000000..a5268f1611 --- /dev/null +++ b/vendor/pimple/pimple/composer.json @@ -0,0 +1,25 @@ +{ + "name": "pimple/pimple", + "type": "library", + "description": "Pimple, a simple Dependency Injection Container", + "keywords": ["dependency injection", "container"], + "homepage": "http://pimple.sensiolabs.org", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "require": { + "php": ">=5.3.0" + }, + "autoload": { + "psr-0": { "Pimple": "src/" } + }, + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + } +} diff --git a/vendor/pimple/pimple/ext/pimple/.gitignore b/vendor/pimple/pimple/ext/pimple/.gitignore new file mode 100644 index 0000000000..1861088ac1 --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/.gitignore @@ -0,0 +1,30 @@ +*.sw* +.deps +Makefile +Makefile.fragments +Makefile.global +Makefile.objects +acinclude.m4 +aclocal.m4 +build/ +config.cache +config.guess +config.h +config.h.in +config.log +config.nice +config.status +config.sub +configure +configure.in +install-sh +libtool +ltmain.sh +missing +mkinstalldirs +run-tests.php +*.loT +.libs/ +modules/ +*.la +*.lo diff --git a/vendor/pimple/pimple/ext/pimple/README.md b/vendor/pimple/pimple/ext/pimple/README.md new file mode 100644 index 0000000000..7b39eb2929 --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/README.md @@ -0,0 +1,12 @@ +This is Pimple 2 implemented in C + +* PHP >= 5.3 +* Not tested under Windows, might work + +Install +======= + + > phpize + > ./configure + > make + > make install diff --git a/vendor/pimple/pimple/ext/pimple/config.m4 b/vendor/pimple/pimple/ext/pimple/config.m4 new file mode 100644 index 0000000000..c9ba17ddbd --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/config.m4 @@ -0,0 +1,63 @@ +dnl $Id$ +dnl config.m4 for extension pimple + +dnl Comments in this file start with the string 'dnl'. +dnl Remove where necessary. This file will not work +dnl without editing. + +dnl If your extension references something external, use with: + +dnl PHP_ARG_WITH(pimple, for pimple support, +dnl Make sure that the comment is aligned: +dnl [ --with-pimple Include pimple support]) + +dnl Otherwise use enable: + +PHP_ARG_ENABLE(pimple, whether to enable pimple support, +dnl Make sure that the comment is aligned: +[ --enable-pimple Enable pimple support]) + +if test "$PHP_PIMPLE" != "no"; then + dnl Write more examples of tests here... + + dnl # --with-pimple -> check with-path + dnl SEARCH_PATH="/usr/local /usr" # you might want to change this + dnl SEARCH_FOR="/include/pimple.h" # you most likely want to change this + dnl if test -r $PHP_PIMPLE/$SEARCH_FOR; then # path given as parameter + dnl PIMPLE_DIR=$PHP_PIMPLE + dnl else # search default path list + dnl AC_MSG_CHECKING([for pimple files in default path]) + dnl for i in $SEARCH_PATH ; do + dnl if test -r $i/$SEARCH_FOR; then + dnl PIMPLE_DIR=$i + dnl AC_MSG_RESULT(found in $i) + dnl fi + dnl done + dnl fi + dnl + dnl if test -z "$PIMPLE_DIR"; then + dnl AC_MSG_RESULT([not found]) + dnl AC_MSG_ERROR([Please reinstall the pimple distribution]) + dnl fi + + dnl # --with-pimple -> add include path + dnl PHP_ADD_INCLUDE($PIMPLE_DIR/include) + + dnl # --with-pimple -> check for lib and symbol presence + dnl LIBNAME=pimple # you may want to change this + dnl LIBSYMBOL=pimple # you most likely want to change this + + dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL, + dnl [ + dnl PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $PIMPLE_DIR/lib, PIMPLE_SHARED_LIBADD) + dnl AC_DEFINE(HAVE_PIMPLELIB,1,[ ]) + dnl ],[ + dnl AC_MSG_ERROR([wrong pimple lib version or lib not found]) + dnl ],[ + dnl -L$PIMPLE_DIR/lib -lm + dnl ]) + dnl + dnl PHP_SUBST(PIMPLE_SHARED_LIBADD) + + PHP_NEW_EXTENSION(pimple, pimple.c, $ext_shared) +fi diff --git a/vendor/pimple/pimple/ext/pimple/config.w32 b/vendor/pimple/pimple/ext/pimple/config.w32 new file mode 100644 index 0000000000..39857b3254 --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/config.w32 @@ -0,0 +1,13 @@ +// $Id$ +// vim:ft=javascript + +// If your extension references something external, use ARG_WITH +// ARG_WITH("pimple", "for pimple support", "no"); + +// Otherwise, use ARG_ENABLE +// ARG_ENABLE("pimple", "enable pimple support", "no"); + +if (PHP_PIMPLE != "no") { + EXTENSION("pimple", "pimple.c"); +} + diff --git a/vendor/pimple/pimple/ext/pimple/php_pimple.h b/vendor/pimple/pimple/ext/pimple/php_pimple.h new file mode 100644 index 0000000000..49431f08a8 --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/php_pimple.h @@ -0,0 +1,121 @@ + +/* + * This file is part of Pimple. + * + * Copyright (c) 2014 Fabien Potencier + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef PHP_PIMPLE_H +#define PHP_PIMPLE_H + +extern zend_module_entry pimple_module_entry; +#define phpext_pimple_ptr &pimple_module_entry + +#ifdef PHP_WIN32 +# define PHP_PIMPLE_API __declspec(dllexport) +#elif defined(__GNUC__) && __GNUC__ >= 4 +# define PHP_PIMPLE_API __attribute__ ((visibility("default"))) +#else +# define PHP_PIMPLE_API +#endif + +#ifdef ZTS +#include "TSRM.h" +#endif + +#define PIMPLE_VERSION "3.0.2" +#define PIMPLE_NS "Pimple" + +#define PIMPLE_DEFAULT_ZVAL_CACHE_NUM 5 +#define PIMPLE_DEFAULT_ZVAL_VALUES_NUM 10 + +zend_module_entry *get_module(void); + +PHP_MINIT_FUNCTION(pimple); +PHP_MINFO_FUNCTION(pimple); + +PHP_METHOD(Pimple, __construct); +PHP_METHOD(Pimple, factory); +PHP_METHOD(Pimple, protect); +PHP_METHOD(Pimple, raw); +PHP_METHOD(Pimple, extend); +PHP_METHOD(Pimple, keys); +PHP_METHOD(Pimple, register); +PHP_METHOD(Pimple, offsetSet); +PHP_METHOD(Pimple, offsetUnset); +PHP_METHOD(Pimple, offsetGet); +PHP_METHOD(Pimple, offsetExists); + +PHP_METHOD(PimpleClosure, invoker); + +typedef struct _pimple_bucket_value { + zval *value; /* Must be the first element */ + zval *raw; + zend_object_handle handle_num; + enum { + PIMPLE_IS_PARAM = 0, + PIMPLE_IS_SERVICE = 2 + } type; + zend_bool initialized; + zend_fcall_info_cache fcc; +} pimple_bucket_value; + +typedef struct _pimple_object { + zend_object zobj; + HashTable values; + HashTable factories; + HashTable protected; +} pimple_object; + +typedef struct _pimple_closure_object { + zend_object zobj; + zval *callable; + zval *factory; +} pimple_closure_object; + +static const char sensiolabs_logo[] = ""; + +static int pimple_zval_to_pimpleval(zval *_zval, pimple_bucket_value *_pimple_bucket_value TSRMLS_DC); +static int pimple_zval_is_valid_callback(zval *_zval, pimple_bucket_value *_pimple_bucket_value TSRMLS_DC); + +static void pimple_bucket_dtor(pimple_bucket_value *bucket); +static void pimple_free_bucket(pimple_bucket_value *bucket); + +static zval *pimple_object_read_dimension(zval *object, zval *offset, int type TSRMLS_DC); +static void pimple_object_write_dimension(zval *object, zval *offset, zval *value TSRMLS_DC); +static int pimple_object_has_dimension(zval *object, zval *offset, int check_empty TSRMLS_DC); +static void pimple_object_unset_dimension(zval *object, zval *offset TSRMLS_DC); +static zend_object_value pimple_object_create(zend_class_entry *ce TSRMLS_DC); +static void pimple_free_object_storage(pimple_object *obj TSRMLS_DC); + +static void pimple_closure_free_object_storage(pimple_closure_object *obj TSRMLS_DC); +static zend_object_value pimple_closure_object_create(zend_class_entry *ce TSRMLS_DC); +static zend_function *pimple_closure_get_constructor(zval * TSRMLS_DC); +static int pimple_closure_get_closure(zval *obj, zend_class_entry **ce_ptr, union _zend_function **fptr_ptr, zval **zobj_ptr TSRMLS_DC); + +#ifdef ZTS +#define PIMPLE_G(v) TSRMG(pimple_globals_id, zend_pimple_globals *, v) +#else +#define PIMPLE_G(v) (pimple_globals.v) +#endif + +#endif /* PHP_PIMPLE_H */ + diff --git a/vendor/pimple/pimple/ext/pimple/pimple.c b/vendor/pimple/pimple/ext/pimple/pimple.c new file mode 100644 index 0000000000..239c01d683 --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/pimple.c @@ -0,0 +1,922 @@ + +/* + * This file is part of Pimple. + * + * Copyright (c) 2014 Fabien Potencier + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "php_pimple.h" +#include "pimple_compat.h" +#include "zend_interfaces.h" +#include "zend.h" +#include "Zend/zend_closures.h" +#include "ext/spl/spl_exceptions.h" +#include "Zend/zend_exceptions.h" +#include "main/php_output.h" +#include "SAPI.h" + +static zend_class_entry *pimple_ce; +static zend_object_handlers pimple_object_handlers; +static zend_class_entry *pimple_closure_ce; +static zend_class_entry *pimple_serviceprovider_ce; +static zend_object_handlers pimple_closure_object_handlers; +static zend_internal_function pimple_closure_invoker_function; + +#define FETCH_DIM_HANDLERS_VARS pimple_object *pimple_obj = NULL; \ + ulong index; \ + pimple_obj = (pimple_object *)zend_object_store_get_object(object TSRMLS_CC); \ + +#define PIMPLE_OBJECT_HANDLE_INHERITANCE_OBJECT_HANDLERS do { \ + if (ce != pimple_ce) { \ + zend_hash_find(&ce->function_table, ZEND_STRS("offsetget"), (void **)&function); \ + if (function->common.scope != ce) { /* if the function is not defined in this actual class */ \ + pimple_object_handlers.read_dimension = pimple_object_read_dimension; /* then overwrite the handler to use custom one */ \ + } \ + zend_hash_find(&ce->function_table, ZEND_STRS("offsetset"), (void **)&function); \ + if (function->common.scope != ce) { \ + pimple_object_handlers.write_dimension = pimple_object_write_dimension; \ + } \ + zend_hash_find(&ce->function_table, ZEND_STRS("offsetexists"), (void **)&function); \ + if (function->common.scope != ce) { \ + pimple_object_handlers.has_dimension = pimple_object_has_dimension; \ + } \ + zend_hash_find(&ce->function_table, ZEND_STRS("offsetunset"), (void **)&function); \ + if (function->common.scope != ce) { \ + pimple_object_handlers.unset_dimension = pimple_object_unset_dimension; \ + } \ + } else { \ + pimple_object_handlers.read_dimension = pimple_object_read_dimension; \ + pimple_object_handlers.write_dimension = pimple_object_write_dimension; \ + pimple_object_handlers.has_dimension = pimple_object_has_dimension; \ + pimple_object_handlers.unset_dimension = pimple_object_unset_dimension; \ + }\ + } while(0); + +#define PIMPLE_CALL_CB do { \ + zend_fcall_info_argn(&fci TSRMLS_CC, 1, &object); \ + fci.size = sizeof(fci); \ + fci.object_ptr = retval->fcc.object_ptr; \ + fci.function_name = retval->value; \ + fci.no_separation = 1; \ + fci.retval_ptr_ptr = &retval_ptr_ptr; \ +\ + zend_call_function(&fci, &retval->fcc TSRMLS_CC); \ + efree(fci.params); \ + if (EG(exception)) { \ + return EG(uninitialized_zval_ptr); \ + } \ + } while(0); + +ZEND_BEGIN_ARG_INFO_EX(arginfo___construct, 0, 0, 0) +ZEND_ARG_ARRAY_INFO(0, value, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetset, 0, 0, 2) +ZEND_ARG_INFO(0, offset) +ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetget, 0, 0, 1) +ZEND_ARG_INFO(0, offset) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetexists, 0, 0, 1) +ZEND_ARG_INFO(0, offset) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetunset, 0, 0, 1) +ZEND_ARG_INFO(0, offset) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_factory, 0, 0, 1) +ZEND_ARG_INFO(0, callable) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_protect, 0, 0, 1) +ZEND_ARG_INFO(0, callable) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_raw, 0, 0, 1) +ZEND_ARG_INFO(0, id) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_extend, 0, 0, 2) +ZEND_ARG_INFO(0, id) +ZEND_ARG_INFO(0, callable) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_keys, 0, 0, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_register, 0, 0, 1) +ZEND_ARG_OBJ_INFO(0, provider, Pimple\\ServiceProviderInterface, 0) +ZEND_ARG_ARRAY_INFO(0, values, 1) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_serviceprovider_register, 0, 0, 1) +ZEND_ARG_OBJ_INFO(0, pimple, Pimple\\Container, 0) +ZEND_END_ARG_INFO() + +static const zend_function_entry pimple_ce_functions[] = { + PHP_ME(Pimple, __construct, arginfo___construct, ZEND_ACC_PUBLIC) + PHP_ME(Pimple, factory, arginfo_factory, ZEND_ACC_PUBLIC) + PHP_ME(Pimple, protect, arginfo_protect, ZEND_ACC_PUBLIC) + PHP_ME(Pimple, raw, arginfo_raw, ZEND_ACC_PUBLIC) + PHP_ME(Pimple, extend, arginfo_extend, ZEND_ACC_PUBLIC) + PHP_ME(Pimple, keys, arginfo_keys, ZEND_ACC_PUBLIC) + PHP_ME(Pimple, register, arginfo_register, ZEND_ACC_PUBLIC) + + PHP_ME(Pimple, offsetSet, arginfo_offsetset, ZEND_ACC_PUBLIC) + PHP_ME(Pimple, offsetGet, arginfo_offsetget, ZEND_ACC_PUBLIC) + PHP_ME(Pimple, offsetExists, arginfo_offsetexists, ZEND_ACC_PUBLIC) + PHP_ME(Pimple, offsetUnset, arginfo_offsetunset, ZEND_ACC_PUBLIC) + PHP_FE_END +}; + +static const zend_function_entry pimple_serviceprovider_iface_ce_functions[] = { + PHP_ABSTRACT_ME(ServiceProviderInterface, register, arginfo_serviceprovider_register) + PHP_FE_END +}; + +static void pimple_closure_free_object_storage(pimple_closure_object *obj TSRMLS_DC) +{ + zend_object_std_dtor(&obj->zobj TSRMLS_CC); + if (obj->factory) { + zval_ptr_dtor(&obj->factory); + } + if (obj->callable) { + zval_ptr_dtor(&obj->callable); + } + efree(obj); +} + +static void pimple_free_object_storage(pimple_object *obj TSRMLS_DC) +{ + zend_hash_destroy(&obj->factories); + zend_hash_destroy(&obj->protected); + zend_hash_destroy(&obj->values); + zend_object_std_dtor(&obj->zobj TSRMLS_CC); + efree(obj); +} + +static void pimple_free_bucket(pimple_bucket_value *bucket) +{ + if (bucket->raw) { + zval_ptr_dtor(&bucket->raw); + } +} + +static zend_object_value pimple_closure_object_create(zend_class_entry *ce TSRMLS_DC) +{ + zend_object_value retval; + pimple_closure_object *pimple_closure_obj = NULL; + + pimple_closure_obj = ecalloc(1, sizeof(pimple_closure_object)); + ZEND_OBJ_INIT(&pimple_closure_obj->zobj, ce); + + pimple_closure_object_handlers.get_constructor = pimple_closure_get_constructor; + retval.handlers = &pimple_closure_object_handlers; + retval.handle = zend_objects_store_put(pimple_closure_obj, (zend_objects_store_dtor_t) zend_objects_destroy_object, (zend_objects_free_object_storage_t) pimple_closure_free_object_storage, NULL TSRMLS_CC); + + return retval; +} + +static zend_function *pimple_closure_get_constructor(zval *obj TSRMLS_DC) +{ + zend_error(E_ERROR, "Pimple\\ContainerClosure is an internal class and cannot be instantiated"); + + return NULL; +} + +static int pimple_closure_get_closure(zval *obj, zend_class_entry **ce_ptr, union _zend_function **fptr_ptr, zval **zobj_ptr TSRMLS_DC) +{ + *zobj_ptr = obj; + *ce_ptr = Z_OBJCE_P(obj); + *fptr_ptr = (zend_function *)&pimple_closure_invoker_function; + + return SUCCESS; +} + +static zend_object_value pimple_object_create(zend_class_entry *ce TSRMLS_DC) +{ + zend_object_value retval; + pimple_object *pimple_obj = NULL; + zend_function *function = NULL; + + pimple_obj = emalloc(sizeof(pimple_object)); + ZEND_OBJ_INIT(&pimple_obj->zobj, ce); + + PIMPLE_OBJECT_HANDLE_INHERITANCE_OBJECT_HANDLERS + + retval.handlers = &pimple_object_handlers; + retval.handle = zend_objects_store_put(pimple_obj, (zend_objects_store_dtor_t) zend_objects_destroy_object, (zend_objects_free_object_storage_t) pimple_free_object_storage, NULL TSRMLS_CC); + + zend_hash_init(&pimple_obj->factories, PIMPLE_DEFAULT_ZVAL_CACHE_NUM, NULL, (dtor_func_t)pimple_bucket_dtor, 0); + zend_hash_init(&pimple_obj->protected, PIMPLE_DEFAULT_ZVAL_CACHE_NUM, NULL, (dtor_func_t)pimple_bucket_dtor, 0); + zend_hash_init(&pimple_obj->values, PIMPLE_DEFAULT_ZVAL_VALUES_NUM, NULL, (dtor_func_t)pimple_bucket_dtor, 0); + + return retval; +} + +static void pimple_object_write_dimension(zval *object, zval *offset, zval *value TSRMLS_DC) +{ + FETCH_DIM_HANDLERS_VARS + + pimple_bucket_value pimple_value = {0}, *found_value = NULL; + ulong hash; + + pimple_zval_to_pimpleval(value, &pimple_value TSRMLS_CC); + + if (!offset) {/* $p[] = 'foo' when not overloaded */ + zend_hash_next_index_insert(&pimple_obj->values, (void *)&pimple_value, sizeof(pimple_bucket_value), NULL); + Z_ADDREF_P(value); + return; + } + + switch (Z_TYPE_P(offset)) { + case IS_STRING: + hash = zend_hash_func(Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1); + zend_hash_quick_find(&pimple_obj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, hash, (void **)&found_value); + if (found_value && found_value->type == PIMPLE_IS_SERVICE && found_value->initialized == 1) { + pimple_free_bucket(&pimple_value); + zend_throw_exception_ex(spl_ce_RuntimeException, 0 TSRMLS_CC, "Cannot override frozen service \"%s\".", Z_STRVAL_P(offset)); + return; + } + if (zend_hash_quick_update(&pimple_obj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, hash, (void *)&pimple_value, sizeof(pimple_bucket_value), NULL) == FAILURE) { + pimple_free_bucket(&pimple_value); + return; + } + Z_ADDREF_P(value); + break; + case IS_DOUBLE: + case IS_BOOL: + case IS_LONG: + if (Z_TYPE_P(offset) == IS_DOUBLE) { + index = (ulong)Z_DVAL_P(offset); + } else { + index = Z_LVAL_P(offset); + } + zend_hash_index_find(&pimple_obj->values, index, (void **)&found_value); + if (found_value && found_value->type == PIMPLE_IS_SERVICE && found_value->initialized == 1) { + pimple_free_bucket(&pimple_value); + zend_throw_exception_ex(spl_ce_RuntimeException, 0 TSRMLS_CC, "Cannot override frozen service \"%ld\".", index); + return; + } + if (zend_hash_index_update(&pimple_obj->values, index, (void *)&pimple_value, sizeof(pimple_bucket_value), NULL) == FAILURE) { + pimple_free_bucket(&pimple_value); + return; + } + Z_ADDREF_P(value); + break; + case IS_NULL: /* $p[] = 'foo' when overloaded */ + zend_hash_next_index_insert(&pimple_obj->values, (void *)&pimple_value, sizeof(pimple_bucket_value), NULL); + Z_ADDREF_P(value); + break; + default: + pimple_free_bucket(&pimple_value); + zend_error(E_WARNING, "Unsupported offset type"); + } +} + +static void pimple_object_unset_dimension(zval *object, zval *offset TSRMLS_DC) +{ + FETCH_DIM_HANDLERS_VARS + + switch (Z_TYPE_P(offset)) { + case IS_STRING: + zend_symtable_del(&pimple_obj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1); + zend_symtable_del(&pimple_obj->factories, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1); + zend_symtable_del(&pimple_obj->protected, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1); + break; + case IS_DOUBLE: + case IS_BOOL: + case IS_LONG: + if (Z_TYPE_P(offset) == IS_DOUBLE) { + index = (ulong)Z_DVAL_P(offset); + } else { + index = Z_LVAL_P(offset); + } + zend_hash_index_del(&pimple_obj->values, index); + zend_hash_index_del(&pimple_obj->factories, index); + zend_hash_index_del(&pimple_obj->protected, index); + break; + default: + zend_error(E_WARNING, "Unsupported offset type"); + } +} + +static int pimple_object_has_dimension(zval *object, zval *offset, int check_empty TSRMLS_DC) +{ + FETCH_DIM_HANDLERS_VARS + + pimple_bucket_value *retval = NULL; + + switch (Z_TYPE_P(offset)) { + case IS_STRING: + if (zend_symtable_find(&pimple_obj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, (void **)&retval) == SUCCESS) { + switch (check_empty) { + case 0: /* isset */ + return 1; /* Differs from PHP behavior (Z_TYPE_P(retval->value) != IS_NULL;) */ + case 1: /* empty */ + default: + return zend_is_true(retval->value); + } + } + return 0; + break; + case IS_DOUBLE: + case IS_BOOL: + case IS_LONG: + if (Z_TYPE_P(offset) == IS_DOUBLE) { + index = (ulong)Z_DVAL_P(offset); + } else { + index = Z_LVAL_P(offset); + } + if (zend_hash_index_find(&pimple_obj->values, index, (void **)&retval) == SUCCESS) { + switch (check_empty) { + case 0: /* isset */ + return 1; /* Differs from PHP behavior (Z_TYPE_P(retval->value) != IS_NULL;)*/ + case 1: /* empty */ + default: + return zend_is_true(retval->value); + } + } + return 0; + break; + default: + zend_error(E_WARNING, "Unsupported offset type"); + return 0; + } +} + +static zval *pimple_object_read_dimension(zval *object, zval *offset, int type TSRMLS_DC) +{ + FETCH_DIM_HANDLERS_VARS + + pimple_bucket_value *retval = NULL; + zend_fcall_info fci = {0}; + zval *retval_ptr_ptr = NULL; + + switch (Z_TYPE_P(offset)) { + case IS_STRING: + if (zend_symtable_find(&pimple_obj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, (void **)&retval) == FAILURE) { + zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0 TSRMLS_CC, "Identifier \"%s\" is not defined.", Z_STRVAL_P(offset)); + return EG(uninitialized_zval_ptr); + } + break; + case IS_DOUBLE: + case IS_BOOL: + case IS_LONG: + if (Z_TYPE_P(offset) == IS_DOUBLE) { + index = (ulong)Z_DVAL_P(offset); + } else { + index = Z_LVAL_P(offset); + } + if (zend_hash_index_find(&pimple_obj->values, index, (void **)&retval) == FAILURE) { + return EG(uninitialized_zval_ptr); + } + break; + case IS_NULL: /* $p[][3] = 'foo' first dim access */ + return EG(uninitialized_zval_ptr); + break; + default: + zend_error(E_WARNING, "Unsupported offset type"); + return EG(uninitialized_zval_ptr); + } + + if(retval->type == PIMPLE_IS_PARAM) { + return retval->value; + } + + if (zend_hash_index_exists(&pimple_obj->protected, retval->handle_num)) { + /* Service is protected, return the value every time */ + return retval->value; + } + + if (zend_hash_index_exists(&pimple_obj->factories, retval->handle_num)) { + /* Service is a factory, call it everytime and never cache its result */ + PIMPLE_CALL_CB + Z_DELREF_P(retval_ptr_ptr); /* fetch dim addr will increment refcount */ + return retval_ptr_ptr; + } + + if (retval->initialized == 1) { + /* Service has already been called, return its cached value */ + return retval->value; + } + + ALLOC_INIT_ZVAL(retval->raw); + MAKE_COPY_ZVAL(&retval->value, retval->raw); + + PIMPLE_CALL_CB + + retval->initialized = 1; + zval_ptr_dtor(&retval->value); + retval->value = retval_ptr_ptr; + + return retval->value; +} + +static int pimple_zval_is_valid_callback(zval *_zval, pimple_bucket_value *_pimple_bucket_value TSRMLS_DC) +{ + if (Z_TYPE_P(_zval) != IS_OBJECT) { + return FAILURE; + } + + if (_pimple_bucket_value->fcc.called_scope) { + return SUCCESS; + } + + if (Z_OBJ_HANDLER_P(_zval, get_closure) && Z_OBJ_HANDLER_P(_zval, get_closure)(_zval, &_pimple_bucket_value->fcc.calling_scope, &_pimple_bucket_value->fcc.function_handler, &_pimple_bucket_value->fcc.object_ptr TSRMLS_CC) == SUCCESS) { + _pimple_bucket_value->fcc.called_scope = _pimple_bucket_value->fcc.calling_scope; + return SUCCESS; + } else { + return FAILURE; + } +} + +static int pimple_zval_to_pimpleval(zval *_zval, pimple_bucket_value *_pimple_bucket_value TSRMLS_DC) +{ + _pimple_bucket_value->value = _zval; + + if (Z_TYPE_P(_zval) != IS_OBJECT) { + return PIMPLE_IS_PARAM; + } + + if (pimple_zval_is_valid_callback(_zval, _pimple_bucket_value TSRMLS_CC) == SUCCESS) { + _pimple_bucket_value->type = PIMPLE_IS_SERVICE; + _pimple_bucket_value->handle_num = Z_OBJ_HANDLE_P(_zval); + } + + return PIMPLE_IS_SERVICE; +} + +static void pimple_bucket_dtor(pimple_bucket_value *bucket) +{ + zval_ptr_dtor(&bucket->value); + pimple_free_bucket(bucket); +} + +PHP_METHOD(Pimple, protect) +{ + zval *protected = NULL; + pimple_object *pobj = NULL; + pimple_bucket_value bucket = {0}; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &protected) == FAILURE) { + return; + } + + if (pimple_zval_is_valid_callback(protected, &bucket TSRMLS_CC) == FAILURE) { + pimple_free_bucket(&bucket); + zend_throw_exception(spl_ce_InvalidArgumentException, "Callable is not a Closure or invokable object.", 0 TSRMLS_CC); + return; + } + + pimple_zval_to_pimpleval(protected, &bucket TSRMLS_CC); + pobj = (pimple_object *)zend_object_store_get_object(getThis() TSRMLS_CC); + + if (zend_hash_index_update(&pobj->protected, bucket.handle_num, (void *)&bucket, sizeof(pimple_bucket_value), NULL) == SUCCESS) { + Z_ADDREF_P(protected); + RETURN_ZVAL(protected, 1 , 0); + } else { + pimple_free_bucket(&bucket); + } + RETURN_FALSE; +} + +PHP_METHOD(Pimple, raw) +{ + zval *offset = NULL; + pimple_object *pobj = NULL; + pimple_bucket_value *value = NULL; + ulong index; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &offset) == FAILURE) { + return; + } + + pobj = zend_object_store_get_object(getThis() TSRMLS_CC); + + switch (Z_TYPE_P(offset)) { + case IS_STRING: + if (zend_symtable_find(&pobj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, (void *)&value) == FAILURE) { + zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0 TSRMLS_CC, "Identifier \"%s\" is not defined.", Z_STRVAL_P(offset)); + RETURN_NULL(); + } + break; + case IS_DOUBLE: + case IS_BOOL: + case IS_LONG: + if (Z_TYPE_P(offset) == IS_DOUBLE) { + index = (ulong)Z_DVAL_P(offset); + } else { + index = Z_LVAL_P(offset); + } + if (zend_hash_index_find(&pobj->values, index, (void *)&value) == FAILURE) { + RETURN_NULL(); + } + break; + case IS_NULL: + default: + zend_error(E_WARNING, "Unsupported offset type"); + } + + if (value->raw) { + RETVAL_ZVAL(value->raw, 1, 0); + } else { + RETVAL_ZVAL(value->value, 1, 0); + } +} + +PHP_METHOD(Pimple, extend) +{ + zval *offset = NULL, *callable = NULL, *pimple_closure_obj = NULL; + pimple_bucket_value bucket = {0}, *value = NULL; + pimple_object *pobj = NULL; + pimple_closure_object *pcobj = NULL; + ulong index; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &offset, &callable) == FAILURE) { + return; + } + + pobj = zend_object_store_get_object(getThis() TSRMLS_CC); + + switch (Z_TYPE_P(offset)) { + case IS_STRING: + if (zend_symtable_find(&pobj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, (void *)&value) == FAILURE) { + zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0 TSRMLS_CC, "Identifier \"%s\" is not defined.", Z_STRVAL_P(offset)); + RETURN_NULL(); + } + if (value->type != PIMPLE_IS_SERVICE) { + zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0 TSRMLS_CC, "Identifier \"%s\" does not contain an object definition.", Z_STRVAL_P(offset)); + RETURN_NULL(); + } + break; + case IS_DOUBLE: + case IS_BOOL: + case IS_LONG: + if (Z_TYPE_P(offset) == IS_DOUBLE) { + index = (ulong)Z_DVAL_P(offset); + } else { + index = Z_LVAL_P(offset); + } + if (zend_hash_index_find(&pobj->values, index, (void *)&value) == FAILURE) { + zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0 TSRMLS_CC, "Identifier \"%ld\" is not defined.", index); + RETURN_NULL(); + } + if (value->type != PIMPLE_IS_SERVICE) { + zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0 TSRMLS_CC, "Identifier \"%ld\" does not contain an object definition.", index); + RETURN_NULL(); + } + break; + case IS_NULL: + default: + zend_error(E_WARNING, "Unsupported offset type"); + } + + if (pimple_zval_is_valid_callback(callable, &bucket TSRMLS_CC) == FAILURE) { + pimple_free_bucket(&bucket); + zend_throw_exception(spl_ce_InvalidArgumentException, "Extension service definition is not a Closure or invokable object.", 0 TSRMLS_CC); + RETURN_NULL(); + } + pimple_free_bucket(&bucket); + + ALLOC_INIT_ZVAL(pimple_closure_obj); + object_init_ex(pimple_closure_obj, pimple_closure_ce); + + pcobj = zend_object_store_get_object(pimple_closure_obj TSRMLS_CC); + pcobj->callable = callable; + pcobj->factory = value->value; + Z_ADDREF_P(callable); + Z_ADDREF_P(value->value); + + if (zend_hash_index_exists(&pobj->factories, value->handle_num)) { + pimple_zval_to_pimpleval(pimple_closure_obj, &bucket TSRMLS_CC); + zend_hash_index_del(&pobj->factories, value->handle_num); + zend_hash_index_update(&pobj->factories, bucket.handle_num, (void *)&bucket, sizeof(pimple_bucket_value), NULL); + Z_ADDREF_P(pimple_closure_obj); + } + + pimple_object_write_dimension(getThis(), offset, pimple_closure_obj TSRMLS_CC); + + RETVAL_ZVAL(pimple_closure_obj, 1, 1); +} + +PHP_METHOD(Pimple, keys) +{ + HashPosition pos; + pimple_object *pobj = NULL; + zval **value = NULL; + zval *endval = NULL; + char *str_index = NULL; + int str_len; + ulong num_index; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + pobj = zend_object_store_get_object(getThis() TSRMLS_CC); + array_init_size(return_value, zend_hash_num_elements(&pobj->values)); + + zend_hash_internal_pointer_reset_ex(&pobj->values, &pos); + + while(zend_hash_get_current_data_ex(&pobj->values, (void **)&value, &pos) == SUCCESS) { + MAKE_STD_ZVAL(endval); + switch (zend_hash_get_current_key_ex(&pobj->values, &str_index, (uint *)&str_len, &num_index, 0, &pos)) { + case HASH_KEY_IS_STRING: + ZVAL_STRINGL(endval, str_index, str_len - 1, 1); + zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &endval, sizeof(zval *), NULL); + break; + case HASH_KEY_IS_LONG: + ZVAL_LONG(endval, num_index); + zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &endval, sizeof(zval *), NULL); + break; + } + zend_hash_move_forward_ex(&pobj->values, &pos); + } +} + +PHP_METHOD(Pimple, factory) +{ + zval *factory = NULL; + pimple_object *pobj = NULL; + pimple_bucket_value bucket = {0}; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &factory) == FAILURE) { + return; + } + + if (pimple_zval_is_valid_callback(factory, &bucket TSRMLS_CC) == FAILURE) { + pimple_free_bucket(&bucket); + zend_throw_exception(spl_ce_InvalidArgumentException, "Service definition is not a Closure or invokable object.", 0 TSRMLS_CC); + return; + } + + pimple_zval_to_pimpleval(factory, &bucket TSRMLS_CC); + pobj = (pimple_object *)zend_object_store_get_object(getThis() TSRMLS_CC); + + if (zend_hash_index_update(&pobj->factories, bucket.handle_num, (void *)&bucket, sizeof(pimple_bucket_value), NULL) == SUCCESS) { + Z_ADDREF_P(factory); + RETURN_ZVAL(factory, 1 , 0); + } else { + pimple_free_bucket(&bucket); + } + + RETURN_FALSE; +} + +PHP_METHOD(Pimple, offsetSet) +{ + zval *offset = NULL, *value = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &offset, &value) == FAILURE) { + return; + } + + pimple_object_write_dimension(getThis(), offset, value TSRMLS_CC); +} + +PHP_METHOD(Pimple, offsetGet) +{ + zval *offset = NULL, *retval = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &offset) == FAILURE) { + return; + } + + retval = pimple_object_read_dimension(getThis(), offset, 0 TSRMLS_CC); + + RETVAL_ZVAL(retval, 1, 0); +} + +PHP_METHOD(Pimple, offsetUnset) +{ + zval *offset = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &offset) == FAILURE) { + return; + } + + pimple_object_unset_dimension(getThis(), offset TSRMLS_CC); +} + +PHP_METHOD(Pimple, offsetExists) +{ + zval *offset = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &offset) == FAILURE) { + return; + } + + RETVAL_BOOL(pimple_object_has_dimension(getThis(), offset, 1 TSRMLS_CC)); +} + +PHP_METHOD(Pimple, register) +{ + zval *provider; + zval **data; + zval *retval = NULL; + zval key; + + HashTable *array = NULL; + HashPosition pos; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|h", &provider, pimple_serviceprovider_ce, &array) == FAILURE) { + return; + } + + RETVAL_ZVAL(getThis(), 1, 0); + + zend_call_method_with_1_params(&provider, Z_OBJCE_P(provider), NULL, "register", &retval, getThis()); + + if (retval) { + zval_ptr_dtor(&retval); + } + + if (!array) { + return; + } + + zend_hash_internal_pointer_reset_ex(array, &pos); + + while(zend_hash_get_current_data_ex(array, (void **)&data, &pos) == SUCCESS) { + zend_hash_get_current_key_zval_ex(array, &key, &pos); + pimple_object_write_dimension(getThis(), &key, *data TSRMLS_CC); + zend_hash_move_forward_ex(array, &pos); + } +} + +PHP_METHOD(Pimple, __construct) +{ + zval *values = NULL, **pData = NULL, offset; + HashPosition pos; + char *str_index = NULL; + zend_uint str_length; + ulong num_index; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!", &values) == FAILURE || !values) { + return; + } + + zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(values), &pos); + while (zend_hash_has_more_elements_ex(Z_ARRVAL_P(values), &pos) == SUCCESS) { + zend_hash_get_current_data_ex(Z_ARRVAL_P(values), (void **)&pData, &pos); + zend_hash_get_current_key_ex(Z_ARRVAL_P(values), &str_index, &str_length, &num_index, 0, &pos); + INIT_ZVAL(offset); + if (zend_hash_get_current_key_type_ex(Z_ARRVAL_P(values), &pos) == HASH_KEY_IS_LONG) { + ZVAL_LONG(&offset, num_index); + } else { + ZVAL_STRINGL(&offset, str_index, (str_length - 1), 0); + } + pimple_object_write_dimension(getThis(), &offset, *pData TSRMLS_CC); + zend_hash_move_forward_ex(Z_ARRVAL_P(values), &pos); + } +} + +/* + * This is PHP code snippet handling extend()s calls : + + $extended = function ($c) use ($callable, $factory) { + return $callable($factory($c), $c); + }; + + */ +PHP_METHOD(PimpleClosure, invoker) +{ + pimple_closure_object *pcobj = NULL; + zval *arg = NULL, *retval = NULL, *newretval = NULL; + zend_fcall_info fci = {0}; + zval **args[2]; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &arg) == FAILURE) { + return; + } + + pcobj = zend_object_store_get_object(getThis() TSRMLS_CC); + + fci.function_name = pcobj->factory; + args[0] = &arg; + zend_fcall_info_argp(&fci TSRMLS_CC, 1, args); + fci.retval_ptr_ptr = &retval; + fci.size = sizeof(fci); + + if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE || EG(exception)) { + efree(fci.params); + return; /* Should here return default zval */ + } + + efree(fci.params); + memset(&fci, 0, sizeof(fci)); + fci.size = sizeof(fci); + + fci.function_name = pcobj->callable; + args[0] = &retval; + args[1] = &arg; + zend_fcall_info_argp(&fci TSRMLS_CC, 2, args); + fci.retval_ptr_ptr = &newretval; + + if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE || EG(exception)) { + efree(fci.params); + zval_ptr_dtor(&retval); + return; + } + + efree(fci.params); + zval_ptr_dtor(&retval); + + RETVAL_ZVAL(newretval, 1 ,1); +} + +PHP_MINIT_FUNCTION(pimple) +{ + zend_class_entry tmp_pimple_ce, tmp_pimple_closure_ce, tmp_pimple_serviceprovider_iface_ce; + INIT_NS_CLASS_ENTRY(tmp_pimple_ce, PIMPLE_NS, "Container", pimple_ce_functions); + INIT_NS_CLASS_ENTRY(tmp_pimple_closure_ce, PIMPLE_NS, "ContainerClosure", NULL); + INIT_NS_CLASS_ENTRY(tmp_pimple_serviceprovider_iface_ce, PIMPLE_NS, "ServiceProviderInterface", pimple_serviceprovider_iface_ce_functions); + + tmp_pimple_ce.create_object = pimple_object_create; + tmp_pimple_closure_ce.create_object = pimple_closure_object_create; + + pimple_ce = zend_register_internal_class(&tmp_pimple_ce TSRMLS_CC); + zend_class_implements(pimple_ce TSRMLS_CC, 1, zend_ce_arrayaccess); + + pimple_closure_ce = zend_register_internal_class(&tmp_pimple_closure_ce TSRMLS_CC); + pimple_closure_ce->ce_flags |= ZEND_ACC_FINAL_CLASS; + + pimple_serviceprovider_ce = zend_register_internal_interface(&tmp_pimple_serviceprovider_iface_ce TSRMLS_CC); + + memcpy(&pimple_closure_object_handlers, zend_get_std_object_handlers(), sizeof(*zend_get_std_object_handlers())); + pimple_object_handlers = std_object_handlers; + pimple_closure_object_handlers.get_closure = pimple_closure_get_closure; + + pimple_closure_invoker_function.function_name = "Pimple closure internal invoker"; + pimple_closure_invoker_function.fn_flags |= ZEND_ACC_CLOSURE; + pimple_closure_invoker_function.handler = ZEND_MN(PimpleClosure_invoker); + pimple_closure_invoker_function.num_args = 1; + pimple_closure_invoker_function.required_num_args = 1; + pimple_closure_invoker_function.scope = pimple_closure_ce; + pimple_closure_invoker_function.type = ZEND_INTERNAL_FUNCTION; + pimple_closure_invoker_function.module = &pimple_module_entry; + + return SUCCESS; +} + +PHP_MINFO_FUNCTION(pimple) +{ + php_info_print_table_start(); + php_info_print_table_header(2, "SensioLabs Pimple C support", "enabled"); + php_info_print_table_row(2, "Pimple supported version", PIMPLE_VERSION); + php_info_print_table_end(); + + php_info_print_box_start(0); + php_write((void *)ZEND_STRL("SensioLabs Pimple C support developed by Julien Pauli") TSRMLS_CC); + if (!sapi_module.phpinfo_as_text) { + php_write((void *)ZEND_STRL(sensiolabs_logo) TSRMLS_CC); + } + php_info_print_box_end(); +} + +zend_module_entry pimple_module_entry = { + STANDARD_MODULE_HEADER, + "pimple", + NULL, + PHP_MINIT(pimple), + NULL, + NULL, + NULL, + PHP_MINFO(pimple), + PIMPLE_VERSION, + STANDARD_MODULE_PROPERTIES +}; + +#ifdef COMPILE_DL_PIMPLE +ZEND_GET_MODULE(pimple) +#endif diff --git a/vendor/pimple/pimple/ext/pimple/pimple_compat.h b/vendor/pimple/pimple/ext/pimple/pimple_compat.h new file mode 100644 index 0000000000..d234e174d0 --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/pimple_compat.h @@ -0,0 +1,81 @@ + +/* + * This file is part of Pimple. + * + * Copyright (c) 2014 Fabien Potencier + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef PIMPLE_COMPAT_H_ +#define PIMPLE_COMPAT_H_ + +#include "Zend/zend_extensions.h" /* for ZEND_EXTENSION_API_NO */ + +#define PHP_5_0_X_API_NO 220040412 +#define PHP_5_1_X_API_NO 220051025 +#define PHP_5_2_X_API_NO 220060519 +#define PHP_5_3_X_API_NO 220090626 +#define PHP_5_4_X_API_NO 220100525 +#define PHP_5_5_X_API_NO 220121212 +#define PHP_5_6_X_API_NO 220131226 + +#define IS_PHP_56 ZEND_EXTENSION_API_NO == PHP_5_6_X_API_NO +#define IS_AT_LEAST_PHP_56 ZEND_EXTENSION_API_NO >= PHP_5_6_X_API_NO + +#define IS_PHP_55 ZEND_EXTENSION_API_NO == PHP_5_5_X_API_NO +#define IS_AT_LEAST_PHP_55 ZEND_EXTENSION_API_NO >= PHP_5_5_X_API_NO + +#define IS_PHP_54 ZEND_EXTENSION_API_NO == PHP_5_4_X_API_NO +#define IS_AT_LEAST_PHP_54 ZEND_EXTENSION_API_NO >= PHP_5_4_X_API_NO + +#define IS_PHP_53 ZEND_EXTENSION_API_NO == PHP_5_3_X_API_NO +#define IS_AT_LEAST_PHP_53 ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + +#if IS_PHP_53 +#define object_properties_init(obj, ce) do { \ + zend_hash_copy(obj->properties, &ce->default_properties, zval_copy_property_ctor(ce), NULL, sizeof(zval *)); \ + } while (0); +#endif + +#define ZEND_OBJ_INIT(obj, ce) do { \ + zend_object_std_init(obj, ce TSRMLS_CC); \ + object_properties_init((obj), (ce)); \ + } while(0); + +#if IS_PHP_53 || IS_PHP_54 +static void zend_hash_get_current_key_zval_ex(const HashTable *ht, zval *key, HashPosition *pos) { + Bucket *p; + + p = pos ? (*pos) : ht->pInternalPointer; + + if (!p) { + Z_TYPE_P(key) = IS_NULL; + } else if (p->nKeyLength) { + Z_TYPE_P(key) = IS_STRING; + Z_STRVAL_P(key) = estrndup(p->arKey, p->nKeyLength - 1); + Z_STRLEN_P(key) = p->nKeyLength - 1; + } else { + Z_TYPE_P(key) = IS_LONG; + Z_LVAL_P(key) = p->h; + } +} +#endif + +#endif /* PIMPLE_COMPAT_H_ */ diff --git a/vendor/pimple/pimple/ext/pimple/tests/001.phpt b/vendor/pimple/pimple/ext/pimple/tests/001.phpt new file mode 100644 index 0000000000..0809ea232b --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/001.phpt @@ -0,0 +1,45 @@ +--TEST-- +Test for read_dim/write_dim handlers +--SKIPIF-- + +--FILE-- + + +--EXPECTF-- +foo +42 +foo2 +foo99 +baz +strstr \ No newline at end of file diff --git a/vendor/pimple/pimple/ext/pimple/tests/002.phpt b/vendor/pimple/pimple/ext/pimple/tests/002.phpt new file mode 100644 index 0000000000..7b56d2c1fe --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/002.phpt @@ -0,0 +1,15 @@ +--TEST-- +Test for constructor +--SKIPIF-- + +--FILE-- +'foo')); +var_dump($p[42]); +?> +--EXPECT-- +NULL +string(3) "foo" diff --git a/vendor/pimple/pimple/ext/pimple/tests/003.phpt b/vendor/pimple/pimple/ext/pimple/tests/003.phpt new file mode 100644 index 0000000000..a22cfa352e --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/003.phpt @@ -0,0 +1,16 @@ +--TEST-- +Test empty dimensions +--SKIPIF-- + +--FILE-- + +--EXPECT-- +int(42) +string(3) "bar" \ No newline at end of file diff --git a/vendor/pimple/pimple/ext/pimple/tests/004.phpt b/vendor/pimple/pimple/ext/pimple/tests/004.phpt new file mode 100644 index 0000000000..1e1d251367 --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/004.phpt @@ -0,0 +1,30 @@ +--TEST-- +Test has/unset dim handlers +--SKIPIF-- + +--FILE-- + +--EXPECT-- +int(42) +NULL +bool(true) +bool(false) +bool(true) +bool(true) \ No newline at end of file diff --git a/vendor/pimple/pimple/ext/pimple/tests/005.phpt b/vendor/pimple/pimple/ext/pimple/tests/005.phpt new file mode 100644 index 0000000000..0479ee055d --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/005.phpt @@ -0,0 +1,27 @@ +--TEST-- +Test simple class inheritance +--SKIPIF-- + +--FILE-- +someAttr; +?> +--EXPECT-- +string(3) "hit" +foo +fooAttr \ No newline at end of file diff --git a/vendor/pimple/pimple/ext/pimple/tests/006.phpt b/vendor/pimple/pimple/ext/pimple/tests/006.phpt new file mode 100644 index 0000000000..cfe8a119e6 --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/006.phpt @@ -0,0 +1,51 @@ +--TEST-- +Test complex class inheritance +--SKIPIF-- + +--FILE-- + 'bar', 88 => 'baz'); + +$p = new TestPimple($defaultValues); +$p[42] = 'foo'; +var_dump($p[42]); +var_dump($p[0]); +?> +--EXPECT-- +string(13) "hit offsetset" +string(27) "hit offsetget in TestPimple" +string(25) "hit offsetget in MyPimple" +string(3) "foo" +string(27) "hit offsetget in TestPimple" +string(25) "hit offsetget in MyPimple" +string(3) "baz" \ No newline at end of file diff --git a/vendor/pimple/pimple/ext/pimple/tests/007.phpt b/vendor/pimple/pimple/ext/pimple/tests/007.phpt new file mode 100644 index 0000000000..5aac683806 --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/007.phpt @@ -0,0 +1,22 @@ +--TEST-- +Test for read_dim/write_dim handlers +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +foo +42 \ No newline at end of file diff --git a/vendor/pimple/pimple/ext/pimple/tests/008.phpt b/vendor/pimple/pimple/ext/pimple/tests/008.phpt new file mode 100644 index 0000000000..db7eeec4a1 --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/008.phpt @@ -0,0 +1,29 @@ +--TEST-- +Test frozen services +--SKIPIF-- + +--FILE-- + +--EXPECTF-- diff --git a/vendor/pimple/pimple/ext/pimple/tests/009.phpt b/vendor/pimple/pimple/ext/pimple/tests/009.phpt new file mode 100644 index 0000000000..bb05ea2964 --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/009.phpt @@ -0,0 +1,13 @@ +--TEST-- +Test service is called as callback, and only once +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +bool(true) \ No newline at end of file diff --git a/vendor/pimple/pimple/ext/pimple/tests/010.phpt b/vendor/pimple/pimple/ext/pimple/tests/010.phpt new file mode 100644 index 0000000000..badce0146a --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/010.phpt @@ -0,0 +1,45 @@ +--TEST-- +Test service is called as callback for every callback type +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +callme +called +Foo::bar +array(2) { + [0]=> + string(3) "Foo" + [1]=> + string(3) "bar" +} \ No newline at end of file diff --git a/vendor/pimple/pimple/ext/pimple/tests/011.phpt b/vendor/pimple/pimple/ext/pimple/tests/011.phpt new file mode 100644 index 0000000000..6682ab8ebd --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/011.phpt @@ -0,0 +1,19 @@ +--TEST-- +Test service callback throwing an exception +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +all right! \ No newline at end of file diff --git a/vendor/pimple/pimple/ext/pimple/tests/012.phpt b/vendor/pimple/pimple/ext/pimple/tests/012.phpt new file mode 100644 index 0000000000..4c6ac486dc --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/012.phpt @@ -0,0 +1,28 @@ +--TEST-- +Test service factory +--SKIPIF-- + +--FILE-- +factory($f = function() { var_dump('called-1'); return 'ret-1';}); + +$p[] = $f; + +$p[] = function () { var_dump('called-2'); return 'ret-2'; }; + +var_dump($p[0]); +var_dump($p[0]); +var_dump($p[1]); +var_dump($p[1]); +?> +--EXPECTF-- +string(8) "called-1" +string(5) "ret-1" +string(8) "called-1" +string(5) "ret-1" +string(8) "called-2" +string(5) "ret-2" +string(5) "ret-2" \ No newline at end of file diff --git a/vendor/pimple/pimple/ext/pimple/tests/013.phpt b/vendor/pimple/pimple/ext/pimple/tests/013.phpt new file mode 100644 index 0000000000..f419958c5f --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/013.phpt @@ -0,0 +1,33 @@ +--TEST-- +Test keys() +--SKIPIF-- + +--FILE-- +keys()); + +$p['foo'] = 'bar'; +$p[] = 'foo'; + +var_dump($p->keys()); + +unset($p['foo']); + +var_dump($p->keys()); +?> +--EXPECTF-- +array(0) { +} +array(2) { + [0]=> + string(3) "foo" + [1]=> + int(0) +} +array(1) { + [0]=> + int(0) +} \ No newline at end of file diff --git a/vendor/pimple/pimple/ext/pimple/tests/014.phpt b/vendor/pimple/pimple/ext/pimple/tests/014.phpt new file mode 100644 index 0000000000..ac937213ac --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/014.phpt @@ -0,0 +1,30 @@ +--TEST-- +Test raw() +--SKIPIF-- + +--FILE-- +raw('foo')); +var_dump($p[42]); + +unset($p['foo']); + +try { + $p->raw('foo'); + echo "expected exception"; +} catch (InvalidArgumentException $e) { } +--EXPECTF-- +string(8) "called-2" +string(5) "ret-2" +object(Closure)#%i (0) { +} +string(8) "called-2" +string(5) "ret-2" \ No newline at end of file diff --git a/vendor/pimple/pimple/ext/pimple/tests/015.phpt b/vendor/pimple/pimple/ext/pimple/tests/015.phpt new file mode 100644 index 0000000000..314f008ac1 --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/015.phpt @@ -0,0 +1,17 @@ +--TEST-- +Test protect() +--SKIPIF-- + +--FILE-- +protect($f); + +var_dump($p['foo']); +--EXPECTF-- +object(Closure)#%i (0) { +} \ No newline at end of file diff --git a/vendor/pimple/pimple/ext/pimple/tests/016.phpt b/vendor/pimple/pimple/ext/pimple/tests/016.phpt new file mode 100644 index 0000000000..e55edb0a7a --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/016.phpt @@ -0,0 +1,24 @@ +--TEST-- +Test extend() +--SKIPIF-- + +--FILE-- +extend(12, function ($w) { var_dump($w); return 'bar'; }); /* $callable in code above */ + +var_dump($c('param')); +--EXPECTF-- +string(5) "param" +string(3) "foo" +string(3) "bar" \ No newline at end of file diff --git a/vendor/pimple/pimple/ext/pimple/tests/017.phpt b/vendor/pimple/pimple/ext/pimple/tests/017.phpt new file mode 100644 index 0000000000..bac23ce09a --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/017.phpt @@ -0,0 +1,17 @@ +--TEST-- +Test extend() with exception in service extension +--SKIPIF-- + +--FILE-- +extend(12, function ($w) { throw new BadMethodCallException; }); + +try { + $p[12]; + echo "Exception expected"; +} catch (BadMethodCallException $e) { } +--EXPECTF-- diff --git a/vendor/pimple/pimple/ext/pimple/tests/017_1.phpt b/vendor/pimple/pimple/ext/pimple/tests/017_1.phpt new file mode 100644 index 0000000000..8f881d6ebf --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/017_1.phpt @@ -0,0 +1,17 @@ +--TEST-- +Test extend() with exception in service factory +--SKIPIF-- + +--FILE-- +extend(12, function ($w) { return 'foobar'; }); + +try { + $p[12]; + echo "Exception expected"; +} catch (BadMethodCallException $e) { } +--EXPECTF-- diff --git a/vendor/pimple/pimple/ext/pimple/tests/018.phpt b/vendor/pimple/pimple/ext/pimple/tests/018.phpt new file mode 100644 index 0000000000..27c12a14e7 --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/018.phpt @@ -0,0 +1,23 @@ +--TEST-- +Test register() +--SKIPIF-- + +--FILE-- +register(new Foo, array(42 => 'bar')); + +var_dump($p[42]); +--EXPECTF-- +object(Pimple\Container)#1 (0) { +} +string(3) "bar" \ No newline at end of file diff --git a/vendor/pimple/pimple/ext/pimple/tests/019.phpt b/vendor/pimple/pimple/ext/pimple/tests/019.phpt new file mode 100644 index 0000000000..28a9aecac7 --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/019.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test register() returns static and is a fluent interface +--SKIPIF-- + +--FILE-- +register(new Foo)); +--EXPECTF-- +bool(true) diff --git a/vendor/pimple/pimple/ext/pimple/tests/bench.phpb b/vendor/pimple/pimple/ext/pimple/tests/bench.phpb new file mode 100644 index 0000000000..8f983e656b --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/bench.phpb @@ -0,0 +1,51 @@ +factory($factory); + +$p['factory'] = $factory; + +echo $p['factory']; +echo $p['factory']; +echo $p['factory']; + +} + +echo microtime(true) - $time; diff --git a/vendor/pimple/pimple/ext/pimple/tests/bench_shared.phpb b/vendor/pimple/pimple/ext/pimple/tests/bench_shared.phpb new file mode 100644 index 0000000000..aec541f0bc --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/bench_shared.phpb @@ -0,0 +1,25 @@ + diff --git a/vendor/pimple/pimple/phpunit.xml.dist b/vendor/pimple/pimple/phpunit.xml.dist new file mode 100644 index 0000000000..5c8d487fea --- /dev/null +++ b/vendor/pimple/pimple/phpunit.xml.dist @@ -0,0 +1,14 @@ + + + + + + ./src/Pimple/Tests + + + diff --git a/vendor/pimple/pimple/src/Pimple/Container.php b/vendor/pimple/pimple/src/Pimple/Container.php new file mode 100644 index 0000000000..c976431e99 --- /dev/null +++ b/vendor/pimple/pimple/src/Pimple/Container.php @@ -0,0 +1,282 @@ +factories = new \SplObjectStorage(); + $this->protected = new \SplObjectStorage(); + + foreach ($values as $key => $value) { + $this->offsetSet($key, $value); + } + } + + /** + * Sets a parameter or an object. + * + * Objects must be defined as Closures. + * + * Allowing any PHP callable leads to difficult to debug problems + * as function names (strings) are callable (creating a function with + * the same name as an existing parameter would break your container). + * + * @param string $id The unique identifier for the parameter or object + * @param mixed $value The value of the parameter or a closure to define an object + * + * @throws \RuntimeException Prevent override of a frozen service + */ + public function offsetSet($id, $value) + { + if (isset($this->frozen[$id])) { + throw new \RuntimeException(sprintf('Cannot override frozen service "%s".', $id)); + } + + $this->values[$id] = $value; + $this->keys[$id] = true; + } + + /** + * Gets a parameter or an object. + * + * @param string $id The unique identifier for the parameter or object + * + * @return mixed The value of the parameter or an object + * + * @throws \InvalidArgumentException if the identifier is not defined + */ + public function offsetGet($id) + { + if (!isset($this->keys[$id])) { + throw new \InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id)); + } + + if ( + isset($this->raw[$id]) + || !is_object($this->values[$id]) + || isset($this->protected[$this->values[$id]]) + || !method_exists($this->values[$id], '__invoke') + ) { + return $this->values[$id]; + } + + if (isset($this->factories[$this->values[$id]])) { + return $this->values[$id]($this); + } + + $raw = $this->values[$id]; + $val = $this->values[$id] = $raw($this); + $this->raw[$id] = $raw; + + $this->frozen[$id] = true; + + return $val; + } + + /** + * Checks if a parameter or an object is set. + * + * @param string $id The unique identifier for the parameter or object + * + * @return bool + */ + public function offsetExists($id) + { + return isset($this->keys[$id]); + } + + /** + * Unsets a parameter or an object. + * + * @param string $id The unique identifier for the parameter or object + */ + public function offsetUnset($id) + { + if (isset($this->keys[$id])) { + if (is_object($this->values[$id])) { + unset($this->factories[$this->values[$id]], $this->protected[$this->values[$id]]); + } + + unset($this->values[$id], $this->frozen[$id], $this->raw[$id], $this->keys[$id]); + } + } + + /** + * Marks a callable as being a factory service. + * + * @param callable $callable A service definition to be used as a factory + * + * @return callable The passed callable + * + * @throws \InvalidArgumentException Service definition has to be a closure of an invokable object + */ + public function factory($callable) + { + if (!method_exists($callable, '__invoke')) { + throw new \InvalidArgumentException('Service definition is not a Closure or invokable object.'); + } + + $this->factories->attach($callable); + + return $callable; + } + + /** + * Protects a callable from being interpreted as a service. + * + * This is useful when you want to store a callable as a parameter. + * + * @param callable $callable A callable to protect from being evaluated + * + * @return callable The passed callable + * + * @throws \InvalidArgumentException Service definition has to be a closure of an invokable object + */ + public function protect($callable) + { + if (!method_exists($callable, '__invoke')) { + throw new \InvalidArgumentException('Callable is not a Closure or invokable object.'); + } + + $this->protected->attach($callable); + + return $callable; + } + + /** + * Gets a parameter or the closure defining an object. + * + * @param string $id The unique identifier for the parameter or object + * + * @return mixed The value of the parameter or the closure defining an object + * + * @throws \InvalidArgumentException if the identifier is not defined + */ + public function raw($id) + { + if (!isset($this->keys[$id])) { + throw new \InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id)); + } + + if (isset($this->raw[$id])) { + return $this->raw[$id]; + } + + return $this->values[$id]; + } + + /** + * Extends an object definition. + * + * Useful when you want to extend an existing object definition, + * without necessarily loading that object. + * + * @param string $id The unique identifier for the object + * @param callable $callable A service definition to extend the original + * + * @return callable The wrapped callable + * + * @throws \InvalidArgumentException if the identifier is not defined or not a service definition + */ + public function extend($id, $callable) + { + if (!isset($this->keys[$id])) { + throw new \InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id)); + } + + if (!is_object($this->values[$id]) || !method_exists($this->values[$id], '__invoke')) { + throw new \InvalidArgumentException(sprintf('Identifier "%s" does not contain an object definition.', $id)); + } + + if (!is_object($callable) || !method_exists($callable, '__invoke')) { + throw new \InvalidArgumentException('Extension service definition is not a Closure or invokable object.'); + } + + $factory = $this->values[$id]; + + $extended = function ($c) use ($callable, $factory) { + return $callable($factory($c), $c); + }; + + if (isset($this->factories[$factory])) { + $this->factories->detach($factory); + $this->factories->attach($extended); + } + + return $this[$id] = $extended; + } + + /** + * Returns all defined value names. + * + * @return array An array of value names + */ + public function keys() + { + return array_keys($this->values); + } + + /** + * Registers a service provider. + * + * @param ServiceProviderInterface $provider A ServiceProviderInterface instance + * @param array $values An array of values that customizes the provider + * + * @return static + */ + public function register(ServiceProviderInterface $provider, array $values = array()) + { + $provider->register($this); + + foreach ($values as $key => $value) { + $this[$key] = $value; + } + + return $this; + } +} diff --git a/vendor/pimple/pimple/src/Pimple/ServiceProviderInterface.php b/vendor/pimple/pimple/src/Pimple/ServiceProviderInterface.php new file mode 100644 index 0000000000..c004594baf --- /dev/null +++ b/vendor/pimple/pimple/src/Pimple/ServiceProviderInterface.php @@ -0,0 +1,46 @@ +value = $value; + + return $service; + } +} diff --git a/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/NonInvokable.php b/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/NonInvokable.php new file mode 100644 index 0000000000..33cd4e5486 --- /dev/null +++ b/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/NonInvokable.php @@ -0,0 +1,34 @@ +factory(function () { + return new Service(); + }); + } +} diff --git a/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/Service.php b/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/Service.php new file mode 100644 index 0000000000..d71b184ddf --- /dev/null +++ b/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/Service.php @@ -0,0 +1,35 @@ + + */ +class Service +{ + public $value; +} diff --git a/vendor/pimple/pimple/src/Pimple/Tests/PimpleServiceProviderInterfaceTest.php b/vendor/pimple/pimple/src/Pimple/Tests/PimpleServiceProviderInterfaceTest.php new file mode 100644 index 0000000000..8e5c4c73de --- /dev/null +++ b/vendor/pimple/pimple/src/Pimple/Tests/PimpleServiceProviderInterfaceTest.php @@ -0,0 +1,76 @@ + + */ +class PimpleServiceProviderInterfaceTest extends \PHPUnit_Framework_TestCase +{ + public function testProvider() + { + $pimple = new Container(); + + $pimpleServiceProvider = new Fixtures\PimpleServiceProvider(); + $pimpleServiceProvider->register($pimple); + + $this->assertEquals('value', $pimple['param']); + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $pimple['service']); + + $serviceOne = $pimple['factory']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceOne); + + $serviceTwo = $pimple['factory']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceTwo); + + $this->assertNotSame($serviceOne, $serviceTwo); + } + + public function testProviderWithRegisterMethod() + { + $pimple = new Container(); + + $pimple->register(new Fixtures\PimpleServiceProvider(), array( + 'anotherParameter' => 'anotherValue', + )); + + $this->assertEquals('value', $pimple['param']); + $this->assertEquals('anotherValue', $pimple['anotherParameter']); + + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $pimple['service']); + + $serviceOne = $pimple['factory']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceOne); + + $serviceTwo = $pimple['factory']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceTwo); + + $this->assertNotSame($serviceOne, $serviceTwo); + } +} diff --git a/vendor/pimple/pimple/src/Pimple/Tests/PimpleTest.php b/vendor/pimple/pimple/src/Pimple/Tests/PimpleTest.php new file mode 100644 index 0000000000..918f620d88 --- /dev/null +++ b/vendor/pimple/pimple/src/Pimple/Tests/PimpleTest.php @@ -0,0 +1,440 @@ + + */ +class PimpleTest extends \PHPUnit_Framework_TestCase +{ + public function testWithString() + { + $pimple = new Container(); + $pimple['param'] = 'value'; + + $this->assertEquals('value', $pimple['param']); + } + + public function testWithClosure() + { + $pimple = new Container(); + $pimple['service'] = function () { + return new Fixtures\Service(); + }; + + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $pimple['service']); + } + + public function testServicesShouldBeDifferent() + { + $pimple = new Container(); + $pimple['service'] = $pimple->factory(function () { + return new Fixtures\Service(); + }); + + $serviceOne = $pimple['service']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceOne); + + $serviceTwo = $pimple['service']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceTwo); + + $this->assertNotSame($serviceOne, $serviceTwo); + } + + public function testShouldPassContainerAsParameter() + { + $pimple = new Container(); + $pimple['service'] = function () { + return new Fixtures\Service(); + }; + $pimple['container'] = function ($container) { + return $container; + }; + + $this->assertNotSame($pimple, $pimple['service']); + $this->assertSame($pimple, $pimple['container']); + } + + public function testIsset() + { + $pimple = new Container(); + $pimple['param'] = 'value'; + $pimple['service'] = function () { + return new Fixtures\Service(); + }; + + $pimple['null'] = null; + + $this->assertTrue(isset($pimple['param'])); + $this->assertTrue(isset($pimple['service'])); + $this->assertTrue(isset($pimple['null'])); + $this->assertFalse(isset($pimple['non_existent'])); + } + + public function testConstructorInjection() + { + $params = array('param' => 'value'); + $pimple = new Container($params); + + $this->assertSame($params['param'], $pimple['param']); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Identifier "foo" is not defined. + */ + public function testOffsetGetValidatesKeyIsPresent() + { + $pimple = new Container(); + echo $pimple['foo']; + } + + public function testOffsetGetHonorsNullValues() + { + $pimple = new Container(); + $pimple['foo'] = null; + $this->assertNull($pimple['foo']); + } + + public function testUnset() + { + $pimple = new Container(); + $pimple['param'] = 'value'; + $pimple['service'] = function () { + return new Fixtures\Service(); + }; + + unset($pimple['param'], $pimple['service']); + $this->assertFalse(isset($pimple['param'])); + $this->assertFalse(isset($pimple['service'])); + } + + /** + * @dataProvider serviceDefinitionProvider + */ + public function testShare($service) + { + $pimple = new Container(); + $pimple['shared_service'] = $service; + + $serviceOne = $pimple['shared_service']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceOne); + + $serviceTwo = $pimple['shared_service']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceTwo); + + $this->assertSame($serviceOne, $serviceTwo); + } + + /** + * @dataProvider serviceDefinitionProvider + */ + public function testProtect($service) + { + $pimple = new Container(); + $pimple['protected'] = $pimple->protect($service); + + $this->assertSame($service, $pimple['protected']); + } + + public function testGlobalFunctionNameAsParameterValue() + { + $pimple = new Container(); + $pimple['global_function'] = 'strlen'; + $this->assertSame('strlen', $pimple['global_function']); + } + + public function testRaw() + { + $pimple = new Container(); + $pimple['service'] = $definition = $pimple->factory(function () { return 'foo'; }); + $this->assertSame($definition, $pimple->raw('service')); + } + + public function testRawHonorsNullValues() + { + $pimple = new Container(); + $pimple['foo'] = null; + $this->assertNull($pimple->raw('foo')); + } + + public function testFluentRegister() + { + $pimple = new Container(); + $this->assertSame($pimple, $pimple->register($this->getMock('Pimple\ServiceProviderInterface'))); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Identifier "foo" is not defined. + */ + public function testRawValidatesKeyIsPresent() + { + $pimple = new Container(); + $pimple->raw('foo'); + } + + /** + * @dataProvider serviceDefinitionProvider + */ + public function testExtend($service) + { + $pimple = new Container(); + $pimple['shared_service'] = function () { + return new Fixtures\Service(); + }; + $pimple['factory_service'] = $pimple->factory(function () { + return new Fixtures\Service(); + }); + + $pimple->extend('shared_service', $service); + $serviceOne = $pimple['shared_service']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceOne); + $serviceTwo = $pimple['shared_service']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceTwo); + $this->assertSame($serviceOne, $serviceTwo); + $this->assertSame($serviceOne->value, $serviceTwo->value); + + $pimple->extend('factory_service', $service); + $serviceOne = $pimple['factory_service']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceOne); + $serviceTwo = $pimple['factory_service']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceTwo); + $this->assertNotSame($serviceOne, $serviceTwo); + $this->assertNotSame($serviceOne->value, $serviceTwo->value); + } + + public function testExtendDoesNotLeakWithFactories() + { + if (extension_loaded('pimple')) { + $this->markTestSkipped('Pimple extension does not support this test'); + } + $pimple = new Container(); + + $pimple['foo'] = $pimple->factory(function () { return; }); + $pimple['foo'] = $pimple->extend('foo', function ($foo, $pimple) { return; }); + unset($pimple['foo']); + + $p = new \ReflectionProperty($pimple, 'values'); + $p->setAccessible(true); + $this->assertEmpty($p->getValue($pimple)); + + $p = new \ReflectionProperty($pimple, 'factories'); + $p->setAccessible(true); + $this->assertCount(0, $p->getValue($pimple)); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Identifier "foo" is not defined. + */ + public function testExtendValidatesKeyIsPresent() + { + $pimple = new Container(); + $pimple->extend('foo', function () {}); + } + + public function testKeys() + { + $pimple = new Container(); + $pimple['foo'] = 123; + $pimple['bar'] = 123; + + $this->assertEquals(array('foo', 'bar'), $pimple->keys()); + } + + /** @test */ + public function settingAnInvokableObjectShouldTreatItAsFactory() + { + $pimple = new Container(); + $pimple['invokable'] = new Fixtures\Invokable(); + + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $pimple['invokable']); + } + + /** @test */ + public function settingNonInvokableObjectShouldTreatItAsParameter() + { + $pimple = new Container(); + $pimple['non_invokable'] = new Fixtures\NonInvokable(); + + $this->assertInstanceOf('Pimple\Tests\Fixtures\NonInvokable', $pimple['non_invokable']); + } + + /** + * @dataProvider badServiceDefinitionProvider + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Service definition is not a Closure or invokable object. + */ + public function testFactoryFailsForInvalidServiceDefinitions($service) + { + $pimple = new Container(); + $pimple->factory($service); + } + + /** + * @dataProvider badServiceDefinitionProvider + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Callable is not a Closure or invokable object. + */ + public function testProtectFailsForInvalidServiceDefinitions($service) + { + $pimple = new Container(); + $pimple->protect($service); + } + + /** + * @dataProvider badServiceDefinitionProvider + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Identifier "foo" does not contain an object definition. + */ + public function testExtendFailsForKeysNotContainingServiceDefinitions($service) + { + $pimple = new Container(); + $pimple['foo'] = $service; + $pimple->extend('foo', function () {}); + } + + /** + * @dataProvider badServiceDefinitionProvider + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Extension service definition is not a Closure or invokable object. + */ + public function testExtendFailsForInvalidServiceDefinitions($service) + { + $pimple = new Container(); + $pimple['foo'] = function () {}; + $pimple->extend('foo', $service); + } + + /** + * Provider for invalid service definitions. + */ + public function badServiceDefinitionProvider() + { + return array( + array(123), + array(new Fixtures\NonInvokable()), + ); + } + + /** + * Provider for service definitions. + */ + public function serviceDefinitionProvider() + { + return array( + array(function ($value) { + $service = new Fixtures\Service(); + $service->value = $value; + + return $service; + }), + array(new Fixtures\Invokable()), + ); + } + + public function testDefiningNewServiceAfterFreeze() + { + $pimple = new Container(); + $pimple['foo'] = function () { + return 'foo'; + }; + $foo = $pimple['foo']; + + $pimple['bar'] = function () { + return 'bar'; + }; + $this->assertSame('bar', $pimple['bar']); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Cannot override frozen service "foo". + */ + public function testOverridingServiceAfterFreeze() + { + $pimple = new Container(); + $pimple['foo'] = function () { + return 'foo'; + }; + $foo = $pimple['foo']; + + $pimple['foo'] = function () { + return 'bar'; + }; + } + + public function testRemovingServiceAfterFreeze() + { + $pimple = new Container(); + $pimple['foo'] = function () { + return 'foo'; + }; + $foo = $pimple['foo']; + + unset($pimple['foo']); + $pimple['foo'] = function () { + return 'bar'; + }; + $this->assertSame('bar', $pimple['foo']); + } + + public function testExtendingService() + { + $pimple = new Container(); + $pimple['foo'] = function () { + return 'foo'; + }; + $pimple['foo'] = $pimple->extend('foo', function ($foo, $app) { + return "$foo.bar"; + }); + $pimple['foo'] = $pimple->extend('foo', function ($foo, $app) { + return "$foo.baz"; + }); + $this->assertSame('foo.bar.baz', $pimple['foo']); + } + + public function testExtendingServiceAfterOtherServiceFreeze() + { + $pimple = new Container(); + $pimple['foo'] = function () { + return 'foo'; + }; + $pimple['bar'] = function () { + return 'bar'; + }; + $foo = $pimple['foo']; + + $pimple['bar'] = $pimple->extend('bar', function ($bar, $app) { + return "$bar.baz"; + }); + $this->assertSame('bar.baz', $pimple['bar']); + } +} diff --git a/vendor/psr/log/.gitignore b/vendor/psr/log/.gitignore new file mode 100644 index 0000000000..22d0d82f80 --- /dev/null +++ b/vendor/psr/log/.gitignore @@ -0,0 +1 @@ +vendor diff --git a/vendor/psr/log/LICENSE b/vendor/psr/log/LICENSE new file mode 100644 index 0000000000..474c952b4b --- /dev/null +++ b/vendor/psr/log/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2012 PHP Framework Interoperability Group + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/psr/log/Psr/Log/AbstractLogger.php b/vendor/psr/log/Psr/Log/AbstractLogger.php new file mode 100644 index 0000000000..90e721af2d --- /dev/null +++ b/vendor/psr/log/Psr/Log/AbstractLogger.php @@ -0,0 +1,128 @@ +log(LogLevel::EMERGENCY, $message, $context); + } + + /** + * Action must be taken immediately. + * + * Example: Entire website down, database unavailable, etc. This should + * trigger the SMS alerts and wake you up. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function alert($message, array $context = array()) + { + $this->log(LogLevel::ALERT, $message, $context); + } + + /** + * Critical conditions. + * + * Example: Application component unavailable, unexpected exception. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function critical($message, array $context = array()) + { + $this->log(LogLevel::CRITICAL, $message, $context); + } + + /** + * Runtime errors that do not require immediate action but should typically + * be logged and monitored. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function error($message, array $context = array()) + { + $this->log(LogLevel::ERROR, $message, $context); + } + + /** + * Exceptional occurrences that are not errors. + * + * Example: Use of deprecated APIs, poor use of an API, undesirable things + * that are not necessarily wrong. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function warning($message, array $context = array()) + { + $this->log(LogLevel::WARNING, $message, $context); + } + + /** + * Normal but significant events. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function notice($message, array $context = array()) + { + $this->log(LogLevel::NOTICE, $message, $context); + } + + /** + * Interesting events. + * + * Example: User logs in, SQL logs. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function info($message, array $context = array()) + { + $this->log(LogLevel::INFO, $message, $context); + } + + /** + * Detailed debug information. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function debug($message, array $context = array()) + { + $this->log(LogLevel::DEBUG, $message, $context); + } +} diff --git a/vendor/psr/log/Psr/Log/InvalidArgumentException.php b/vendor/psr/log/Psr/Log/InvalidArgumentException.php new file mode 100644 index 0000000000..67f852d1db --- /dev/null +++ b/vendor/psr/log/Psr/Log/InvalidArgumentException.php @@ -0,0 +1,7 @@ +logger = $logger; + } +} diff --git a/vendor/psr/log/Psr/Log/LoggerInterface.php b/vendor/psr/log/Psr/Log/LoggerInterface.php new file mode 100644 index 0000000000..5ea72438b5 --- /dev/null +++ b/vendor/psr/log/Psr/Log/LoggerInterface.php @@ -0,0 +1,123 @@ +log(LogLevel::EMERGENCY, $message, $context); + } + + /** + * Action must be taken immediately. + * + * Example: Entire website down, database unavailable, etc. This should + * trigger the SMS alerts and wake you up. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function alert($message, array $context = array()) + { + $this->log(LogLevel::ALERT, $message, $context); + } + + /** + * Critical conditions. + * + * Example: Application component unavailable, unexpected exception. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function critical($message, array $context = array()) + { + $this->log(LogLevel::CRITICAL, $message, $context); + } + + /** + * Runtime errors that do not require immediate action but should typically + * be logged and monitored. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function error($message, array $context = array()) + { + $this->log(LogLevel::ERROR, $message, $context); + } + + /** + * Exceptional occurrences that are not errors. + * + * Example: Use of deprecated APIs, poor use of an API, undesirable things + * that are not necessarily wrong. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function warning($message, array $context = array()) + { + $this->log(LogLevel::WARNING, $message, $context); + } + + /** + * Normal but significant events. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function notice($message, array $context = array()) + { + $this->log(LogLevel::NOTICE, $message, $context); + } + + /** + * Interesting events. + * + * Example: User logs in, SQL logs. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function info($message, array $context = array()) + { + $this->log(LogLevel::INFO, $message, $context); + } + + /** + * Detailed debug information. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function debug($message, array $context = array()) + { + $this->log(LogLevel::DEBUG, $message, $context); + } + + /** + * Logs with an arbitrary level. + * + * @param mixed $level + * @param string $message + * @param array $context + * + * @return void + */ + abstract public function log($level, $message, array $context = array()); +} diff --git a/vendor/psr/log/Psr/Log/NullLogger.php b/vendor/psr/log/Psr/Log/NullLogger.php new file mode 100644 index 0000000000..d8cd682c8f --- /dev/null +++ b/vendor/psr/log/Psr/Log/NullLogger.php @@ -0,0 +1,28 @@ +logger) { }` + * blocks. + */ +class NullLogger extends AbstractLogger +{ + /** + * Logs with an arbitrary level. + * + * @param mixed $level + * @param string $message + * @param array $context + * + * @return void + */ + public function log($level, $message, array $context = array()) + { + // noop + } +} diff --git a/vendor/psr/log/Psr/Log/Test/LoggerInterfaceTest.php b/vendor/psr/log/Psr/Log/Test/LoggerInterfaceTest.php new file mode 100644 index 0000000000..a0391a52b8 --- /dev/null +++ b/vendor/psr/log/Psr/Log/Test/LoggerInterfaceTest.php @@ -0,0 +1,140 @@ + ". + * + * Example ->error('Foo') would yield "error Foo". + * + * @return string[] + */ + abstract public function getLogs(); + + public function testImplements() + { + $this->assertInstanceOf('Psr\Log\LoggerInterface', $this->getLogger()); + } + + /** + * @dataProvider provideLevelsAndMessages + */ + public function testLogsAtAllLevels($level, $message) + { + $logger = $this->getLogger(); + $logger->{$level}($message, array('user' => 'Bob')); + $logger->log($level, $message, array('user' => 'Bob')); + + $expected = array( + $level.' message of level '.$level.' with context: Bob', + $level.' message of level '.$level.' with context: Bob', + ); + $this->assertEquals($expected, $this->getLogs()); + } + + public function provideLevelsAndMessages() + { + return array( + LogLevel::EMERGENCY => array(LogLevel::EMERGENCY, 'message of level emergency with context: {user}'), + LogLevel::ALERT => array(LogLevel::ALERT, 'message of level alert with context: {user}'), + LogLevel::CRITICAL => array(LogLevel::CRITICAL, 'message of level critical with context: {user}'), + LogLevel::ERROR => array(LogLevel::ERROR, 'message of level error with context: {user}'), + LogLevel::WARNING => array(LogLevel::WARNING, 'message of level warning with context: {user}'), + LogLevel::NOTICE => array(LogLevel::NOTICE, 'message of level notice with context: {user}'), + LogLevel::INFO => array(LogLevel::INFO, 'message of level info with context: {user}'), + LogLevel::DEBUG => array(LogLevel::DEBUG, 'message of level debug with context: {user}'), + ); + } + + /** + * @expectedException \Psr\Log\InvalidArgumentException + */ + public function testThrowsOnInvalidLevel() + { + $logger = $this->getLogger(); + $logger->log('invalid level', 'Foo'); + } + + public function testContextReplacement() + { + $logger = $this->getLogger(); + $logger->info('{Message {nothing} {user} {foo.bar} a}', array('user' => 'Bob', 'foo.bar' => 'Bar')); + + $expected = array('info {Message {nothing} Bob Bar a}'); + $this->assertEquals($expected, $this->getLogs()); + } + + public function testObjectCastToString() + { + if (method_exists($this, 'createPartialMock')) { + $dummy = $this->createPartialMock('Psr\Log\Test\DummyTest', array('__toString')); + } else { + $dummy = $this->getMock('Psr\Log\Test\DummyTest', array('__toString')); + } + $dummy->expects($this->once()) + ->method('__toString') + ->will($this->returnValue('DUMMY')); + + $this->getLogger()->warning($dummy); + + $expected = array('warning DUMMY'); + $this->assertEquals($expected, $this->getLogs()); + } + + public function testContextCanContainAnything() + { + $context = array( + 'bool' => true, + 'null' => null, + 'string' => 'Foo', + 'int' => 0, + 'float' => 0.5, + 'nested' => array('with object' => new DummyTest), + 'object' => new \DateTime, + 'resource' => fopen('php://memory', 'r'), + ); + + $this->getLogger()->warning('Crazy context data', $context); + + $expected = array('warning Crazy context data'); + $this->assertEquals($expected, $this->getLogs()); + } + + public function testContextExceptionKeyCanBeExceptionOrOtherValues() + { + $logger = $this->getLogger(); + $logger->warning('Random message', array('exception' => 'oops')); + $logger->critical('Uncaught Exception!', array('exception' => new \LogicException('Fail'))); + + $expected = array( + 'warning Random message', + 'critical Uncaught Exception!' + ); + $this->assertEquals($expected, $this->getLogs()); + } +} + +class DummyTest +{ + public function __toString() + { + } +} diff --git a/vendor/psr/log/README.md b/vendor/psr/log/README.md new file mode 100644 index 0000000000..574bc1cb2a --- /dev/null +++ b/vendor/psr/log/README.md @@ -0,0 +1,45 @@ +PSR Log +======= + +This repository holds all interfaces/classes/traits related to +[PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md). + +Note that this is not a logger of its own. It is merely an interface that +describes a logger. See the specification for more details. + +Usage +----- + +If you need a logger, you can use the interface like this: + +```php +logger = $logger; + } + + public function doSomething() + { + if ($this->logger) { + $this->logger->info('Doing work'); + } + + // do something useful + } +} +``` + +You can then pick one of the implementations of the interface to get a logger. + +If you want to implement the interface, you can require this package and +implement `Psr\Log\LoggerInterface` in your code. Please read the +[specification text](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md) +for details. diff --git a/vendor/psr/log/composer.json b/vendor/psr/log/composer.json new file mode 100644 index 0000000000..87934d707e --- /dev/null +++ b/vendor/psr/log/composer.json @@ -0,0 +1,26 @@ +{ + "name": "psr/log", + "description": "Common interface for logging libraries", + "keywords": ["psr", "psr-3", "log"], + "homepage": "https://github.com/php-fig/log", + "license": "MIT", + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "require": { + "php": ">=5.3.0" + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} diff --git a/vendor/ramsey/array_column/.gitignore b/vendor/ramsey/array_column/.gitignore new file mode 100644 index 0000000000..7e8f330bd7 --- /dev/null +++ b/vendor/ramsey/array_column/.gitignore @@ -0,0 +1,6 @@ +.DS_Store +phpunit.xml +composer.lock +build +vendor +*.phar diff --git a/vendor/ramsey/array_column/.travis.yml b/vendor/ramsey/array_column/.travis.yml new file mode 100644 index 0000000000..ffc234e71f --- /dev/null +++ b/vendor/ramsey/array_column/.travis.yml @@ -0,0 +1,26 @@ +language: php + +php: + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - 7.0 + - hhvm + +before_script: + - travis_retry composer self-update + - travis_retry composer install --no-interaction --prefer-source + +script: + - mkdir -p build/logs + - ./vendor/bin/parallel-lint src tests + - ./vendor/bin/phpunit --coverage-text + - ./vendor/bin/phpcs src --standard=psr2 -sp + +after_script: + - php vendor/bin/coveralls + +matrix: + allow_failures: + - php: hhvm diff --git a/vendor/ramsey/array_column/CHANGELOG.md b/vendor/ramsey/array_column/CHANGELOG.md new file mode 100644 index 0000000000..1dd01361e2 --- /dev/null +++ b/vendor/ramsey/array_column/CHANGELOG.md @@ -0,0 +1,37 @@ +# array_column() Changelog + +## 1.1.3 + +_Released 2015-03-20_ + +* Changing package name from "rhumsaa/array_column" to "ramsey/array_column" +* Updated Travis CI build config to test more versions of PHP +* Updated Travis CI config to lint and check PSR-2 coding standards +* Added project badges +* Added CHANGELOG + +## 1.1.2 + +_Released 2013-07-22_ + +* Remove unnecessary conditionals +* Whitespace changes + +## 1.1.1 + +_Released 2013-07-06_ + +* Documentation updates + +## 1.1.0 + +_Released 2013-07-06_ + +* Catching up array_column() library functionality and tests with PHP 5.5.0 +* Set Travis CI to run tests on PHP 5.5 + +## 1.0.0 + +_Released 2013-03-22_ + +* Initial release of userland library diff --git a/vendor/ramsey/array_column/LICENSE b/vendor/ramsey/array_column/LICENSE new file mode 100644 index 0000000000..d099612dea --- /dev/null +++ b/vendor/ramsey/array_column/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2013-2015 Ben Ramsey (http://benramsey.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/ramsey/array_column/README.md b/vendor/ramsey/array_column/README.md new file mode 100644 index 0000000000..24b5b73f34 --- /dev/null +++ b/vendor/ramsey/array_column/README.md @@ -0,0 +1,91 @@ +# array_column() for PHP + +[![Build Status](https://travis-ci.org/ramsey/array_column.svg?branch=master)](https://travis-ci.org/ramsey/array_column) +[![Coverage Status](https://coveralls.io/repos/ramsey/array_column/badge.svg?branch=master)](https://coveralls.io/r/ramsey/array_column) +[![Latest Stable Version](https://poser.pugx.org/ramsey/array_column/v/stable.svg)](https://packagist.org/packages/ramsey/array_column) +[![Total Downloads](https://poser.pugx.org/ramsey/array_column/downloads.svg)](https://packagist.org/packages/ramsey/array_column) +[![Latest Unstable Version](https://poser.pugx.org/ramsey/array_column/v/unstable.svg)](https://packagist.org/packages/ramsey/array_column) +[![License](https://poser.pugx.org/ramsey/array_column/license.svg)](https://packagist.org/packages/ramsey/array_column) + +This simple library provides functionality for [`array_column()`](http://php.net/array_column) +to versions of PHP earlier than 5.5. It mimics the functionality of the built-in +function in every way. + + +## Usage + +``` +array array_column(array $input, mixed $columnKey[, mixed $indexKey]) +``` + +Given a multi-dimensional array of data, `array_column()` returns the values +from a single column of the input array, identified by the `$columnKey`. +Optionally, you may provide an `$indexKey` to index the values in the returned +array by the values from the `$indexKey` column in the input array. + +For example, using the following array of data, we tell `array_column()` to +return an array of just the last names, indexed by their record IDs. + +``` php + 2135, + 'first_name' => 'John', + 'last_name' => 'Doe' + ), + array( + 'id' => 3245, + 'first_name' => 'Sally', + 'last_name' => 'Smith' + ), + array( + 'id' => 5342, + 'first_name' => 'Jane', + 'last_name' => 'Jones' + ), + array( + 'id' => 5623, + 'first_name' => 'Peter', + 'last_name' => 'Doe' + ) +); + +$lastNames = array_column($records, 'last_name', 'id'); +``` + +If we call `print_r()` on `$lastNames`, you'll see a resulting array that looks +a bit like this: + +``` text +Array +( + [2135] => Doe + [3245] => Smith + [5342] => Jones + [5623] => Doe +) +``` + + +## Installation + +The easiest way to install this library is to use [Composer](https://getcomposer.org/): + +``` +php composer.phar require ramsey/array_column +``` + +Then, when you run `composer install`, everything will fall magically into place, +and the `array_column()` function will be available to your project, as long as +you are including Composer's autoloader. + +_However, you do not need Composer to use this library._ + +This library has no dependencies and should work on older versions of PHP. +Download the code and include `src/array_column.php` in your project, and all +should work fine. + +When you are ready to run your project on PHP 5.5, everything should +continue to run well without conflicts, even if this library remains included +in your project. diff --git a/vendor/ramsey/array_column/composer.json b/vendor/ramsey/array_column/composer.json new file mode 100644 index 0000000000..2316b22557 --- /dev/null +++ b/vendor/ramsey/array_column/composer.json @@ -0,0 +1,27 @@ +{ + "name": "ramsey/array_column", + "description": "Provides functionality for array_column() to projects using PHP earlier than version 5.5.", + "type": "library", + "keywords": ["array", "column", "array_column"], + "homepage": "https://github.com/ramsey/array_column", + "license": "MIT", + "authors": [ + { + "name": "Ben Ramsey", + "homepage": "http://benramsey.com" + } + ], + "support": { + "issues": "https://github.com/ramsey/array_column/issues", + "source": "https://github.com/ramsey/array_column" + }, + "require-dev": { + "phpunit/phpunit": "~4.5", + "squizlabs/php_codesniffer": "~2.2", + "jakub-onderka/php-parallel-lint": "0.8.*", + "satooshi/php-coveralls": "0.6.*" + }, + "autoload": { + "files": ["src/array_column.php"] + } +} diff --git a/vendor/ramsey/array_column/phpunit.xml.dist b/vendor/ramsey/array_column/phpunit.xml.dist new file mode 100644 index 0000000000..61da3c177b --- /dev/null +++ b/vendor/ramsey/array_column/phpunit.xml.dist @@ -0,0 +1,21 @@ + + + + + ./tests + + + + + ./src + + + + + + + + diff --git a/vendor/ramsey/array_column/src/array_column.php b/vendor/ramsey/array_column/src/array_column.php new file mode 100644 index 0000000000..1e7fa99f28 --- /dev/null +++ b/vendor/ramsey/array_column/src/array_column.php @@ -0,0 +1,115 @@ +recordSet = array( + array( + 'id' => 1, + 'first_name' => 'John', + 'last_name' => 'Doe' + ), + array( + 'id' => 2, + 'first_name' => 'Sally', + 'last_name' => 'Smith' + ), + array( + 'id' => 3, + 'first_name' => 'Jane', + 'last_name' => 'Jones' + ), + ); + + $fh = fopen(__FILE__, 'r', true); + + $this->multiDataTypes = array( + array( + 'id' => 1, + 'value' => new stdClass + ), + array( + 'id' => 2, + 'value' => 34.2345 + ), + array( + 'id' => 3, + 'value' => true + ), + array( + 'id' => 4, + 'value' => false + ), + array( + 'id' => 5, + 'value' => null + ), + array( + 'id' => 6, + 'value' => 1234 + ), + array( + 'id' => 7, + 'value' => 'Foo' + ), + array( + 'id' => 8, + 'value' => $fh + ), + ); + + $this->numericColumns = array( + array('aaa', '111'), + array('bbb', '222'), + array('ccc', '333', -1 => 'ddd'), + ); + + $this->mismatchedColumns = array( + array('a' => 'foo', 'b' => 'bar', 'e' => 'bbb'), + array('a' => 'baz', 'c' => 'qux', 'd' => 'aaa'), + array('a' => 'eee', 'b' => 'fff', 'e' => 'ggg'), + ); + } + + public function testFirstNameColumnFromRecordset() + { + $expected = array('John', 'Sally', 'Jane'); + $this->assertEquals($expected, array_column($this->recordSet, 'first_name')); + } + + public function testIdColumnFromRecordset() + { + $expected = array(1, 2, 3); + $this->assertEquals($expected, array_column($this->recordSet, 'id')); + } + + public function testLastNameColumnKeyedByIdColumnFromRecordset() + { + $expected = array(1 => 'Doe', 2 => 'Smith', 3 => 'Jones'); + $this->assertEquals($expected, array_column($this->recordSet, 'last_name', 'id')); + } + + public function testLastNameColumnKeyedByFirstNameColumnFromRecordset() + { + $expected = array('John' => 'Doe', 'Sally' => 'Smith', 'Jane' => 'Jones'); + $this->assertEquals($expected, array_column($this->recordSet, 'last_name', 'first_name')); + } + + public function testValueColumnWithMultipleDataTypes() + { + $expected = array(); + foreach ($this->multiDataTypes as $row) { + $expected[] = $row['value']; + } + $this->assertEquals($expected, array_column($this->multiDataTypes, 'value')); + } + + public function testValueColumnKeyedByIdWithMultipleDataTypes() + { + $expected = array(); + foreach ($this->multiDataTypes as $row) { + $expected[$row['id']] = $row['value']; + } + $this->assertEquals($expected, array_column($this->multiDataTypes, 'value', 'id')); + } + + public function testNumericColumnKeys1() + { + $expected = array('111', '222', '333'); + $this->assertEquals($expected, array_column($this->numericColumns, 1)); + } + + public function testNumericColumnKeys2() + { + $expected = array('aaa' => '111', 'bbb' => '222', 'ccc' => '333'); + $this->assertEquals($expected, array_column($this->numericColumns, 1, 0)); + } + + public function testNumericColumnKeys3() + { + $expected = array('aaa' => '111', 'bbb' => '222', 'ccc' => '333'); + $this->assertEquals($expected, array_column($this->numericColumns, 1, 0.123)); + } + + public function testNumericColumnKeys4() + { + $expected = array(0 => '111', 1 => '222', 'ddd' => '333'); + $this->assertEquals($expected, array_column($this->numericColumns, 1, -1)); + } + + public function testFailureToFindColumn1() + { + $this->assertEquals(array(), array_column($this->numericColumns, 2)); + } + + public function testFailureToFindColumn2() + { + $this->assertEquals(array(), array_column($this->numericColumns, 'foo')); + } + + public function testFailureToFindColumn3() + { + $expected = array('aaa', 'bbb', 'ccc'); + $this->assertEquals($expected, array_column($this->numericColumns, 0, 'foo')); + } + + public function testFailureToFindColumn4() + { + $this->assertEquals(array(), array_column($this->numericColumns, 3.14)); + } + + public function testSingleDimensionalArray() + { + $singleDimension = array('foo', 'bar', 'baz'); + $this->assertEquals(array(), array_column($singleDimension, 1)); + } + + public function testMismatchedColumns1() + { + $expected = array('qux'); + $this->assertEquals($expected, array_column($this->mismatchedColumns, 'c')); + } + + public function testMismatchedColumns2() + { + $expected = array('baz' => 'qux'); + $this->assertEquals($expected, array_column($this->mismatchedColumns, 'c', 'a')); + } + + public function testMismatchedColumns3() + { + $expected = array('foo', 'aaa' => 'baz', 'eee'); + $this->assertEquals($expected, array_column($this->mismatchedColumns, 'a', 'd')); + } + + public function testMismatchedColumns4() + { + $expected = array('bbb' => 'foo', 'baz', 'ggg' => 'eee'); + $this->assertEquals($expected, array_column($this->mismatchedColumns, 'a', 'e')); + } + + public function testMismatchedColumns5() + { + $expected = array('bar', 'fff'); + $this->assertEquals($expected, array_column($this->mismatchedColumns, 'b')); + } + + public function testMismatchedColumns6() + { + $expected = array('foo' => 'bar', 'eee' => 'fff'); + $this->assertEquals($expected, array_column($this->mismatchedColumns, 'b', 'a')); + } + + public function testObjectConvertedToString() + { + $f = new Foo(); + $b = new Bar(); + $this->assertEquals(array('Doe', 'Smith', 'Jones'), array_column($this->recordSet, $f)); + $this->assertEquals(array('John' => 'Doe', 'Sally' => 'Smith', 'Jane' => 'Jones'), array_column($this->recordSet, $f, $b)); + } + + /** + * @expectedException PHPUnit_Framework_Error_Warning + * @expectedExceptionMessage array_column() expects at least 2 parameters, 0 given + */ + public function testFunctionWithZeroArgs() + { + $foo = array_column(); + } + + public function testFunctionWithZeroArgsReturnValue() + { + $foo = @array_column(); + $this->assertNull($foo); + } + + /** + * @expectedException PHPUnit_Framework_Error_Warning + * @expectedExceptionMessage array_column() expects at least 2 parameters, 1 given + */ + public function testFunctionWithOneArg() + { + $foo = array_column(array()); + } + + public function testFunctionWithOneArgReturnValue() + { + $foo = @array_column(array()); + $this->assertNull($foo); + } + + /** + * @expectedException PHPUnit_Framework_Error_Warning + * @expectedExceptionMessage array_column() expects parameter 1 to be array, string given + */ + public function testFunctionWithStringAsFirstArg() + { + $foo = array_column('foo', 0); + } + + public function testFunctionWithStringAsFirstArgReturnValue() + { + $foo = @array_column('foo', 0); + $this->assertNull($foo); + } + + /** + * @expectedException PHPUnit_Framework_Error_Warning + * @expectedExceptionMessage array_column() expects parameter 1 to be array, integer given + */ + public function testFunctionWithIntAsFirstArg() + { + $foo = array_column(1, 'foo'); + } + + public function testFunctionWithIntAsFirstArgReturnValue() + { + $foo = @array_column(1, 'foo'); + $this->assertNull($foo); + } + + /** + * @expectedException PHPUnit_Framework_Error_Warning + * @expectedExceptionMessage array_column(): The column key should be either a string or an integer + */ + public function testFunctionWithColumnKeyAsBool() + { + $foo = array_column(array(), true); + } + + public function testFunctionWithColumnKeyAsBoolReturnValue() + { + $foo = @array_column(array(), true); + $this->assertFalse($foo); + } + + /** + * @expectedException PHPUnit_Framework_Error_Warning + * @expectedExceptionMessage array_column(): The column key should be either a string or an integer + */ + public function testFunctionWithColumnKeyAsArray() + { + $foo = array_column(array(), array()); + } + + public function testFunctionWithColumnKeyAsArrayReturnValue() + { + $foo = @array_column(array(), array()); + $this->assertFalse($foo); + } + + /** + * @expectedException PHPUnit_Framework_Error_Warning + * @expectedExceptionMessage array_column(): The index key should be either a string or an integer + */ + public function testFunctionWithIndexKeyAsBool() + { + $foo = array_column(array(), 'foo', true); + } + + public function testFunctionWithIndexKeyAsBoolReturnValue() + { + $foo = @array_column(array(), 'foo', true); + $this->assertFalse($foo); + } + + /** + * @expectedException PHPUnit_Framework_Error_Warning + * @expectedExceptionMessage array_column(): The index key should be either a string or an integer + */ + public function testFunctionWithIndexKeyAsArray() + { + $foo = array_column(array(), 'foo', array()); + } + + public function testFunctionWithIndexKeyAsArrayReturnValue() + { + $foo = @array_column(array(), 'foo', array()); + $this->assertFalse($foo); + } + + /** + * @link https://bugs.php.net/bug.php?id=64493 + */ + public function testBugRequest64493() + { + // Array from Bug Request #64493 test script + $rows = array( + 456 => array('id' => '3', 'title' => 'Foo', 'date' => '2013-03-25'), + 457 => array('id' => '5', 'title' => 'Bar', 'date' => '2012-05-20'), + ); + + // pass null as second parameter to get back all columns indexed by third parameter + $expected1 = array( + 3 => array('id' => '3', 'title' => 'Foo', 'date' => '2013-03-25'), + 5 => array('id' => '5', 'title' => 'Bar', 'date' => '2012-05-20'), + ); + $this->assertEquals($expected1, array_column($rows, null, 'id')); + + // pass null as second parameter and bogus third param to get back zero-indexed array of all columns + $expected2 = array( + array('id' => '3', 'title' => 'Foo', 'date' => '2013-03-25'), + array('id' => '5', 'title' => 'Bar', 'date' => '2012-05-20'), + ); + $this->assertEquals($expected2, array_column($rows, null, 'foo')); + + // pass null as second parameter and no third param to get back array_values(input) (same as $expected2) + $this->assertEquals($expected2, array_column($rows, null)); + } + + public function testObjectCast() + { + $columnKey = new ColumnKeyClass(); + $indexKey = new IndexKeyClass(); + $value = new ValueClass(); + + $records = array( + array( + 'id' => $value, + 'first_name' => 'John', + 'last_name' => 'XXX' + ), + array( + 'id' => 3245, + 'first_name' => 'Sally', + 'last_name' => 'Smith' + ), + ); + + $expected = array( + 2135 => 'John', + 3245 => 'Sally', + ); + + $this->assertEquals($expected, array_column($records, $columnKey, $indexKey)); + } +} + +class Foo +{ + public function __toString() + { + return 'last_name'; + } +} + +class Bar +{ + public function __toString() + { + return 'first_name'; + } +} + +class ColumnKeyClass +{ + function __toString() { return 'first_name'; } +} + +class IndexKeyClass +{ + function __toString() { return 'id'; } +} + +class ValueClass +{ + function __toString() { return '2135'; } +} diff --git a/vendor/ramsey/array_column/tests/bootstrap.php b/vendor/ramsey/array_column/tests/bootstrap.php new file mode 100644 index 0000000000..97eabbac7b --- /dev/null +++ b/vendor/ramsey/array_column/tests/bootstrap.php @@ -0,0 +1,5 @@ +=5.3.3" + }, + "require-dev": { + "mockery/mockery": "~0.9.1", + "symfony/phpunit-bridge": "~3.2" + }, + "autoload": { + "files": ["lib/swift_required.php"] + }, + "autoload-dev": { + "psr-0": { + "Swift_": "tests/unit" + } + }, + "extra": { + "branch-alias": { + "dev-master": "5.4-dev" + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/doc/headers.rst b/vendor/swiftmailer/swiftmailer/doc/headers.rst new file mode 100644 index 0000000000..6aec23ff38 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/headers.rst @@ -0,0 +1,742 @@ +Message Headers +=============== + +Sometimes you'll want to add your own headers to a message or modify/remove +headers that are already present. You work with the message's HeaderSet to do +this. + +Header Basics +------------- + +All MIME entities in Swift Mailer -- including the message itself -- +store their headers in a single object called a HeaderSet. This HeaderSet is +retrieved with the ``getHeaders()`` method. + +As mentioned in the previous chapter, everything that forms a part of a message +in Swift Mailer is a MIME entity that is represented by an instance of +``Swift_Mime_MimeEntity``. This includes -- most notably -- the message object +itself, attachments, MIME parts and embedded images. Each of these MIME entities +consists of a body and a set of headers that describe the body. + +For all of the "standard" headers in these MIME entities, such as the +``Content-Type``, there are named methods for working with them, such as +``setContentType()`` and ``getContentType()``. This is because headers are a +moderately complex area of the library. Each header has a slightly different +required structure that it must meet in order to comply with the standards that +govern email (and that are checked by spam blockers etc). + +You fetch the HeaderSet from a MIME entity like so: + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + // Fetch the HeaderSet from a Message object + $headers = $message->getHeaders(); + + $attachment = Swift_Attachment::fromPath('document.pdf'); + + // Fetch the HeaderSet from an attachment object + $headers = $attachment->getHeaders(); + +The job of the HeaderSet is to contain and manage instances of Header objects. +Depending upon the MIME entity the HeaderSet came from, the contents of the +HeaderSet will be different, since an attachment for example has a different +set of headers to those in a message. + +You can find out what the HeaderSet contains with a quick loop, dumping out +the names of the headers: + +.. code-block:: php + + foreach ($headers->getAll() as $header) { + printf("%s
\n", $header->getFieldName()); + } + + /* + Content-Transfer-Encoding + Content-Type + MIME-Version + Date + Message-ID + From + Subject + To + */ + +You can also dump out the rendered HeaderSet by calling its ``toString()`` +method: + +.. code-block:: php + + echo $headers->toString(); + + /* + Message-ID: <1234869991.499a9ee7f1d5e@swift.generated> + Date: Tue, 17 Feb 2009 22:26:31 +1100 + Subject: Awesome subject! + From: sender@example.org + To: recipient@example.org + MIME-Version: 1.0 + Content-Type: text/plain; charset=utf-8 + Content-Transfer-Encoding: quoted-printable + */ + +Where the complexity comes in is when you want to modify an existing header. +This complexity comes from the fact that each header can be of a slightly +different type (such as a Date header, or a header that contains email +addresses, or a header that has key-value parameters on it!). Each header in the +HeaderSet is an instance of ``Swift_Mime_Header``. They all have common +functionality, but knowing exactly what type of header you're working with will +allow you a little more control. + +You can determine the type of header by comparing the return value of its +``getFieldType()`` method with the constants ``TYPE_TEXT``, +``TYPE_PARAMETERIZED``, ``TYPE_DATE``, ``TYPE_MAILBOX``, ``TYPE_ID`` and +``TYPE_PATH`` which are defined in ``Swift_Mime_Header``. + + +.. code-block:: php + + foreach ($headers->getAll() as $header) { + switch ($header->getFieldType()) { + case Swift_Mime_Header::TYPE_TEXT: $type = 'text'; + break; + case Swift_Mime_Header::TYPE_PARAMETERIZED: $type = 'parameterized'; + break; + case Swift_Mime_Header::TYPE_MAILBOX: $type = 'mailbox'; + break; + case Swift_Mime_Header::TYPE_DATE: $type = 'date'; + break; + case Swift_Mime_Header::TYPE_ID: $type = 'ID'; + break; + case Swift_Mime_Header::TYPE_PATH: $type = 'path'; + break; + } + printf("%s: is a %s header
\n", $header->getFieldName(), $type); + } + + /* + Content-Transfer-Encoding: is a text header + Content-Type: is a parameterized header + MIME-Version: is a text header + Date: is a date header + Message-ID: is a ID header + From: is a mailbox header + Subject: is a text header + To: is a mailbox header + */ + +Headers can be removed from the set, modified within the set, or added to the +set. + +The following sections show you how to work with the HeaderSet and explain the +details of each implementation of ``Swift_Mime_Header`` that may +exist within the HeaderSet. + +Header Types +------------ + +Because all headers are modeled on different data (dates, addresses, text!) +there are different types of Header in Swift Mailer. Swift Mailer attempts to +categorize all possible MIME headers into more general groups, defined by a +small number of classes. + +Text Headers +~~~~~~~~~~~~ + +Text headers are the simplest type of Header. They contain textual information +with no special information included within it -- for example the Subject +header in a message. + +There's nothing particularly interesting about a text header, though it is +probably the one you'd opt to use if you need to add a custom header to a +message. It represents text just like you'd think it does. If the text +contains characters that are not permitted in a message header (such as new +lines, or non-ascii characters) then the header takes care of encoding the +text so that it can be used. + +No header -- including text headers -- in Swift Mailer is vulnerable to +header-injection attacks. Swift Mailer breaks any attempt at header injection by +encoding the dangerous data into a non-dangerous form. + +It's easy to add a new text header to a HeaderSet. You do this by calling the +HeaderSet's ``addTextHeader()`` method. + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + $headers = $message->getHeaders(); + + $headers->addTextHeader('Your-Header-Name', 'the header value'); + +Changing the value of an existing text header is done by calling it's +``setValue()`` method. + +.. code-block:: php + + $subject = $message->getHeaders()->get('Subject'); + + $subject->setValue('new subject'); + +When output via ``toString()``, a text header produces something like the +following: + +.. code-block:: php + + $subject = $message->getHeaders()->get('Subject'); + + $subject->setValue('amazing subject line'); + + echo $subject->toString(); + + /* + + Subject: amazing subject line + + */ + +If the header contains any characters that are outside of the US-ASCII range +however, they will be encoded. This is nothing to be concerned about since +mail clients will decode them back. + +.. code-block:: php + + $subject = $message->getHeaders()->get('Subject'); + + $subject->setValue('contains – dash'); + + echo $subject->toString(); + + /* + + Subject: contains =?utf-8?Q?=E2=80=93?= dash + + */ + +Parameterized Headers +~~~~~~~~~~~~~~~~~~~~~ + +Parameterized headers are text headers that contain key-value parameters +following the textual content. The Content-Type header of a message is a +parameterized header since it contains charset information after the content +type. + +The parameterized header type is a special type of text header. It extends the +text header by allowing additional information to follow it. All of the methods +from text headers are available in addition to the methods described here. + +Adding a parameterized header to a HeaderSet is done by using the +``addParameterizedHeader()`` method which takes a text value like +``addTextHeader()`` but it also accepts an associative array of +key-value parameters. + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + $headers = $message->getHeaders(); + + $headers->addParameterizedHeader( + 'Header-Name', 'header value', + array('foo' => 'bar') + ); + +To change the text value of the header, call it's ``setValue()`` method just as +you do with text headers. + +To change the parameters in the header, call the header's ``setParameters()`` +method or the ``setParameter()`` method (note the pluralization). + +.. code-block:: php + + $type = $message->getHeaders()->get('Content-Type'); + + // setParameters() takes an associative array + $type->setParameters(array( + 'name' => 'file.txt', + 'charset' => 'iso-8859-1' + )); + + // setParameter() takes two args for $key and $value + $type->setParameter('charset', 'iso-8859-1'); + +When output via ``toString()``, a parameterized header produces something like +the following: + +.. code-block:: php + + $type = $message->getHeaders()->get('Content-Type'); + + $type->setValue('text/html'); + $type->setParameter('charset', 'utf-8'); + + echo $type->toString(); + + /* + + Content-Type: text/html; charset=utf-8 + + */ + +If the header contains any characters that are outside of the US-ASCII range +however, they will be encoded, just like they are for text headers. This is +nothing to be concerned about since mail clients will decode them back. +Likewise, if the parameters contain any non-ascii characters they will be +encoded so that they can be transmitted safely. + +.. code-block:: php + + $attachment = Swift_Attachment::newInstance(); + + $disp = $attachment->getHeaders()->get('Content-Disposition'); + + $disp->setValue('attachment'); + $disp->setParameter('filename', 'report–may.pdf'); + + echo $disp->toString(); + + /* + + Content-Disposition: attachment; filename*=utf-8''report%E2%80%93may.pdf + + */ + +Date Headers +~~~~~~~~~~~~ + +Date headers contains an RFC 2822 formatted date (i.e. what PHP's ``date('r')`` +returns). They are used anywhere a date or time is needed to be presented as a +message header. + +The data on which a date header is modeled is simply a UNIX timestamp such as +that returned by ``time()`` or ``strtotime()``. The timestamp is used to create +a correctly structured RFC 2822 formatted date such as +``Tue, 17 Feb 2009 22:26:31 +1100``. + +The obvious place this header type is used is in the ``Date:`` header of the +message itself. + +It's easy to add a new date header to a HeaderSet. You do this by calling +the HeaderSet's ``addDateHeader()`` method. + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + $headers = $message->getHeaders(); + + $headers->addDateHeader('Your-Header-Name', strtotime('3 days ago')); + +Changing the value of an existing date header is done by calling it's +``setTimestamp()`` method. + +.. code-block:: php + + $date = $message->getHeaders()->get('Date'); + + $date->setTimestamp(time()); + +When output via ``toString()``, a date header produces something like the +following: + +.. code-block:: php + + $date = $message->getHeaders()->get('Date'); + + echo $date->toString(); + + /* + + Date: Wed, 18 Feb 2009 13:35:02 +1100 + + */ + +Mailbox (e-mail address) Headers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Mailbox headers contain one or more email addresses, possibly with +personalized names attached to them. The data on which they are modeled is +represented by an associative array of email addresses and names. + +Mailbox headers are probably the most complex header type to understand in +Swift Mailer because they accept their input as an array which can take various +forms, as described in the previous chapter. + +All of the headers that contain e-mail addresses in a message -- with the +exception of ``Return-Path:`` which has a stricter syntax -- use this header +type. That is, ``To:``, ``From:`` etc. + +You add a new mailbox header to a HeaderSet by calling the HeaderSet's +``addMailboxHeader()`` method. + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + $headers = $message->getHeaders(); + + $headers->addMailboxHeader('Your-Header-Name', array( + 'person1@example.org' => 'Person Name One', + 'person2@example.org', + 'person3@example.org', + 'person4@example.org' => 'Another named person' + )); + +Changing the value of an existing mailbox header is done by calling it's +``setNameAddresses()`` method. + +.. code-block:: php + + $to = $message->getHeaders()->get('To'); + + $to->setNameAddresses(array( + 'joe@example.org' => 'Joe Bloggs', + 'john@example.org' => 'John Doe', + 'no-name@example.org' + )); + +If you don't wish to concern yourself with the complicated accepted input +formats accepted by ``setNameAddresses()`` as described in the previous chapter +and you only want to set one or more addresses (not names) then you can just +use the ``setAddresses()`` method instead. + +.. code-block:: php + + $to = $message->getHeaders()->get('To'); + + $to->setAddresses(array( + 'joe@example.org', + 'john@example.org', + 'no-name@example.org' + )); + +.. note:: + + Both methods will accept the above input format in practice. + +If all you want to do is set a single address in the header, you can use a +string as the input parameter to ``setAddresses()`` and/or +``setNameAddresses()``. + +.. code-block:: php + + $to = $message->getHeaders()->get('To'); + + $to->setAddresses('joe-bloggs@example.org'); + +When output via ``toString()``, a mailbox header produces something like the +following: + +.. code-block:: php + + $to = $message->getHeaders()->get('To'); + + $to->setNameAddresses(array( + 'person1@example.org' => 'Name of Person', + 'person2@example.org', + 'person3@example.org' => 'Another Person' + )); + + echo $to->toString(); + + /* + + To: Name of Person , person2@example.org, Another Person + + + */ + +ID Headers +~~~~~~~~~~ + +ID headers contain identifiers for the entity (or the message). The most +notable ID header is the Message-ID header on the message itself. + +An ID that exists inside an ID header looks more-or-less less like an email +address. For example, ``<1234955437.499becad62ec2@example.org>``. +The part to the left of the @ sign is usually unique, based on the current time +and some random factor. The part on the right is usually a domain name. + +Any ID passed to the header's ``setId()`` method absolutely MUST conform to +this structure, otherwise you'll get an Exception thrown at you by Swift Mailer +(a ``Swift_RfcComplianceException``). This is to ensure that the generated +email complies with relevant RFC documents and therefore is less likely to be +blocked as spam. + +It's easy to add a new ID header to a HeaderSet. You do this by calling +the HeaderSet's ``addIdHeader()`` method. + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + $headers = $message->getHeaders(); + + $headers->addIdHeader('Your-Header-Name', '123456.unqiue@example.org'); + +Changing the value of an existing date header is done by calling its +``setId()`` method. + +.. code-block:: php + + $msgId = $message->getHeaders()->get('Message-ID'); + + $msgId->setId(time() . '.' . uniqid('thing') . '@example.org'); + +When output via ``toString()``, an ID header produces something like the +following: + +.. code-block:: php + + $msgId = $message->getHeaders()->get('Message-ID'); + + echo $msgId->toString(); + + /* + + Message-ID: <1234955437.499becad62ec2@example.org> + + */ + +Path Headers +~~~~~~~~~~~~ + +Path headers are like very-restricted mailbox headers. They contain a single +email address with no associated name. The Return-Path header of a message is +a path header. + +You add a new path header to a HeaderSet by calling the HeaderSet's +``addPathHeader()`` method. + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + $headers = $message->getHeaders(); + + $headers->addPathHeader('Your-Header-Name', 'person@example.org'); + + +Changing the value of an existing path header is done by calling its +``setAddress()`` method. + +.. code-block:: php + + $return = $message->getHeaders()->get('Return-Path'); + + $return->setAddress('my-address@example.org'); + +When output via ``toString()``, a path header produces something like the +following: + +.. code-block:: php + + $return = $message->getHeaders()->get('Return-Path'); + + $return->setAddress('person@example.org'); + + echo $return->toString(); + + /* + + Return-Path: + + */ + +Header Operations +----------------- + +Working with the headers in a message involves knowing how to use the methods +on the HeaderSet and on the individual Headers within the HeaderSet. + +Adding new Headers +~~~~~~~~~~~~~~~~~~ + +New headers can be added to the HeaderSet by using one of the provided +``add..Header()`` methods. + +To add a header to a MIME entity (such as the message): + +Get the HeaderSet from the entity by via its ``getHeaders()`` method. + +* Add the header to the HeaderSet by calling one of the ``add..Header()`` + methods. + +The added header will appear in the message when it is sent. + +.. code-block:: php + + // Adding a custom header to a message + $message = Swift_Message::newInstance(); + $headers = $message->getHeaders(); + $headers->addTextHeader('X-Mine', 'something here'); + + // Adding a custom header to an attachment + $attachment = Swift_Attachment::fromPath('/path/to/doc.pdf'); + $attachment->getHeaders()->addDateHeader('X-Created-Time', time()); + +Retrieving Headers +~~~~~~~~~~~~~~~~~~ + +Headers are retrieved through the HeaderSet's ``get()`` and ``getAll()`` +methods. + +To get a header, or several headers from a MIME entity: + +* Get the HeaderSet from the entity by via its ``getHeaders()`` method. + +* Get the header(s) from the HeaderSet by calling either ``get()`` or + ``getAll()``. + +When using ``get()`` a single header is returned that matches the name (case +insensitive) that is passed to it. When using ``getAll()`` with a header name, +an array of headers with that name are returned. Calling ``getAll()`` with no +arguments returns an array of all headers present in the entity. + +.. note:: + + It's valid for some headers to appear more than once in a message (e.g. + the Received header). For this reason ``getAll()`` exists to fetch all + headers with a specified name. In addition, ``get()`` accepts an optional + numerical index, starting from zero to specify which header you want more + specifically. + +.. note:: + + If you want to modify the contents of the header and you don't know for + sure what type of header it is then you may need to check the type by + calling its ``getFieldType()`` method. + + .. code-block:: php + + $headers = $message->getHeaders(); + + // Get the To: header + $toHeader = $headers->get('To'); + + // Get all headers named "X-Foo" + $fooHeaders = $headers->getAll('X-Foo'); + + // Get the second header named "X-Foo" + $foo = $headers->get('X-Foo', 1); + + // Get all headers that are present + $all = $headers->getAll(); + +Check if a Header Exists +~~~~~~~~~~~~~~~~~~~~~~~~ + +You can check if a named header is present in a HeaderSet by calling its +``has()`` method. + +To check if a header exists: + +* Get the HeaderSet from the entity by via its ``getHeaders()`` method. + +* Call the HeaderSet's ``has()`` method specifying the header you're looking + for. + +If the header exists, ``true`` will be returned or ``false`` if not. + +.. note:: + + It's valid for some headers to appear more than once in a message (e.g. + the Received header). For this reason ``has()`` accepts an optional + numerical index, starting from zero to specify which header you want to + check more specifically. + + .. code-block:: php + + $headers = $message->getHeaders(); + + // Check if the To: header exists + if ($headers->has('To')) { + echo 'To: exists'; + } + + // Check if an X-Foo header exists twice (i.e. check for the 2nd one) + if ($headers->has('X-Foo', 1)) { + echo 'Second X-Foo header exists'; + } + +Removing Headers +~~~~~~~~~~~~~~~~ + +Removing a Header from the HeaderSet is done by calling the HeaderSet's +``remove()`` or ``removeAll()`` methods. + +To remove an existing header: + +* Get the HeaderSet from the entity by via its ``getHeaders()`` method. + +* Call the HeaderSet's ``remove()`` or ``removeAll()`` methods specifying the + header you want to remove. + +When calling ``remove()`` a single header will be removed. When calling +``removeAll()`` all headers with the given name will be removed. If no headers +exist with the given name, no errors will occur. + +.. note:: + + It's valid for some headers to appear more than once in a message (e.g. + the Received header). For this reason ``remove()`` accepts an optional + numerical index, starting from zero to specify which header you want to + check more specifically. For the same reason, ``removeAll()`` exists to + remove all headers that have the given name. + + .. code-block:: php + + $headers = $message->getHeaders(); + + // Remove the Subject: header + $headers->remove('Subject'); + + // Remove all X-Foo headers + $headers->removeAll('X-Foo'); + + // Remove only the second X-Foo header + $headers->remove('X-Foo', 1); + +Modifying a Header's Content +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To change a Header's content you should know what type of header it is and then +call it's appropriate setter method. All headers also have a +``setFieldBodyModel()`` method that accepts a mixed parameter and delegates to +the correct setter. + +To modify an existing header: + +* Get the HeaderSet from the entity by via its ``getHeaders()`` method. + +* Get the Header by using the HeaderSet's ``get()``. + +* Call the Header's appropriate setter method or call the header's + ``setFieldBodyModel()`` method. + +The header will be updated inside the HeaderSet and the changes will be seen +when the message is sent. + +.. code-block:: php + + $headers = $message->getHeaders(); + + // Change the Subject: header + $subj = $headers->get('Subject'); + $subj->setValue('new subject here'); + + // Change the To: header + $to = $headers->get('To'); + $to->setNameAddresses(array( + 'person@example.org' => 'Person', + 'thing@example.org' + )); + + // Using the setFieldBodyModel() just delegates to the correct method + // So here to calls setNameAddresses() + $to->setFieldBodyModel(array( + 'person@example.org' => 'Person', + 'thing@example.org' + )); diff --git a/vendor/swiftmailer/swiftmailer/doc/help-resources.rst b/vendor/swiftmailer/swiftmailer/doc/help-resources.rst new file mode 100644 index 0000000000..420893590a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/help-resources.rst @@ -0,0 +1,44 @@ +Getting Help +============ + +There are a number of ways you can get help when using Swift Mailer, depending +upon the nature of your problem. For bug reports and feature requests create a +new ticket in GitHub. For general advice ask on the Google Group +(swiftmailer). + +Submitting Bugs & Feature Requests +---------------------------------- + +Bugs and feature requests should be posted on GitHub. + +If you post a bug or request a feature in the forum, or on the Google Group +you will most likely be asked to create a ticket in `GitHub`_ since it is +simply not feasible to manage such requests from a number of a different +sources. + +When you go to GitHub you will be asked to create a username and password +before you can create a ticket. This is free and takes very little time. + +When you create your ticket, do not assign it to any milestones. A developer +will assess your ticket and re-assign it as needed. + +If your ticket is reporting a bug present in the current version, which was +not present in the previous version please include the tag "regression" in +your ticket. + +GitHub will update you when work is performed on your ticket. + +Ask on the Google Group +----------------------- + +You can seek advice at Google Groups, within the "swiftmailer" `group`_. + +You can post messages to this group if you want help, or there's something you +wish to discuss with the developers and with other users. + +This is probably the fastest way to get help since it is primarily email-based +for most users, though bug reports should not be posted here since they may +not be resolved. + +.. _`GitHub`: https://github.com/swiftmailer/swiftmailer/issues +.. _`group`: http://groups.google.com/group/swiftmailer diff --git a/vendor/swiftmailer/swiftmailer/doc/including-the-files.rst b/vendor/swiftmailer/swiftmailer/doc/including-the-files.rst new file mode 100644 index 0000000000..978dca206a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/including-the-files.rst @@ -0,0 +1,46 @@ +Including Swift Mailer (Autoloading) +==================================== + +If you are using Composer, Swift Mailer will be automatically autoloaded. + +If not, you can use the built-in autoloader by requiring the +``swift_required.php`` file:: + + require_once '/path/to/swift-mailer/lib/swift_required.php'; + + /* rest of code goes here */ + +If you want to override the default Swift Mailer configuration, call the +``init()`` method on the ``Swift`` class and pass it a valid PHP callable (a +PHP function name, a PHP 5.3 anonymous function, ...):: + + require_once '/path/to/swift-mailer/lib/swift_required.php'; + + function swiftmailer_configurator() { + // configure Swift Mailer + + Swift_DependencyContainer::getInstance()->... + Swift_Preferences::getInstance()->... + } + + Swift::init('swiftmailer_configurator'); + + /* rest of code goes here */ + +The advantage of using the ``init()`` method is that your code will be +executed only if you use Swift Mailer in your script. + +.. note:: + + While Swift Mailer's autoloader is designed to play nicely with other + autoloaders, sometimes you may have a need to avoid using Swift Mailer's + autoloader and use your own instead. Include the ``swift_init.php`` + instead of the ``swift_required.php`` if you need to do this. The very + minimum include is the ``swift_init.php`` file since Swift Mailer will not + work without the dependency injection this file sets up: + + .. code-block:: php + + require_once '/path/to/swift-mailer/lib/swift_init.php'; + + /* rest of code goes here */ diff --git a/vendor/swiftmailer/swiftmailer/doc/index.rst b/vendor/swiftmailer/swiftmailer/doc/index.rst new file mode 100644 index 0000000000..a1a0a92464 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/index.rst @@ -0,0 +1,16 @@ +Swiftmailer +=========== + +.. toctree:: + :maxdepth: 2 + + introduction + overview + installing + help-resources + including-the-files + messages + headers + sending + plugins + japanese diff --git a/vendor/swiftmailer/swiftmailer/doc/installing.rst b/vendor/swiftmailer/swiftmailer/doc/installing.rst new file mode 100644 index 0000000000..557211d48b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/installing.rst @@ -0,0 +1,89 @@ +Installing the Library +====================== + +Installing with Composer +------------------------ + +The recommended way to install Swiftmailer is via Composer: + +.. code-block:: bash + + $ php composer.phar require swiftmailer/swiftmailer @stable + +Installing from Git +------------------- + +It's possible to download and install Swift Mailer directly from github.com if +you want to keep up-to-date with ease. + +Swift Mailer's source code is kept in a git repository at github.com so you +can get the source directly from the repository. + +.. note:: + + You do not need to have git installed to use Swift Mailer from GitHub. If + you don't have git installed, go to `GitHub`_ and click the "Download" + button. + +Cloning the Repository +~~~~~~~~~~~~~~~~~~~~~~ + +The repository can be cloned from git://github.com/swiftmailer/swiftmailer.git +using the ``git clone`` command. + +You will need to have ``git`` installed before you can use the +``git clone`` command. + +To clone the repository: + +* Open your favorite terminal environment (command line). + +* Move to the directory you want to clone to. + +* Run the command ``git clone git://github.com/swiftmailer/swiftmailer.git + swiftmailer``. + +The source code will be downloaded into a directory called "swiftmailer". + +The example shows the process on a UNIX-like system such as Linux, BSD or Mac +OS X. + +.. code-block:: bash + + $ cd source_code/ + $ git clone git://github.com/swiftmailer/swiftmailer.git swiftmailer + Initialized empty Git repository in /Users/chris/source_code/swiftmailer/.git/ + remote: Counting objects: 6815, done. + remote: Compressing objects: 100% (2761/2761), done. + remote: Total 6815 (delta 3641), reused 6326 (delta 3286) + Receiving objects: 100% (6815/6815), 4.35 MiB | 162 KiB/s, done. + Resolving deltas: 100% (3641/3641), done. + Checking out files: 100% (1847/1847), done. + $ cd swiftmailer/ + $ ls + CHANGES LICENSE ... + $ + +Troubleshooting +--------------- + +Swift Mailer does not work when used with function overloading as implemented +by ``mbstring`` (``mbstring.func_overload`` set to ``2``). A workaround is to +temporarily change the internal encoding to ``ASCII`` when sending an email: + +.. code-block:: php + + if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) + { + $mbEncoding = mb_internal_encoding(); + mb_internal_encoding('ASCII'); + } + + // Create your message and send it with Swift Mailer + + if (isset($mbEncoding)) + { + mb_internal_encoding($mbEncoding); + } + +.. _`GitHub`: http://github.com/swiftmailer/swiftmailer diff --git a/vendor/swiftmailer/swiftmailer/doc/introduction.rst b/vendor/swiftmailer/swiftmailer/doc/introduction.rst new file mode 100644 index 0000000000..a85336b7ce --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/introduction.rst @@ -0,0 +1,135 @@ +Introduction +============ + +Swift Mailer is a component-based library for sending e-mails from PHP +applications. + +Organization of this Book +------------------------- + +This book has been written so that those who need information quickly are able +to find what they need, and those who wish to learn more advanced topics can +read deeper into each chapter. + +The book begins with an overview of Swift Mailer, discussing what's included +in the package and preparing you for the remainder of the book. + +It is possible to read this user guide just like any other book (from +beginning to end). Each chapter begins with a discussion of the contents it +contains, followed by a short code sample designed to give you a head start. +As you get further into a chapter you will learn more about Swift Mailer's +capabilities, but often you will be able to head directly to the topic you +wish to learn about. + +Throughout this book you will be presented with code samples, which most +people should find ample to implement Swift Mailer appropriately in their own +projects. We will also use diagrams where appropriate, and where we believe +readers may find it helpful we will discuss some related theory, including +reference to certain documents you are able to find online. + +Code Samples +------------ + +Code samples presented in this book will be displayed on a different colored +background in a monospaced font. Samples are not to be taken as copy & paste +code snippets. + +Code examples are used through the book to clarify what is written in text. +They will sometimes be usable as-is, but they should always be taken as +outline/pseudo code only. + +A code sample will look like this:: + + class AClass + { + ... + } + + // A Comment + $obj = new AClass($arg1, $arg2, ... ); + + /* A note about another way of doing something + $obj = AClass::newInstance($arg1, $arg2, ... ); + + */ + +The presence of 3 dots ``...`` in a code sample indicates that we have left +out a chunk of the code for brevity, they are not actually part of the code. + +We will often place multi-line comments ``/* ... */`` in the code so that we +can show alternative ways of achieving the same result. + +You should read the code examples given and try to understand them. They are +kept concise so that you are not overwhelmed with information. + +History of Swift Mailer +----------------------- + +Swift Mailer began back in 2005 as a one-class project for sending mail over +SMTP. It has since grown into the flexible component-based library that is in +development today. + +Chris Corbyn first posted Swift Mailer on a web forum asking for comments from +other developers. It was never intended as a fully supported open source +project, but members of the forum began to adopt it and make use of it. + +Very quickly feature requests were coming for the ability to add attachments +and use SMTP authentication, along with a number of other "obvious" missing +features. Considering the only alternative was PHPMailer it seemed like a good +time to bring some fresh tools to the table. Chris began working towards a +more component based, PHP5-like approach unlike the existing single-class, +legacy PHP4 approach taken by PHPMailer. + +Members of the forum offered a lot of advice and critique on the code as he +worked through this project and released versions 2 and 3 of the library in +2005 and 2006, which by then had been broken down into smaller classes +offering more flexibility and supporting plugins. To this day the Swift Mailer +team still receive a lot of feature requests from users both on the forum and +in by email. + +Until 2008 Chris was the sole developer of Swift Mailer, but entering 2009 he +gained the support of two experienced developers well-known to him: Paul +Annesley and Christopher Thompson. This has been an extremely welcome change. + +As of September 2009, Chris handed over the maintenance of Swift Mailer to +Fabien Potencier. + +Now 2009 and in its fourth major version Swift Mailer is more object-oriented +and flexible than ever, both from a usability standpoint and from a +development standpoint. + +By no means is Swift Mailer ready to call "finished". There are still many +features that can be added to the library along with the constant refactoring +that happens behind the scenes. + +It's a Library! +--------------- + +Swift Mailer is not an application - it's a library. + +To most experienced developers this is probably an obvious point to make, but +it's certainly worth mentioning. Many people often contact us having gotten +the completely wrong end of the stick in terms of what Swift Mailer is +actually for. + +It's not an application. It does not have a graphical user interface. It +cannot be opened in your web browser directly. + +It's a library (or a framework if you like). It provides a whole lot of +classes that do some very complicated things, so that you don't have to. You +"use" Swift Mailer within an application so that your application can have the +ability to send emails. + +The component-based structure of the library means that you are free to +implement it in a number of different ways and that you can pick and choose +what you want to use. + +An application on the other hand (such as a blog or a forum) is already "put +together" in a particular way, (usually) provides a graphical user interface +and most likely doesn't offer a great deal of integration with your own +application. + +Embrace the structure of the library and use the components it offers to your +advantage. Learning what the components do, rather than blindly copying and +pasting existing code will put you in a great position to build a powerful +application! diff --git a/vendor/swiftmailer/swiftmailer/doc/japanese.rst b/vendor/swiftmailer/swiftmailer/doc/japanese.rst new file mode 100644 index 0000000000..34afa7b8fb --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/japanese.rst @@ -0,0 +1,22 @@ +Using Swift Mailer for Japanese Emails +====================================== + +To send emails in Japanese, you need to tweak the default configuration. + +After requiring the Swift Mailer autoloader (by including the +``swift_required.php`` file), call the ``Swift::init()`` method with the +following code:: + + require_once '/path/to/swift-mailer/lib/swift_required.php'; + + Swift::init(function () { + Swift_DependencyContainer::getInstance() + ->register('mime.qpheaderencoder') + ->asAliasOf('mime.base64headerencoder'); + + Swift_Preferences::getInstance()->setCharset('iso-2022-jp'); + }); + + /* rest of code goes here */ + +That's all! diff --git a/vendor/swiftmailer/swiftmailer/doc/messages.rst b/vendor/swiftmailer/swiftmailer/doc/messages.rst new file mode 100644 index 0000000000..fb5e7fc390 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/messages.rst @@ -0,0 +1,1061 @@ +Creating Messages +================= + +Creating messages in Swift Mailer is done by making use of the various MIME +entities provided with the library. Complex messages can be quickly created +with very little effort. + +Quick Reference for Creating a Message +--------------------------------------- + +You can think of creating a Message as being similar to the steps you perform +when you click the Compose button in your mail client. You give it a subject, +specify some recipients, add any attachments and write your message. + +To create a Message: + +* Call the ``newInstance()`` method of ``Swift_Message``. + +* Set your sender address (``From:``) with ``setFrom()`` or ``setSender()``. + +* Set a subject line with ``setSubject()``. + +* Set recipients with ``setTo()``, ``setCc()`` and/or ``setBcc()``. + +* Set a body with ``setBody()``. + +* Add attachments with ``attach()``. + +.. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the message + $message = Swift_Message::newInstance() + + // Give the message a subject + ->setSubject('Your subject') + + // Set the From address with an associative array + ->setFrom(array('john@doe.com' => 'John Doe')) + + // Set the To addresses with an associative array + ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name')) + + // Give it a body + ->setBody('Here is the message itself') + + // And optionally an alternative body + ->addPart('Here is the message itself', 'text/html') + + // Optionally add any attachments + ->attach(Swift_Attachment::fromPath('my-document.pdf')) + ; + +Message Basics +-------------- + +A message is a container for anything you want to send to somebody else. There +are several basic aspects of a message that you should know. + +An e-mail message is made up of several relatively simple entities that are +combined in different ways to achieve different results. All of these entities +have the same fundamental outline but serve a different purpose. The Message +itself can be defined as a MIME entity, an Attachment is a MIME entity, all +MIME parts are MIME entities -- and so on! + +The basic units of each MIME entity -- be it the Message itself, or an +Attachment -- are its Headers and its body: + +.. code-block:: text + + Header-Name: A header value + Other-Header: Another value + + The body content itself + +The Headers of a MIME entity, and its body must conform to some strict +standards defined by various RFC documents. Swift Mailer ensures that these +specifications are followed by using various types of object, including +Encoders and different Header types to generate the entity. + +The Structure of a Message +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Of all of the MIME entities, a message -- ``Swift_Message`` +is the largest and most complex. It has many properties that can be updated +and it can contain other MIME entities -- attachments for example -- +nested inside it. + +A Message has a lot of different Headers which are there to present +information about the message to the recipients' mail client. Most of these +headers will be familiar to the majority of users, but we'll list the basic +ones. Although it's possible to work directly with the Headers of a Message +(or other MIME entity), the standard Headers have accessor methods provided to +abstract away the complex details for you. For example, although the Date on a +message is written with a strict format, you only need to pass a UNIX +timestamp to ``setDate()``. + ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| Header | Description | Accessors | ++===============================+====================================================================================================================================+=============================================+ +| ``Message-ID`` | Identifies this message with a unique ID, usually containing the domain name and time generated | ``getId()`` / ``setId()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Return-Path`` | Specifies where bounces should go (Swift Mailer reads this for other uses) | ``getReturnPath()`` / ``setReturnPath()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``From`` | Specifies the address of the person who the message is from. This can be multiple addresses if multiple people wrote the message. | ``getFrom()`` / ``setFrom()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Sender`` | Specifies the address of the person who physically sent the message (higher precedence than ``From:``) | ``getSender()`` / ``setSender()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``To`` | Specifies the addresses of the intended recipients | ``getTo()`` / ``setTo()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Cc`` | Specifies the addresses of recipients who will be copied in on the message | ``getCc()`` / ``setCc()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Bcc`` | Specifies the addresses of recipients who the message will be blind-copied to. Other recipients will not be aware of these copies. | ``getBcc()`` / ``setBcc()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Reply-To`` | Specifies the address where replies are sent to | ``getReplyTo()`` / ``setReplyTo()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Subject`` | Specifies the subject line that is displayed in the recipients' mail client | ``getSubject()`` / ``setSubject()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Date`` | Specifies the date at which the message was sent | ``getDate()`` / ``setDate()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Content-Type`` | Specifies the format of the message (usually text/plain or text/html) | ``getContentType()`` / ``setContentType()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Content-Transfer-Encoding`` | Specifies the encoding scheme in the message | ``getEncoder()`` / ``setEncoder()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ + +Working with a Message Object +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Although there are a lot of available methods on a message object, you only +need to make use of a small subset of them. Usually you'll use +``setSubject()``, ``setTo()`` and +``setFrom()`` before setting the body of your message with +``setBody()``. + +Calling methods is simple. You just call them like functions, but using the +object operator "``->``" to do so. If you've created +a message object and called it ``$message`` then you'd set a +subject on it like so: + +.. code-block:: php + + require_once 'lib/swift_required.php'; + + $message = Swift_Message::newInstance(); + $message->setSubject('My subject'); + +All MIME entities (including a message) have a ``toString()`` +method that you can call if you want to take a look at what is going to be +sent. For example, if you ``echo +$message->toString();`` you would see something like this: + +.. code-block:: bash + + Message-ID: <1230173678.4952f5eeb1432@swift.generated> + Date: Thu, 25 Dec 2008 13:54:38 +1100 + Subject: Example subject + From: Chris Corbyn + To: Receiver Name + MIME-Version: 1.0 + Content-Type: text/plain; charset=utf-8 + Content-Transfer-Encoding: quoted-printable + + Here is the message + +We'll take a closer look at the methods you use to create your message in the +following sections. + +Adding Content to Your Message +------------------------------ + +Rich content can be added to messages in Swift Mailer with relative ease by +calling methods such as ``setSubject()``, ``setBody()``, ``addPart()`` and +``attach()``. + +Setting the Subject Line +~~~~~~~~~~~~~~~~~~~~~~~~ + +The subject line, displayed in the recipients' mail client can be set with the +``setSubject()`` method, or as a parameter to ``Swift_Message::newInstance()``. + +To set the subject of your Message: + +* Call the ``setSubject()`` method of the Message, or specify it at the time + you create the message. + + .. code-block:: php + + // Pass it as a parameter when you create the message + $message = Swift_Message::newInstance('My amazing subject'); + + // Or set it after like this + $message->setSubject('My amazing subject'); + +Setting the Body Content +~~~~~~~~~~~~~~~~~~~~~~~~ + +The body of the message -- seen when the user opens the message -- +is specified by calling the ``setBody()`` method. If an alternative body is to +be included ``addPart()`` can be used. + +The body of a message is the main part that is read by the user. Often people +want to send a message in HTML format (``text/html``), other +times people want to send in plain text (``text/plain``), or +sometimes people want to send both versions and allow the recipient to choose +how they view the message. + +As a rule of thumb, if you're going to send a HTML email, always include a +plain-text equivalent of the same content so that users who prefer to read +plain text can do so. + +To set the body of your Message: + +* Call the ``setBody()`` method of the Message, or specify it at the time you + create the message. + +* Add any alternative bodies with ``addPart()``. + +If the recipient's mail client offers preferences for displaying text vs. HTML +then the mail client will present that part to the user where available. In +other cases the mail client will display the "best" part it can - usually HTML +if you've included HTML. + +.. code-block:: php + + // Pass it as a parameter when you create the message + $message = Swift_Message::newInstance('Subject here', 'My amazing body'); + + // Or set it after like this + $message->setBody('My amazing body', 'text/html'); + + // Add alternative parts with addPart() + $message->addPart('My amazing body in plain text', 'text/plain'); + +Attaching Files +--------------- + +Attachments are downloadable parts of a message and can be added by calling +the ``attach()`` method on the message. You can add attachments that exist on +disk, or you can create attachments on-the-fly. + +Attachments are actually an interesting area of Swift Mailer and something +that could put a lot of power at your fingertips if you grasp the concept +behind the way a message is held together. + +Although we refer to files sent over e-mails as "attachments" -- because +they're attached to the message -- lots of other parts of the message are +actually "attached" even if we don't refer to these parts as attachments. + +File attachments are created by the ``Swift_Attachment`` class +and then attached to the message via the ``attach()`` method on +it. For all of the "every day" MIME types such as all image formats, word +documents, PDFs and spreadsheets you don't need to explicitly set the +content-type of the attachment, though it would do no harm to do so. For less +common formats you should set the content-type -- which we'll cover in a +moment. + +Attaching Existing Files +~~~~~~~~~~~~~~~~~~~~~~~~ + +Files that already exist, either on disk or at a URL can be attached to a +message with just one line of code, using ``Swift_Attachment::fromPath()``. + +You can attach files that exist locally, or if your PHP installation has +``allow_url_fopen`` turned on you can attach files from other +websites. + +To attach an existing file: + +* Create an attachment with ``Swift_Attachment::fromPath()``. + +* Add the attachment to the message with ``attach()``. + +The attachment will be presented to the recipient as a downloadable file with +the same filename as the one you attached. + +.. code-block:: php + + // Create the attachment + // * Note that you can technically leave the content-type parameter out + $attachment = Swift_Attachment::fromPath('/path/to/image.jpg', 'image/jpeg'); + + // Attach it to the message + $message->attach($attachment); + + + // The two statements above could be written in one line instead + $message->attach(Swift_Attachment::fromPath('/path/to/image.jpg')); + + + // You can attach files from a URL if allow_url_fopen is on in php.ini + $message->attach(Swift_Attachment::fromPath('http://site.tld/logo.png')); + +Setting the Filename +~~~~~~~~~~~~~~~~~~~~ + +Usually you don't need to explicitly set the filename of an attachment because +the name of the attached file will be used by default, but if you want to set +the filename you use the ``setFilename()`` method of the Attachment. + +To change the filename of an attachment: + +* Call its ``setFilename()`` method. + +The attachment will be attached in the normal way, but meta-data sent inside +the email will rename the file to something else. + +.. code-block:: php + + // Create the attachment and call its setFilename() method + $attachment = Swift_Attachment::fromPath('/path/to/image.jpg') + ->setFilename('cool.jpg'); + + + // Because there's a fluid interface, you can do this in one statement + $message->attach( + Swift_Attachment::fromPath('/path/to/image.jpg')->setFilename('cool.jpg') + ); + +Attaching Dynamic Content +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Files that are generated at runtime, such as PDF documents or images created +via GD can be attached directly to a message without writing them out to disk. +Use the standard ``Swift_Attachment::newInstance()`` method. + +To attach dynamically created content: + +* Create your content as you normally would. + +* Create an attachment with ``Swift_Attachment::newInstance()``, specifying + the source data of your content along with a name and the content-type. + +* Add the attachment to the message with ``attach()``. + +The attachment will be presented to the recipient as a downloadable file +with the filename and content-type you specify. + +.. note:: + + If you would usually write the file to disk anyway you should just attach + it with ``Swift_Attachment::fromPath()`` since this will use less memory: + + .. code-block:: php + + // Create your file contents in the normal way, but don't write them to disk + $data = create_my_pdf_data(); + + // Create the attachment with your data + $attachment = Swift_Attachment::newInstance($data, 'my-file.pdf', 'application/pdf'); + + // Attach it to the message + $message->attach($attachment); + + + // You can alternatively use method chaining to build the attachment + $attachment = Swift_Attachment::newInstance() + ->setFilename('my-file.pdf') + ->setContentType('application/pdf') + ->setBody($data) + ; + +Changing the Disposition +~~~~~~~~~~~~~~~~~~~~~~~~ + +Attachments just appear as files that can be saved to the Desktop if desired. +You can make attachment appear inline where possible by using the +``setDisposition()`` method of an attachment. + +To make an attachment appear inline: + +* Call its ``setDisposition()`` method. + +The attachment will be displayed within the email viewing window if the mail +client knows how to display it. + +.. note:: + + If you try to create an inline attachment for a non-displayable file type + such as a ZIP file, the mail client should just present the attachment as + normal: + + .. code-block:: php + + // Create the attachment and call its setDisposition() method + $attachment = Swift_Attachment::fromPath('/path/to/image.jpg') + ->setDisposition('inline'); + + + // Because there's a fluid interface, you can do this in one statement + $message->attach( + Swift_Attachment::fromPath('/path/to/image.jpg')->setDisposition('inline') + ); + +Embedding Inline Media Files +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Often people want to include an image or other content inline with a HTML +message. It's easy to do this with HTML linking to remote resources, but this +approach is usually blocked by mail clients. Swift Mailer allows you to embed +your media directly into the message. + +Mail clients usually block downloads from remote resources because this +technique was often abused as a mean of tracking who opened an email. If +you're sending a HTML email and you want to include an image in the message +another approach you can take is to embed the image directly. + +Swift Mailer makes embedding files into messages extremely streamlined. You +embed a file by calling the ``embed()`` method of the message, +which returns a value you can use in a ``src`` or +``href`` attribute in your HTML. + +Just like with attachments, it's possible to embed dynamically generated +content without having an existing file available. + +The embedded files are sent in the email as a special type of attachment that +has a unique ID used to reference them within your HTML attributes. On mail +clients that do not support embedded files they may appear as attachments. + +Although this is commonly done for images, in theory it will work for any +displayable (or playable) media type. Support for other media types (such as +video) is dependent on the mail client however. + +Embedding Existing Files +........................ + +Files that already exist, either on disk or at a URL can be embedded in a +message with just one line of code, using ``Swift_EmbeddedFile::fromPath()``. + +You can embed files that exist locally, or if your PHP installation has +``allow_url_fopen`` turned on you can embed files from other websites. + +To embed an existing file: + +* Create a message object with ``Swift_Message::newInstance()``. + +* Set the body as HTML, and embed a file at the correct point in the message with ``embed()``. + +The file will be displayed with the message inline with the HTML wherever its ID +is used as a ``src`` attribute. + +.. note:: + + ``Swift_Image`` and ``Swift_EmbeddedFile`` are just aliases of one + another. ``Swift_Image`` exists for semantic purposes. + +.. note:: + + You can embed files in two stages if you prefer. Just capture the return + value of ``embed()`` in a variable and use that as the ``src`` attribute. + + .. code-block:: php + + // Create the message + $message = Swift_Message::newInstance('My subject'); + + // Set the body + $message->setBody( + '' . + ' ' . + ' ' . + ' Here is an image Image' . + ' Rest of message' . + ' ' . + '', + 'text/html' // Mark the content-type as HTML + ); + + // You can embed files from a URL if allow_url_fopen is on in php.ini + $message->setBody( + '' . + ' ' . + ' ' . + ' Here is an image Image' . + ' Rest of message' . + ' ' . + '', + 'text/html' + ); + + + // If placing the embed() code inline becomes cumbersome + // it's easy to do this in two steps + $cid = $message->embed(Swift_Image::fromPath('image.png')); + + $message->setBody( + '' . + ' ' . + ' ' . + ' Here is an image Image' . + ' Rest of message' . + ' ' . + '', + 'text/html' // Mark the content-type as HTML + ); + +Embedding Dynamic Content +......................... + +Images that are generated at runtime, such as images created via GD can be +embedded directly to a message without writing them out to disk. Use the +standard ``Swift_Image::newInstance()`` method. + +To embed dynamically created content: + +* Create a message object with ``Swift_Message::newInstance()``. + +* Set the body as HTML, and embed a file at the correct point in the message + with ``embed()``. You will need to specify a filename and a content-type. + +The file will be displayed with the message inline with the HTML wherever its ID +is used as a ``src`` attribute. + +.. note:: + + ``Swift_Image`` and ``Swift_EmbeddedFile`` are just aliases of one + another. ``Swift_Image`` exists for semantic purposes. + +.. note:: + + You can embed files in two stages if you prefer. Just capture the return + value of ``embed()`` in a variable and use that as the ``src`` attribute. + + .. code-block:: php + + // Create your file contents in the normal way, but don't write them to disk + $img_data = create_my_image_data(); + + // Create the message + $message = Swift_Message::newInstance('My subject'); + + // Set the body + $message->setBody( + '' . + ' ' . + ' ' . + ' Here is an image Image' . + ' Rest of message' . + ' ' . + '', + 'text/html' // Mark the content-type as HTML + ); + + + // If placing the embed() code inline becomes cumbersome + // it's easy to do this in two steps + $cid = $message->embed(Swift_Image::newInstance($img_data, 'image.jpg', 'image/jpeg')); + + $message->setBody( + '' . + ' ' . + ' ' . + ' Here is an image Image' . + ' Rest of message' . + ' ' . + '', + 'text/html' // Mark the content-type as HTML + ); + +Adding Recipients to Your Message +--------------------------------- + +Recipients are specified within the message itself via ``setTo()``, ``setCc()`` +and ``setBcc()``. Swift Mailer reads these recipients from the message when it +gets sent so that it knows where to send the message to. + +Message recipients are one of three types: + +* ``To:`` recipients -- the primary recipients (required) + +* ``Cc:`` recipients -- receive a copy of the message (optional) + +* ``Bcc:`` recipients -- hidden from other recipients (optional) + +Each type can contain one, or several addresses. It's possible to list only +the addresses of the recipients, or you can personalize the address by +providing the real name of the recipient. + +Make sure to add only valid email addresses as recipients. If you try to add an +invalid email address with ``setTo()``, ``setCc()`` or ``setBcc()``, Swift +Mailer will throw a ``Swift_RfcComplianceException``. + +If you add recipients automatically based on a data source that may contain +invalid email addresses, you can prevent possible exceptions by validating the +addresses using ``Swift_Validate::email($email)`` and only adding addresses +that validate. Another way would be to wrap your ``setTo()``, ``setCc()`` and +``setBcc()`` calls in a try-catch block and handle the +``Swift_RfcComplianceException`` in the catch block. + +.. sidebar:: Syntax for Addresses + + If you only wish to refer to a single email address (for example your + ``From:`` address) then you can just use a string. + + .. code-block:: php + + $message->setFrom('some@address.tld'); + + If you want to include a name then you must use an associative array. + + .. code-block:: php + + $message->setFrom(array('some@address.tld' => 'The Name')); + + If you want to include multiple addresses then you must use an array. + + .. code-block:: php + + $message->setTo(array('some@address.tld', 'other@address.tld')); + + You can mix personalized (addresses with a name) and non-personalized + addresses in the same list by mixing the use of associative and + non-associative array syntax. + + .. code-block:: php + + $message->setTo(array( + 'recipient-with-name@example.org' => 'Recipient Name One', + 'no-name@example.org', // Note that this is not a key-value pair + 'named-recipient@example.org' => 'Recipient Name Two' + )); + +Setting ``To:`` Recipients +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``To:`` recipients are required in a message and are set with the +``setTo()`` or ``addTo()`` methods of the message. + +To set ``To:`` recipients, create the message object using either +``new Swift_Message( ... )`` or ``Swift_Message::newInstance( ... )``, +then call the ``setTo()`` method with a complete array of addresses, or use the +``addTo()`` method to iteratively add recipients. + +The ``setTo()`` method accepts input in various formats as described earlier in +this chapter. The ``addTo()`` method takes either one or two parameters. The +first being the email address and the second optional parameter being the name +of the recipient. + +``To:`` recipients are visible in the message headers and will be +seen by the other recipients. + +.. note:: + + Multiple calls to ``setTo()`` will not add new recipients -- each + call overrides the previous calls. If you want to iteratively add + recipients, use the ``addTo()`` method. + + .. code-block:: php + + // Using setTo() to set all recipients in one go + $message->setTo(array( + 'person1@example.org', + 'person2@otherdomain.org' => 'Person 2 Name', + 'person3@example.org', + 'person4@example.org', + 'person5@example.org' => 'Person 5 Name' + )); + + // Using addTo() to add recipients iteratively + $message->addTo('person1@example.org'); + $message->addTo('person2@example.org', 'Person 2 Name'); + +Setting ``Cc:`` Recipients +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``Cc:`` recipients are set with the ``setCc()`` or ``addCc()`` methods of the +message. + +To set ``Cc:`` recipients, create the message object using either +``new Swift_Message( ... )`` or ``Swift_Message::newInstance( ... )``, then call +the ``setCc()`` method with a complete array of addresses, or use the +``addCc()`` method to iteratively add recipients. + +The ``setCc()`` method accepts input in various formats as described earlier in +this chapter. The ``addCc()`` method takes either one or two parameters. The +first being the email address and the second optional parameter being the name +of the recipient. + +``Cc:`` recipients are visible in the message headers and will be +seen by the other recipients. + +.. note:: + + Multiple calls to ``setCc()`` will not add new recipients -- each + call overrides the previous calls. If you want to iteratively add Cc: + recipients, use the ``addCc()`` method. + + .. code-block:: php + + // Using setCc() to set all recipients in one go + $message->setCc(array( + 'person1@example.org', + 'person2@otherdomain.org' => 'Person 2 Name', + 'person3@example.org', + 'person4@example.org', + 'person5@example.org' => 'Person 5 Name' + )); + + // Using addCc() to add recipients iteratively + $message->addCc('person1@example.org'); + $message->addCc('person2@example.org', 'Person 2 Name'); + +Setting ``Bcc:`` Recipients +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``Bcc:`` recipients receive a copy of the message without anybody else knowing +it, and are set with the ``setBcc()`` or ``addBcc()`` methods of the message. + +To set ``Bcc:`` recipients, create the message object using either ``new +Swift_Message( ... )`` or ``Swift_Message::newInstance( ... )``, then call the +``setBcc()`` method with a complete array of addresses, or use +the ``addBcc()`` method to iteratively add recipients. + +The ``setBcc()`` method accepts input in various formats as described earlier in +this chapter. The ``addBcc()`` method takes either one or two parameters. The +first being the email address and the second optional parameter being the name +of the recipient. + +Only the individual ``Bcc:`` recipient will see their address in the message +headers. Other recipients (including other ``Bcc:`` recipients) will not see the +address. + +.. note:: + + Multiple calls to ``setBcc()`` will not add new recipients -- each + call overrides the previous calls. If you want to iteratively add Bcc: + recipients, use the ``addBcc()`` method. + + .. code-block:: php + + // Using setBcc() to set all recipients in one go + $message->setBcc(array( + 'person1@example.org', + 'person2@otherdomain.org' => 'Person 2 Name', + 'person3@example.org', + 'person4@example.org', + 'person5@example.org' => 'Person 5 Name' + )); + + // Using addBcc() to add recipients iteratively + $message->addBcc('person1@example.org'); + $message->addBcc('person2@example.org', 'Person 2 Name'); + +Specifying Sender Details +------------------------- + +An email must include information about who sent it. Usually this is managed +by the ``From:`` address, however there are other options. + +The sender information is contained in three possible places: + +* ``From:`` -- the address(es) of who wrote the message (required) + +* ``Sender:`` -- the address of the single person who sent the message + (optional) + +* ``Return-Path:`` -- the address where bounces should go to (optional) + +You must always include a ``From:`` address by using ``setFrom()`` on the +message. Swift Mailer will use this as the default ``Return-Path:`` unless +otherwise specified. + +The ``Sender:`` address exists because the person who actually sent the email +may not be the person who wrote the email. It has a higher precedence than the +``From:`` address and will be used as the ``Return-Path:`` unless otherwise +specified. + +Setting the ``From:`` Address +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A ``From:`` address is required and is set with the ``setFrom()`` method of the +message. ``From:`` addresses specify who actually wrote the email, and usually who sent it. + +What most people probably don't realise is that you can have more than one +``From:`` address if more than one person wrote the email -- for example if an +email was put together by a committee. + +To set the ``From:`` address(es): + +* Call the ``setFrom()`` method on the Message. + +The ``From:`` address(es) are visible in the message headers and +will be seen by the recipients. + +.. note:: + + If you set multiple ``From:`` addresses then you absolutely must set a + ``Sender:`` address to indicate who physically sent the message. + + .. code-block:: php + + // Set a single From: address + $message->setFrom('your@address.tld'); + + // Set a From: address including a name + $message->setFrom(array('your@address.tld' => 'Your Name')); + + // Set multiple From: addresses if multiple people wrote the email + $message->setFrom(array( + 'person1@example.org' => 'Sender One', + 'person2@example.org' => 'Sender Two' + )); + +Setting the ``Sender:`` Address +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A ``Sender:`` address specifies who sent the message and is set with the +``setSender()`` method of the message. + +To set the ``Sender:`` address: + +* Call the ``setSender()`` method on the Message. + +The ``Sender:`` address is visible in the message headers and will be seen by +the recipients. + +This address will be used as the ``Return-Path:`` unless otherwise specified. + +.. note:: + + If you set multiple ``From:`` addresses then you absolutely must set a + ``Sender:`` address to indicate who physically sent the message. + +You must not set more than one sender address on a message because it's not +possible for more than one person to send a single message. + +.. code-block:: php + + $message->setSender('your@address.tld'); + +Setting the ``Return-Path:`` (Bounce) Address +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``Return-Path:`` address specifies where bounce notifications should +be sent and is set with the ``setReturnPath()`` method of the message. + +You can only have one ``Return-Path:`` and it must not include +a personal name. + +To set the ``Return-Path:`` address: + +* Call the ``setReturnPath()`` method on the Message. + +Bounce notifications will be sent to this address. + +.. code-block:: php + + $message->setReturnPath('bounces@address.tld'); + + +Signed/Encrypted Message +------------------------ + +To increase the integrity/security of a message it is possible to sign and/or +encrypt an message using one or multiple signers. + +S/MIME +~~~~~~ + +S/MIME can sign and/or encrypt a message using the OpenSSL extension. + +When signing a message, the signer creates a signature of the entire content of the message (including attachments). + +The certificate and private key must be PEM encoded, and can be either created using for example OpenSSL or +obtained at an official Certificate Authority (CA). + +**The recipient must have the CA certificate in the list of trusted issuers in order to verify the signature.** + +**Make sure the certificate supports emailProtection.** + +When using OpenSSL this can done by the including the *-addtrust emailProtection* parameter when creating the certificate. + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + $smimeSigner = Swift_Signers_SMimeSigner::newInstance(); + $smimeSigner->setSignCertificate('/path/to/certificate.pem', '/path/to/private-key.pem'); + $message->attachSigner($smimeSigner); + +When the private key is secured using a passphrase use the following instead. + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + $smimeSigner = Swift_Signers_SMimeSigner::newInstance(); + $smimeSigner->setSignCertificate('/path/to/certificate.pem', array('/path/to/private-key.pem', 'passphrase')); + $message->attachSigner($smimeSigner); + +By default the signature is added as attachment, +making the message still readable for mailing agents not supporting signed messages. + +Storing the message as binary is also possible but not recommended. + +.. code-block:: php + + $smimeSigner->setSignCertificate('/path/to/certificate.pem', '/path/to/private-key.pem', PKCS7_BINARY); + +When encrypting the message (also known as enveloping), the entire message (including attachments) +is encrypted using a certificate, and the recipient can then decrypt the message using corresponding private key. + +Encrypting ensures nobody can read the contents of the message without the private key. + +Normally the recipient provides a certificate for encrypting and keeping the decryption key private. + +Using both signing and encrypting is also possible. + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + $smimeSigner = Swift_Signers_SMimeSigner::newInstance(); + $smimeSigner->setSignCertificate('/path/to/sign-certificate.pem', '/path/to/private-key.pem'); + $smimeSigner->setEncryptCertificate('/path/to/encrypt-certificate.pem'); + $message->attachSigner($smimeSigner); + +The used encryption cipher can be set as the second parameter of setEncryptCertificate() + +See http://php.net/manual/openssl.ciphers for a list of supported ciphers. + +By default the message is first signed and then encrypted, this can be changed by adding. + +.. code-block:: php + + $smimeSigner->setSignThenEncrypt(false); + +**Changing this is not recommended as most mail agents don't support this none-standard way.** + +Only when having trouble with sign then encrypt method, this should be changed. + +Requesting a Read Receipt +------------------------- + +It is possible to request a read-receipt to be sent to an address when the +email is opened. To request a read receipt set the address with +``setReadReceiptTo()``. + +To request a read receipt: + +* Set the address you want the receipt to be sent to with the + ``setReadReceiptTo()`` method on the Message. + +When the email is opened, if the mail client supports it a notification will be sent to this address. + +.. note:: + + Read receipts won't work for the majority of recipients since many mail + clients auto-disable them. Those clients that will send a read receipt + will make the user aware that one has been requested. + + .. code-block:: php + + $message->setReadReceiptTo('your@address.tld'); + +Setting the Character Set +------------------------- + +The character set of the message (and it's MIME parts) is set with the +``setCharset()`` method. You can also change the global default of UTF-8 by +working with the ``Swift_Preferences`` class. + +Swift Mailer will default to the UTF-8 character set unless otherwise +overridden. UTF-8 will work in most instances since it includes all of the +standard US keyboard characters in addition to most international characters. + +It is absolutely vital however that you know what character set your message +(or it's MIME parts) are written in otherwise your message may be received +completely garbled. + +There are two places in Swift Mailer where you can change the character set: + +* In the ``Swift_Preferences`` class + +* On each individual message and/or MIME part + +To set the character set of your Message: + +* Change the global UTF-8 setting by calling + ``Swift_Preferences::setCharset()``; or + +* Call the ``setCharset()`` method on the message or the MIME part. + + .. code-block:: php + + // Approach 1: Change the global setting (suggested) + Swift_Preferences::getInstance()->setCharset('iso-8859-2'); + + // Approach 2: Call the setCharset() method of the message + $message = Swift_Message::newInstance() + ->setCharset('iso-8859-2'); + + // Approach 3: Specify the charset when setting the body + $message->setBody('My body', 'text/html', 'iso-8859-2'); + + // Approach 4: Specify the charset for each part added + $message->addPart('My part', 'text/plain', 'iso-8859-2'); + +Setting the Line Length +----------------------- + +The length of lines in a message can be changed by using the ``setMaxLineLength()`` method on the message. It should be kept to less than +1000 characters. + +Swift Mailer defaults to using 78 characters per line in a message. This is +done for historical reasons and so that the message can be easily viewed in +plain-text terminals. + +To change the maximum length of lines in your Message: + +* Call the ``setMaxLineLength()`` method on the Message. + +Lines that are longer than the line length specified will be wrapped between +words. + +.. note:: + + You should never set a maximum length longer than 1000 characters + according to RFC 2822. Doing so could have unspecified side-effects such + as truncating parts of your message when it is transported between SMTP + servers. + + .. code-block:: php + + $message->setMaxLineLength(1000); + +Setting the Message Priority +---------------------------- + +You can change the priority of the message with ``setPriority()``. Setting the +priority will not change the way your email is sent -- it is purely an +indicative setting for the recipient. + +The priority of a message is an indication to the recipient what significance +it has. Swift Mailer allows you to set the priority by calling the +``setPriority`` method. This method takes an integer value between 1 and 5: + +* `Swift_Mime_SimpleMessage::PRIORITY_HIGHEST`: 1 +* `Swift_Mime_SimpleMessage::PRIORITY_HIGH`: 2 +* `Swift_Mime_SimpleMessage::PRIORITY_NORMAL`: 3 +* `Swift_Mime_SimpleMessage::PRIORITY_LOW`: 4 +* `Swift_Mime_SimpleMessage::PRIORITY_LOWEST`: 5 + +To set the message priority: + +* Set the priority as an integer between 1 and 5 with the ``setPriority()`` + method on the Message. + +.. code-block:: php + + // Indicate "High" priority + $message->setPriority(2); + + // Or use the constant to be more explicit + $message->setPriority(Swift_Mime_SimpleMessage::PRIORITY_HIGH); diff --git a/vendor/swiftmailer/swiftmailer/doc/overview.rst b/vendor/swiftmailer/swiftmailer/doc/overview.rst new file mode 100644 index 0000000000..ebfe008360 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/overview.rst @@ -0,0 +1,159 @@ +Library Overview +================ + +Most features (and more) of your every day mail client software are provided +by Swift Mailer, using object-oriented PHP code as the interface. + +In this chapter we will take a short tour of the various components, which put +together form the Swift Mailer library as a whole. You will learn key +terminology used throughout the rest of this book and you will gain a little +understanding of the classes you will work with as you integrate Swift Mailer +into your application. + +This chapter is intended to prepare you for the information contained in the +subsequent chapters of this book. You may choose to skip this chapter if you +are fairly technically minded, though it is likely to save you some time in +the long run if you at least read between the lines here. + +System Requirements +------------------- + +The basic requirements to operate Swift Mailer are extremely minimal and +easily achieved. Historically, Swift Mailer has supported both PHP 4 and PHP 5 +by following a parallel development workflow. Now in it's fourth major +version, and Swift Mailer operates on servers running PHP 5.3.3 or higher. + +The library aims to work with as many PHP 5 projects as possible: + +* PHP 5.3.3 or higher, with the SPL extension (standard) + +* Limited network access to connect to remote SMTP servers + +* 8 MB or more memory limit (Swift Mailer uses around 2 MB) + +Component Breakdown +------------------- + +Swift Mailer is made up of many classes. Each of these classes can be grouped +into a general "component" group which describes the task it is designed to +perform. + +We'll take a brief look at the components which form Swift Mailer in this +section of the book. + +The Mailer +~~~~~~~~~~ + +The mailer class, ``Swift_Mailer`` is the central class in the library where +all of the other components meet one another. ``Swift_Mailer`` acts as a sort +of message dispatcher, communicating with the underlying Transport to deliver +your Message to all intended recipients. + +If you were to dig around in the source code for Swift Mailer you'd notice +that ``Swift_Mailer`` itself is pretty bare. It delegates to other objects for +most tasks and in theory, if you knew the internals of Swift Mailer well you +could by-pass this class entirely. We wouldn't advise doing such a thing +however -- there are reasons this class exists: + +* for consistency, regardless of the Transport used + +* to provide abstraction from the internals in the event internal API changes + are made + +* to provide convenience wrappers around aspects of the internal API + +An instance of ``Swift_Mailer`` is created by the developer before sending any +Messages. + +Transports +~~~~~~~~~~ + +Transports are the classes in Swift Mailer that are responsible for +communicating with a service in order to deliver a Message. There are several +types of Transport in Swift Mailer, all of which implement the Swift_Transport +interface and offer underlying start(), stop() and send() methods. + +Typically you will not need to know how a Transport works under-the-surface, +you will only need to know how to create an instance of one, and which one to +use for your environment. + ++---------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------+ +| Class | Features | Pros/cons | ++=================================+=============================================================================================+===============================================================================================================================================+ +| ``Swift_SmtpTransport`` | Sends messages over SMTP; Supports Authentication; Supports Encryption | Very portable; Pleasingly predictable results; Provides good feedback | ++---------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------+ +| ``Swift_SendmailTransport`` | Communicates with a locally installed ``sendmail`` executable (Linux/UNIX) | Quick time-to-run; Provides less-accurate feedback than SMTP; Requires ``sendmail`` installation | ++---------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------+ +| ``Swift_LoadBalancedTransport`` | Cycles through a collection of the other Transports to manage load-reduction | Provides graceful fallback if one Transport fails (e.g. an SMTP server is down); Keeps the load on remote services down by spreading the work | ++---------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------+ +| ``Swift_FailoverTransport`` | Works in conjunction with a collection of the other Transports to provide high-availability | Provides graceful fallback if one Transport fails (e.g. an SMTP server is down) | ++---------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------+ + +MIME Entities +~~~~~~~~~~~~~ + +Everything that forms part of a Message is called a MIME Entity. All MIME +entities in Swift Mailer share a common set of features. There are various +types of MIME entity that serve different purposes such as Attachments and +MIME parts. + +An e-mail message is made up of several relatively simple entities that are +combined in different ways to achieve different results. All of these entities +have the same fundamental outline but serve a different purpose. The Message +itself can be defined as a MIME entity, an Attachment is a MIME entity, all +MIME parts are MIME entities -- and so on! + +The basic units of each MIME entity -- be it the Message itself, or an +Attachment -- are its Headers and its body: + +.. code-block:: text + + Other-Header: Another value + + The body content itself + +The Headers of a MIME entity, and its body must conform to some strict +standards defined by various RFC documents. Swift Mailer ensures that these +specifications are followed by using various types of object, including +Encoders and different Header types to generate the entity. + +Each MIME component implements the base ``Swift_Mime_MimeEntity`` interface, +which offers methods for retrieving Headers, adding new Headers, changing the +Encoder, updating the body and so on! + +All MIME entities have one Header in common -- the Content-Type Header, +updated with the entity's ``setContentType()`` method. + +Encoders +~~~~~~~~ + +Encoders are used to transform the content of Messages generated in Swift +Mailer into a format that is safe to send across the internet and that +conforms to RFC specifications. + +Generally speaking you will not need to interact with the Encoders in Swift +Mailer -- the correct settings will be handled by the library itself. +However they are probably worth a brief mention in the event that you do want +to play with them. + +Both the Headers and the body of all MIME entities (including the Message +itself) use Encoders to ensure the data they contain can be sent over the +internet without becoming corrupted or misinterpreted. + +There are two types of Encoder: Base64 and Quoted-Printable. + +Plugins +~~~~~~~ + +Plugins exist to extend, or modify the behaviour of Swift Mailer. They respond +to Events that are fired within the Transports during sending. + +There are a number of Plugins provided as part of the base Swift Mailer +package and they all follow a common interface to respond to Events fired +within the library. Interfaces are provided to "listen" to each type of Event +fired and to act as desired when a listened-to Event occurs. + +Although several plugins are provided with Swift Mailer out-of-the-box, the +Events system has been specifically designed to make it easy for experienced +object-oriented developers to write their own plugins in order to achieve +goals that may not be possible with the base library. diff --git a/vendor/swiftmailer/swiftmailer/doc/plugins.rst b/vendor/swiftmailer/swiftmailer/doc/plugins.rst new file mode 100644 index 0000000000..16ae3356f3 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/plugins.rst @@ -0,0 +1,385 @@ +Plugins +======= + +Plugins are provided with Swift Mailer and can be used to extend the behavior +of the library in situations where using simple class inheritance would be more complex. + +AntiFlood Plugin +---------------- + +Many SMTP servers have limits on the number of messages that may be sent +during any single SMTP connection. The AntiFlood plugin provides a way to stay +within this limit while still managing a large number of emails. + +A typical limit for a single connection is 100 emails. If the server you +connect to imposes such a limit, it expects you to disconnect after that +number of emails has been sent. You could manage this manually within a loop, +but the AntiFlood plugin provides the necessary wrapper code so that you don't +need to worry about this logic. + +Regardless of limits imposed by the server, it's usually a good idea to be +conservative with the resources of the SMTP server. Sending will become +sluggish if the server is being over-used so using the AntiFlood plugin will +not be a bad idea even if no limits exist. + +The AntiFlood plugin's logic is basically to disconnect and the immediately +re-connect with the SMTP server every X number of emails sent, where X is a +number you specify to the plugin. + +You can also specify a time period in seconds that Swift Mailer should pause +for between the disconnect/re-connect process. It's a good idea to pause for a +short time (say 30 seconds every 100 emails) simply to give the SMTP server a +chance to process its queue and recover some resources. + +Using the AntiFlood Plugin +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The AntiFlood Plugin -- like all plugins -- is added with the Mailer class's +``registerPlugin()`` method. It takes two constructor parameters: the number of +emails to pause after, and optionally the number of seconds to pause for. + +To use the AntiFlood plugin: + +* Create an instance of the Mailer using any Transport you choose. + +* Create an instance of the ``Swift_Plugins_AntiFloodPlugin`` class, passing + in one or two constructor parameters. + +* Register the plugin using the Mailer's ``registerPlugin()`` method. + +* Continue using Swift Mailer to send messages as normal. + +When Swift Mailer sends messages it will count the number of messages that +have been sent since the last re-connect. Once the number hits your specified +threshold it will disconnect and re-connect, optionally pausing for a +specified amount of time. + +.. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Mailer using any Transport + $mailer = Swift_Mailer::newInstance( + Swift_SmtpTransport::newInstance('smtp.example.org', 25) + ); + + // Use AntiFlood to re-connect after 100 emails + $mailer->registerPlugin(new Swift_Plugins_AntiFloodPlugin(100)); + + // And specify a time in seconds to pause for (30 secs) + $mailer->registerPlugin(new Swift_Plugins_AntiFloodPlugin(100, 30)); + + // Continue sending as normal + for ($lotsOfRecipients as $recipient) { + ... + + $mailer->send( ... ); + } + +Throttler Plugin +---------------- + +If your SMTP server has restrictions in place to limit the rate at which you +send emails, then your code will need to be aware of this rate-limiting. The +Throttler plugin makes Swift Mailer run at a rate-limited speed. + +Many shared hosts don't open their SMTP servers as a free-for-all. Usually +they have policies in place (probably to discourage spammers) that only allow +you to send a fixed number of emails per-hour/day. + +The Throttler plugin supports two modes of rate-limiting and with each, you +will need to do that math to figure out the values you want. The plugin can +limit based on the number of emails per minute, or the number of +bytes-transferred per-minute. + +Using the Throttler Plugin +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Throttler Plugin -- like all plugins -- is added with the Mailer class' +``registerPlugin()`` method. It has two required constructor parameters that +tell it how to do its rate-limiting. + +To use the Throttler plugin: + +* Create an instance of the Mailer using any Transport you choose. + +* Create an instance of the ``Swift_Plugins_ThrottlerPlugin`` class, passing + the number of emails, or bytes you wish to limit by, along with the mode + you're using. + +* Register the plugin using the Mailer's ``registerPlugin()`` method. + +* Continue using Swift Mailer to send messages as normal. + +When Swift Mailer sends messages it will keep track of the rate at which sending +messages is occurring. If it realises that sending is happening too fast, it +will cause your program to ``sleep()`` for enough time to average out the rate. + +.. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Mailer using any Transport + $mailer = Swift_Mailer::newInstance( + Swift_SmtpTransport::newInstance('smtp.example.org', 25) + ); + + // Rate limit to 100 emails per-minute + $mailer->registerPlugin(new Swift_Plugins_ThrottlerPlugin( + 100, Swift_Plugins_ThrottlerPlugin::MESSAGES_PER_MINUTE + )); + + // Rate limit to 10MB per-minute + $mailer->registerPlugin(new Swift_Plugins_ThrottlerPlugin( + 1024 * 1024 * 10, Swift_Plugins_ThrottlerPlugin::BYTES_PER_MINUTE + )); + + // Continue sending as normal + for ($lotsOfRecipients as $recipient) { + ... + + $mailer->send( ... ); + } + +Logger Plugin +------------- + +The Logger plugins helps with debugging during the process of sending. It can +help to identify why an SMTP server is rejecting addresses, or any other +hard-to-find problems that may arise. + +The Logger plugin comes in two parts. There's the plugin itself, along with +one of a number of possible Loggers that you may choose to use. For example, +the logger may output messages directly in realtime, or it may capture +messages in an array. + +One other notable feature is the way in which the Logger plugin changes +Exception messages. If Exceptions are being thrown but the error message does +not provide conclusive information as to the source of the problem (such as an +ambiguous SMTP error) the Logger plugin includes the entire SMTP transcript in +the error message so that debugging becomes a simpler task. + +There are a few available Loggers included with Swift Mailer, but writing your +own implementation is incredibly simple and is achieved by creating a short +class that implements the ``Swift_Plugins_Logger`` interface. + +* ``Swift_Plugins_Loggers_ArrayLogger``: Keeps a collection of log messages + inside an array. The array content can be cleared or dumped out to the + screen. + +* ``Swift_Plugins_Loggers_EchoLogger``: Prints output to the screen in + realtime. Handy for very rudimentary debug output. + +Using the Logger Plugin +~~~~~~~~~~~~~~~~~~~~~~~ + +The Logger Plugin -- like all plugins -- is added with the Mailer class' +``registerPlugin()`` method. It accepts an instance of ``Swift_Plugins_Logger`` +in its constructor. + +To use the Logger plugin: + +* Create an instance of the Mailer using any Transport you choose. + +* Create an instance of the a Logger implementation of + ``Swift_Plugins_Logger``. + +* Create an instance of the ``Swift_Plugins_LoggerPlugin`` class, passing the + created Logger instance to its constructor. + +* Register the plugin using the Mailer's ``registerPlugin()`` method. + +* Continue using Swift Mailer to send messages as normal. + +* Dump the contents of the log with the logger's ``dump()`` method. + +When Swift Mailer sends messages it will keep a log of all the interactions +with the underlying Transport being used. Depending upon the Logger that has +been used the behaviour will differ, but all implementations offer a way to +get the contents of the log. + +.. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Mailer using any Transport + $mailer = Swift_Mailer::newInstance( + Swift_SmtpTransport::newInstance('smtp.example.org', 25) + ); + + // To use the ArrayLogger + $logger = new Swift_Plugins_Loggers_ArrayLogger(); + $mailer->registerPlugin(new Swift_Plugins_LoggerPlugin($logger)); + + // Or to use the Echo Logger + $logger = new Swift_Plugins_Loggers_EchoLogger(); + $mailer->registerPlugin(new Swift_Plugins_LoggerPlugin($logger)); + + // Continue sending as normal + for ($lotsOfRecipients as $recipient) { + ... + + $mailer->send( ... ); + } + + // Dump the log contents + // NOTE: The EchoLogger dumps in realtime so dump() does nothing for it + echo $logger->dump(); + +Decorator Plugin +---------------- + +Often there's a need to send the same message to multiple recipients, but with +tiny variations such as the recipient's name being used inside the message +body. The Decorator plugin aims to provide a solution for allowing these small +differences. + +The decorator plugin works by intercepting the sending process of Swift +Mailer, reading the email address in the To: field and then looking up a set +of replacements for a template. + +While the use of this plugin is simple, it is probably the most commonly +misunderstood plugin due to the way in which it works. The typical mistake +users make is to try registering the plugin multiple times (once for each +recipient) -- inside a loop for example. This is incorrect. + +The Decorator plugin should be registered just once, but containing the list +of all recipients prior to sending. It will use this list of recipients to +find the required replacements during sending. + +Using the Decorator Plugin +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To use the Decorator plugin, simply create an associative array of replacements +based on email addresses and then use the mailer's ``registerPlugin()`` method +to add the plugin. + +First create an associative array of replacements based on the email addresses +you'll be sending the message to. + +.. note:: + + The replacements array becomes a 2-dimensional array whose keys are the + email addresses and whose values are an associative array of replacements + for that email address. The curly braces used in this example can be any + type of syntax you choose, provided they match the placeholders in your + email template. + + .. code-block:: php + + $replacements = array(); + foreach ($users as $user) { + $replacements[$user['email']] = array( + '{username}'=>$user['username'], + '{password}'=>$user['password'] + ); + } + +Now create an instance of the Decorator plugin using this array of replacements +and then register it with the Mailer. Do this only once! + +.. code-block:: php + + $decorator = new Swift_Plugins_DecoratorPlugin($replacements); + + $mailer->registerPlugin($decorator); + +When you create your message, replace elements in the body (and/or the subject +line) with your placeholders. + +.. code-block:: php + + $message = Swift_Message::newInstance() + ->setSubject('Important notice for {username}') + ->setBody( + "Hello {username}, we have reset your password to {password}\n" . + "Please log in and change it at your earliest convenience." + ) + ; + + foreach ($users as $user) { + $message->addTo($user['email']); + } + +When you send this message to each of your recipients listed in your +``$replacements`` array they will receive a message customized for just +themselves. For example, the message used above when received may appear like +this to one user: + +.. code-block:: text + + Subject: Important notice for smilingsunshine2009 + + Hello smilingsunshine2009, we have reset your password to rainyDays + Please log in and change it at your earliest convenience. + +While another use may receive the message as: + +.. code-block:: text + + Subject: Important notice for billy-bo-bob + + Hello billy-bo-bob, we have reset your password to dancingOctopus + Please log in and change it at your earliest convenience. + +While the decorator plugin provides a means to solve this problem, there are +various ways you could tackle this problem without the need for a plugin. +We're trying to come up with a better way ourselves and while we have several +(obvious) ideas we don't quite have the perfect solution to go ahead and +implement it. Watch this space. + +Providing Your Own Replacements Lookup for the Decorator +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Filling an array with replacements may not be the best solution for providing +replacement information to the decorator. If you have a more elegant algorithm +that performs replacement lookups on-the-fly you may provide your own +implementation. + +Providing your own replacements lookup implementation for the Decorator is +simply a matter of passing an instance of ``Swift_Plugins_Decorator_Replacements`` to the decorator plugin's constructor, +rather than passing in an array. + +The Replacements interface is very simple to implement since it has just one +method: ``getReplacementsFor($address)``. + +Imagine you want to look up replacements from a database on-the-fly, you might +provide an implementation that does this. You need to create a small class. + +.. code-block:: php + + class DbReplacements implements Swift_Plugins_Decorator_Replacements { + public function getReplacementsFor($address) { + $sql = sprintf( + "SELECT * FROM user WHERE email = '%s'", + mysql_real_escape_string($address) + ); + + $result = mysql_query($sql); + + if ($row = mysql_fetch_assoc($result)) { + return array( + '{username}'=>$row['username'], + '{password}'=>$row['password'] + ); + } + } + } + +Now all you need to do is pass an instance of your class into the Decorator +plugin's constructor instead of passing an array. + +.. code-block:: php + + $decorator = new Swift_Plugins_DecoratorPlugin(new DbReplacements()); + + $mailer->registerPlugin($decorator); + +For each message sent, the plugin will call your class' ``getReplacementsFor()`` +method to find the array of replacements it needs. + +.. note:: + + If your lookup algorithm is case sensitive, you should transform the + ``$address`` argument as appropriate -- for example by passing it + through ``strtolower()``. diff --git a/vendor/swiftmailer/swiftmailer/doc/sending.rst b/vendor/swiftmailer/swiftmailer/doc/sending.rst new file mode 100644 index 0000000000..f340404cad --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/sending.rst @@ -0,0 +1,571 @@ +Sending Messages +================ + +Quick Reference for Sending a Message +------------------------------------- + +Sending a message is very straightforward. You create a Transport, use it to +create the Mailer, then you use the Mailer to send the message. + +To send a Message: + +* Create a Transport from one of the provided Transports -- + ``Swift_SmtpTransport``, ``Swift_SendmailTransport`` + or one of the aggregate Transports. + +* Create an instance of the ``Swift_Mailer`` class, using the Transport as + it's constructor parameter. + +* Create a Message. + +* Send the message via the ``send()`` method on the Mailer object. + +.. caution:: + + The ``Swift_SmtpTransport`` and ``Swift_SendmailTransport`` transports use + ``proc_*`` PHP functions, which might not be available on your PHP + installation. You can easily check if that's the case by running the + following PHP script: ``setUsername('your username') + ->setPassword('your password') + ; + + /* + You could alternatively use a different transport such as Sendmail: + + // Sendmail + $transport = Swift_SendmailTransport::newInstance('/usr/sbin/sendmail -bs'); + */ + + // Create the Mailer using your created Transport + $mailer = Swift_Mailer::newInstance($transport); + + // Create a message + $message = Swift_Message::newInstance('Wonderful Subject') + ->setFrom(array('john@doe.com' => 'John Doe')) + ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name')) + ->setBody('Here is the message itself') + ; + + // Send the message + $result = $mailer->send($message); + +Transport Types +~~~~~~~~~~~~~~~ + +A Transport is the component which actually does the sending. You need to +provide a Transport object to the Mailer class and there are several possible +options. + +Typically you will not need to know how a Transport works under-the-surface, +you will only need to know how to create an instance of one, and which one to +use for your environment. + +The SMTP Transport +.................. + +The SMTP Transport sends messages over the (standardized) Simple Message +Transfer Protocol. It can deal with encryption and authentication. + +The SMTP Transport, ``Swift_SmtpTransport`` is without doubt the most commonly +used Transport because it will work on 99% of web servers (I just made that +number up, but you get the idea). All the server needs is the ability to +connect to a remote (or even local) SMTP server on the correct port number +(usually 25). + +SMTP servers often require users to authenticate with a username and password +before any mail can be sent to other domains. This is easily achieved using +Swift Mailer with the SMTP Transport. + +SMTP is a protocol -- in other words it's a "way" of communicating a job +to be done (i.e. sending a message). The SMTP protocol is the fundamental +basis on which messages are delivered all over the internet 7 days a week, 365 +days a year. For this reason it's the most "direct" method of sending messages +you can use and it's the one that will give you the most power and feedback +(such as delivery failures) when using Swift Mailer. + +Because SMTP is generally run as a remote service (i.e. you connect to it over +the network/internet) it's extremely portable from server-to-server. You can +easily store the SMTP server address and port number in a configuration file +within your application and adjust the settings accordingly if the code is +moved or if the SMTP server is changed. + +Some SMTP servers -- Google for example -- use encryption for security reasons. +Swift Mailer supports using both SSL and TLS encryption settings. + +Using the SMTP Transport +^^^^^^^^^^^^^^^^^^^^^^^^ + +The SMTP Transport is easy to use. Most configuration options can be set with +the constructor. + +To use the SMTP Transport you need to know which SMTP server your code needs +to connect to. Ask your web host if you're not sure. Lots of people ask me who +to connect to -- I really can't answer that since it's a setting that's +extremely specific to your hosting environment. + +To use the SMTP Transport: + +* Call ``Swift_SmtpTransport::newInstance()`` with the SMTP server name and + optionally with a port number (defaults to 25). + +* Use the returned object to create the Mailer. + +A connection to the SMTP server will be established upon the first call to +``send()``. + +.. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Transport + $transport = Swift_SmtpTransport::newInstance('smtp.example.org', 25); + + // Create the Mailer using your created Transport + $mailer = Swift_Mailer::newInstance($transport); + + /* + It's also possible to use multiple method calls + + $transport = Swift_SmtpTransport::newInstance() + ->setHost('smtp.example.org') + ->setPort(25) + ; + */ + +Encrypted SMTP +^^^^^^^^^^^^^^ + +You can use SSL or TLS encryption with the SMTP Transport by specifying it as +a parameter or with a method call. + +To use encryption with the SMTP Transport: + +* Pass the encryption setting as a third parameter to + ``Swift_SmtpTransport::newInstance()``; or + +* Call the ``setEncryption()`` method on the Transport. + +A connection to the SMTP server will be established upon the first call to +``send()``. The connection will be initiated with the correct encryption +settings. + +.. note:: + + For SSL or TLS encryption to work your PHP installation must have + appropriate OpenSSL transports wrappers. You can check if "tls" and/or + "ssl" are present in your PHP installation by using the PHP function + ``stream_get_transports()`` + + .. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Transport + $transport = Swift_SmtpTransport::newInstance('smtp.example.org', 587, 'ssl'); + + // Create the Mailer using your created Transport + $mailer = Swift_Mailer::newInstance($transport); + + /* + It's also possible to use multiple method calls + + $transport = Swift_SmtpTransport::newInstance() + ->setHost('smtp.example.org') + ->setPort(587) + ->setEncryption('ssl') + ; + */ + +SMTP with a Username and Password +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Some servers require authentication. You can provide a username and password +with ``setUsername()`` and ``setPassword()`` methods. + +To use a username and password with the SMTP Transport: + +* Create the Transport with ``Swift_SmtpTransport::newInstance()``. + +* Call the ``setUsername()`` and ``setPassword()`` methods on the Transport. + +Your username and password will be used to authenticate upon first connect +when ``send()`` are first used on the Mailer. + +If authentication fails, an Exception of type ``Swift_TransportException`` will +be thrown. + +.. note:: + + If you need to know early whether or not authentication has failed and an + Exception is going to be thrown, call the ``start()`` method on the + created Transport. + + .. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Transport the call setUsername() and setPassword() + $transport = Swift_SmtpTransport::newInstance('smtp.example.org', 25) + ->setUsername('username') + ->setPassword('password') + ; + + // Create the Mailer using your created Transport + $mailer = Swift_Mailer::newInstance($transport); + +The Sendmail Transport +...................... + +The Sendmail Transport sends messages by communicating with a locally +installed MTA -- such as ``sendmail``. + +The Sendmail Transport, ``Swift_SendmailTransport`` does not directly connect to +any remote services. It is designed for Linux servers that have ``sendmail`` +installed. The Transport starts a local ``sendmail`` process and sends messages +to it. Usually the ``sendmail`` process will respond quickly as it spools your +messages to disk before sending them. + +The Transport is named the Sendmail Transport for historical reasons +(``sendmail`` was the "standard" UNIX tool for sending e-mail for years). It +will send messages using other transfer agents such as Exim or Postfix despite +its name, provided they have the relevant sendmail wrappers so that they can be +started with the correct command-line flags. + +It's a common misconception that because the Sendmail Transport returns a +result very quickly it must therefore deliver messages to recipients quickly +-- this is not true. It's not slow by any means, but it's certainly not +faster than SMTP when it comes to getting messages to the intended recipients. +This is because sendmail itself sends the messages over SMTP once they have +been quickly spooled to disk. + +The Sendmail Transport has the potential to be just as smart of the SMTP +Transport when it comes to notifying Swift Mailer about which recipients were +rejected, but in reality the majority of locally installed ``sendmail`` +instances are not configured well enough to provide any useful feedback. As such +Swift Mailer may report successful deliveries where they did in fact fail before +they even left your server. + +You can run the Sendmail Transport in two different modes specified by command +line flags: + +* "``-bs``" runs in SMTP mode so theoretically it will act like the SMTP + Transport + +* "``-t``" runs in piped mode with no feedback, but theoretically faster, + though not advised + +You can think of the Sendmail Transport as a sort of asynchronous SMTP Transport +-- though if you have problems with delivery failures you should try using the +SMTP Transport instead. Swift Mailer isn't doing the work here, it's simply +passing the work to somebody else (i.e. ``sendmail``). + +Using the Sendmail Transport +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +To use the Sendmail Transport you simply need to call +``Swift_SendmailTransport::newInstance()`` with the command as a parameter. + +To use the Sendmail Transport you need to know where ``sendmail`` or another MTA +exists on the server. Swift Mailer uses a default value of +``/usr/sbin/sendmail``, which should work on most systems. + +You specify the entire command as a parameter (i.e. including the command line +flags). Swift Mailer supports operational modes of "``-bs``" (default) and +"``-t``". + +.. note:: + + If you run sendmail in "``-t``" mode you will get no feedback as to whether + or not sending has succeeded. Use "``-bs``" unless you have a reason not to. + +To use the Sendmail Transport: + +* Call ``Swift_SendmailTransport::newInstance()`` with the command, including + the correct command line flags. The default is to use ``/usr/sbin/sendmail + -bs`` if this is not specified. + +* Use the returned object to create the Mailer. + +A sendmail process will be started upon the first call to ``send()``. If the +process cannot be started successfully an Exception of type +``Swift_TransportException`` will be thrown. + +.. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Transport + $transport = Swift_SendmailTransport::newInstance('/usr/sbin/exim -bs'); + + // Create the Mailer using your created Transport + $mailer = Swift_Mailer::newInstance($transport); + +The Mail Transport +.................. + +The Mail Transport sends messages by delegating to PHP's internal +``mail()`` function. + +In my experience -- and others' -- the ``mail()`` function is not particularly +predictable, or helpful. + +Quite notably, the ``mail()`` function behaves entirely differently between +Linux and Windows servers. On linux it uses ``sendmail``, but on Windows it uses +SMTP. + +In order for the ``mail()`` function to even work at all ``php.ini`` needs to be +configured correctly, specifying the location of sendmail or of an SMTP server. + +The problem with ``mail()`` is that it "tries" to simplify things to the point +that it actually makes things more complex due to poor interface design. The +developers of Swift Mailer have gone to a lot of effort to make the Mail +Transport work with a reasonable degree of consistency. + +Serious drawbacks when using this Transport are: + +* Unpredictable message headers + +* Lack of feedback regarding delivery failures + +* Lack of support for several plugins that require real-time delivery feedback + +It's a last resort, and we say that with a passion! + +Available Methods for Sending Messages +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Mailer class offers two methods for sending Messages -- ``send()``. +Each behaves in a slightly different way. + +When a message is sent in Swift Mailer, the Mailer class communicates with +whichever Transport class you have chosen to use. + +Each recipient in the message should either be accepted or rejected by the +Transport. For example, if the domain name on the email address is not +reachable the SMTP Transport may reject the address because it cannot process +it. Whichever method you use -- ``send()`` -- Swift Mailer will return +an integer indicating the number of accepted recipients. + +.. note:: + + It's possible to find out which recipients were rejected -- we'll cover that + later in this chapter. + +Using the ``send()`` Method +........................... + +The ``send()`` method of the ``Swift_Mailer`` class sends a message using +exactly the same logic as your Desktop mail client would use. Just pass it a +Message and get a result. + +To send a Message with ``send()``: + +* Create a Transport from one of the provided Transports -- + ``Swift_SmtpTransport``, ``Swift_SendmailTransport``, + or one of the aggregate Transports. + +* Create an instance of the ``Swift_Mailer`` class, using the Transport as + it's constructor parameter. + +* Create a Message. + +* Send the message via the ``send()`` method on the Mailer object. + +The message will be sent just like it would be sent if you used your mail +client. An integer is returned which includes the number of successful +recipients. If none of the recipients could be sent to then zero will be +returned, which equates to a boolean ``false``. If you set two +``To:`` recipients and three ``Bcc:`` recipients in the message and all of the +recipients are delivered to successfully then the value 5 will be returned. + +.. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Transport + $transport = Swift_SmtpTransport::newInstance('localhost', 25); + + // Create the Mailer using your created Transport + $mailer = Swift_Mailer::newInstance($transport); + + // Create a message + $message = Swift_Message::newInstance('Wonderful Subject') + ->setFrom(array('john@doe.com' => 'John Doe')) + ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name')) + ->setBody('Here is the message itself') + ; + + // Send the message + $numSent = $mailer->send($message); + + printf("Sent %d messages\n", $numSent); + + /* Note that often that only the boolean equivalent of the + return value is of concern (zero indicates FALSE) + + if ($mailer->send($message)) + { + echo "Sent\n"; + } + else + { + echo "Failed\n"; + } + + */ + +Sending Emails in Batch +....................... + +If you want to send a separate message to each recipient so that only their +own address shows up in the ``To:`` field, follow the following recipe: + +* Create a Transport from one of the provided Transports -- + ``Swift_SmtpTransport``, ``Swift_SendmailTransport``, + or one of the aggregate Transports. + +* Create an instance of the ``Swift_Mailer`` class, using the Transport as + it's constructor parameter. + +* Create a Message. + +* Iterate over the recipients and send message via the ``send()`` method on + the Mailer object. + +Each recipient of the messages receives a different copy with only their own +email address on the ``To:`` field. + +Make sure to add only valid email addresses as recipients. If you try to add an +invalid email address with ``setTo()``, ``setCc()`` or ``setBcc()``, Swift +Mailer will throw a ``Swift_RfcComplianceException``. + +If you add recipients automatically based on a data source that may contain +invalid email addresses, you can prevent possible exceptions by validating the +addresses using ``Swift_Validate::email($email)`` and only adding addresses +that validate. Another way would be to wrap your ``setTo()``, ``setCc()`` and +``setBcc()`` calls in a try-catch block and handle the +``Swift_RfcComplianceException`` in the catch block. + +Handling invalid addresses properly is especially important when sending emails +in large batches since a single invalid address might cause an unhandled +exception and stop the execution or your script early. + +.. note:: + + In the following example, two emails are sent. One to each of + ``receiver@domain.org`` and ``other@domain.org``. These recipients will + not be aware of each other. + + .. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Transport + $transport = Swift_SmtpTransport::newInstance('localhost', 25); + + // Create the Mailer using your created Transport + $mailer = Swift_Mailer::newInstance($transport); + + // Create a message + $message = Swift_Message::newInstance('Wonderful Subject') + ->setFrom(array('john@doe.com' => 'John Doe')) + ->setBody('Here is the message itself') + ; + + // Send the message + $failedRecipients = array(); + $numSent = 0; + $to = array('receiver@domain.org', 'other@domain.org' => 'A name'); + + foreach ($to as $address => $name) + { + if (is_int($address)) { + $message->setTo($name); + } else { + $message->setTo(array($address => $name)); + } + + $numSent += $mailer->send($message, $failedRecipients); + } + + printf("Sent %d messages\n", $numSent); + +Finding out Rejected Addresses +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It's possible to get a list of addresses that were rejected by the Transport +by using a by-reference parameter to ``send()``. + +As Swift Mailer attempts to send the message to each address given to it, if a +recipient is rejected it will be added to the array. You can pass an existing +array, otherwise one will be created by-reference. + +Collecting the list of recipients that were rejected can be useful in +circumstances where you need to "prune" a mailing list for example when some +addresses cannot be delivered to. + +Getting Failures By-reference +............................. + +Collecting delivery failures by-reference with the ``send()`` method is as +simple as passing a variable name to the method call. + +To get failed recipients by-reference: + +* Pass a by-reference variable name to the ``send()`` method of the Mailer + class. + +If the Transport rejects any of the recipients, the culprit addresses will be +added to the array provided by-reference. + +.. note:: + + If the variable name does not yet exist, it will be initialized as an + empty array and then failures will be added to that array. If the variable + already exists it will be type-cast to an array and failures will be added + to it. + + .. code-block:: php + + $mailer = Swift_Mailer::newInstance( ... ); + + $message = Swift_Message::newInstance( ... ) + ->setFrom( ... ) + ->setTo(array( + 'receiver@bad-domain.org' => 'Receiver Name', + 'other@domain.org' => 'A name', + 'other-receiver@bad-domain.org' => 'Other Name' + )) + ->setBody( ... ) + ; + + // Pass a variable name to the send() method + if (!$mailer->send($message, $failures)) + { + echo "Failures:"; + print_r($failures); + } + + /* + Failures: + Array ( + 0 => receiver@bad-domain.org, + 1 => other-receiver@bad-domain.org + ) + */ diff --git a/vendor/swiftmailer/swiftmailer/doc/uml/Encoders.graffle b/vendor/swiftmailer/swiftmailer/doc/uml/Encoders.graffle new file mode 100644 index 0000000000000000000000000000000000000000..f895752b70947592db74b6a3b8a6bd135a9fd150 GIT binary patch literal 3503 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 literal 0 HcmV?d00001 diff --git a/vendor/swiftmailer/swiftmailer/doc/uml/Mime.graffle b/vendor/swiftmailer/swiftmailer/doc/uml/Mime.graffle new file mode 100644 index 0000000000000000000000000000000000000000..e1e33cbf16ea45d29ad48eb9b2f37959292881b2 GIT binary patch literal 5575 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 VzxW)xOdDSB{SO2kTt$6+0RTlqtKk3u literal 0 HcmV?d00001 diff --git a/vendor/swiftmailer/swiftmailer/doc/uml/Transports.graffle b/vendor/swiftmailer/swiftmailer/doc/uml/Transports.graffle new file mode 100644 index 0000000000000000000000000000000000000000..5670e2b65ada6e01406334a4b462f58e8f48f4fd GIT binary patch literal 3061 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 D6q4x9 literal 0 HcmV?d00001 diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift.php new file mode 100644 index 0000000000..82c381b73d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift.php @@ -0,0 +1,80 @@ +createDependenciesFor('mime.attachment') + ); + + $this->setBody($data); + $this->setFilename($filename); + if ($contentType) { + $this->setContentType($contentType); + } + } + + /** + * Create a new Attachment. + * + * @param string|Swift_OutputByteStream $data + * @param string $filename + * @param string $contentType + * + * @return Swift_Mime_Attachment + */ + public static function newInstance($data = null, $filename = null, $contentType = null) + { + return new self($data, $filename, $contentType); + } + + /** + * Create a new Attachment from a filesystem path. + * + * @param string $path + * @param string $contentType optional + * + * @return Swift_Mime_Attachment + */ + public static function fromPath($path, $contentType = null) + { + return self::newInstance()->setFile( + new Swift_ByteStream_FileByteStream($path), + $contentType + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/AbstractFilterableInputStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/AbstractFilterableInputStream.php new file mode 100644 index 0000000000..a7b0e3a620 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/AbstractFilterableInputStream.php @@ -0,0 +1,181 @@ +_filters[$key] = $filter; + } + + /** + * Remove an already present StreamFilter based on its $key. + * + * @param string $key + */ + public function removeFilter($key) + { + unset($this->_filters[$key]); + } + + /** + * Writes $bytes to the end of the stream. + * + * @param string $bytes + * + * @throws Swift_IoException + * + * @return int + */ + public function write($bytes) + { + $this->_writeBuffer .= $bytes; + foreach ($this->_filters as $filter) { + if ($filter->shouldBuffer($this->_writeBuffer)) { + return; + } + } + $this->_doWrite($this->_writeBuffer); + + return ++$this->_sequence; + } + + /** + * For any bytes that are currently buffered inside the stream, force them + * off the buffer. + * + * @throws Swift_IoException + */ + public function commit() + { + $this->_doWrite($this->_writeBuffer); + } + + /** + * Attach $is to this stream. + * + * The stream acts as an observer, receiving all data that is written. + * All {@link write()} and {@link flushBuffers()} operations will be mirrored. + * + * @param Swift_InputByteStream $is + */ + public function bind(Swift_InputByteStream $is) + { + $this->_mirrors[] = $is; + } + + /** + * Remove an already bound stream. + * + * If $is is not bound, no errors will be raised. + * If the stream currently has any buffered data it will be written to $is + * before unbinding occurs. + * + * @param Swift_InputByteStream $is + */ + public function unbind(Swift_InputByteStream $is) + { + foreach ($this->_mirrors as $k => $stream) { + if ($is === $stream) { + if ($this->_writeBuffer !== '') { + $stream->write($this->_writeBuffer); + } + unset($this->_mirrors[$k]); + } + } + } + + /** + * Flush the contents of the stream (empty it) and set the internal pointer + * to the beginning. + * + * @throws Swift_IoException + */ + public function flushBuffers() + { + if ($this->_writeBuffer !== '') { + $this->_doWrite($this->_writeBuffer); + } + $this->_flush(); + + foreach ($this->_mirrors as $stream) { + $stream->flushBuffers(); + } + } + + /** Run $bytes through all filters */ + private function _filter($bytes) + { + foreach ($this->_filters as $filter) { + $bytes = $filter->filter($bytes); + } + + return $bytes; + } + + /** Just write the bytes to the stream */ + private function _doWrite($bytes) + { + $this->_commit($this->_filter($bytes)); + + foreach ($this->_mirrors as $stream) { + $stream->write($bytes); + } + + $this->_writeBuffer = ''; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/ArrayByteStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/ArrayByteStream.php new file mode 100644 index 0000000000..ef05a6d5e6 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/ArrayByteStream.php @@ -0,0 +1,182 @@ +_array = $stack; + $this->_arraySize = count($stack); + } elseif (is_string($stack)) { + $this->write($stack); + } else { + $this->_array = array(); + } + } + + /** + * Reads $length bytes from the stream into a string and moves the pointer + * through the stream by $length. + * + * If less bytes exist than are requested the + * remaining bytes are given instead. If no bytes are remaining at all, boolean + * false is returned. + * + * @param int $length + * + * @return string + */ + public function read($length) + { + if ($this->_offset == $this->_arraySize) { + return false; + } + + // Don't use array slice + $end = $length + $this->_offset; + $end = $this->_arraySize < $end ? $this->_arraySize : $end; + $ret = ''; + for (; $this->_offset < $end; ++$this->_offset) { + $ret .= $this->_array[$this->_offset]; + } + + return $ret; + } + + /** + * Writes $bytes to the end of the stream. + * + * @param string $bytes + */ + public function write($bytes) + { + $to_add = str_split($bytes); + foreach ($to_add as $value) { + $this->_array[] = $value; + } + $this->_arraySize = count($this->_array); + + foreach ($this->_mirrors as $stream) { + $stream->write($bytes); + } + } + + /** + * Not used. + */ + public function commit() + { + } + + /** + * Attach $is to this stream. + * + * The stream acts as an observer, receiving all data that is written. + * All {@link write()} and {@link flushBuffers()} operations will be mirrored. + * + * @param Swift_InputByteStream $is + */ + public function bind(Swift_InputByteStream $is) + { + $this->_mirrors[] = $is; + } + + /** + * Remove an already bound stream. + * + * If $is is not bound, no errors will be raised. + * If the stream currently has any buffered data it will be written to $is + * before unbinding occurs. + * + * @param Swift_InputByteStream $is + */ + public function unbind(Swift_InputByteStream $is) + { + foreach ($this->_mirrors as $k => $stream) { + if ($is === $stream) { + unset($this->_mirrors[$k]); + } + } + } + + /** + * Move the internal read pointer to $byteOffset in the stream. + * + * @param int $byteOffset + * + * @return bool + */ + public function setReadPointer($byteOffset) + { + if ($byteOffset > $this->_arraySize) { + $byteOffset = $this->_arraySize; + } elseif ($byteOffset < 0) { + $byteOffset = 0; + } + + $this->_offset = $byteOffset; + } + + /** + * Flush the contents of the stream (empty it) and set the internal pointer + * to the beginning. + */ + public function flushBuffers() + { + $this->_offset = 0; + $this->_array = array(); + $this->_arraySize = 0; + + foreach ($this->_mirrors as $stream) { + $stream->flushBuffers(); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/FileByteStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/FileByteStream.php new file mode 100644 index 0000000000..9ed8523107 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/FileByteStream.php @@ -0,0 +1,231 @@ +_path = $path; + $this->_mode = $writable ? 'w+b' : 'rb'; + + if (function_exists('get_magic_quotes_runtime') && @get_magic_quotes_runtime() == 1) { + $this->_quotes = true; + } + } + + /** + * Get the complete path to the file. + * + * @return string + */ + public function getPath() + { + return $this->_path; + } + + /** + * Reads $length bytes from the stream into a string and moves the pointer + * through the stream by $length. + * + * If less bytes exist than are requested the + * remaining bytes are given instead. If no bytes are remaining at all, boolean + * false is returned. + * + * @param int $length + * + * @throws Swift_IoException + * + * @return string|bool + */ + public function read($length) + { + $fp = $this->_getReadHandle(); + if (!feof($fp)) { + if ($this->_quotes) { + ini_set('magic_quotes_runtime', 0); + } + $bytes = fread($fp, $length); + if ($this->_quotes) { + ini_set('magic_quotes_runtime', 1); + } + $this->_offset = ftell($fp); + + // If we read one byte after reaching the end of the file + // feof() will return false and an empty string is returned + if ($bytes === '' && feof($fp)) { + $this->_resetReadHandle(); + + return false; + } + + return $bytes; + } + + $this->_resetReadHandle(); + + return false; + } + + /** + * Move the internal read pointer to $byteOffset in the stream. + * + * @param int $byteOffset + * + * @return bool + */ + public function setReadPointer($byteOffset) + { + if (isset($this->_reader)) { + $this->_seekReadStreamToPosition($byteOffset); + } + $this->_offset = $byteOffset; + } + + /** Just write the bytes to the file */ + protected function _commit($bytes) + { + fwrite($this->_getWriteHandle(), $bytes); + $this->_resetReadHandle(); + } + + /** Not used */ + protected function _flush() + { + } + + /** Get the resource for reading */ + private function _getReadHandle() + { + if (!isset($this->_reader)) { + $pointer = @fopen($this->_path, 'rb'); + if (!$pointer) { + throw new Swift_IoException( + 'Unable to open file for reading ['.$this->_path.']' + ); + } + $this->_reader = $pointer; + if ($this->_offset != 0) { + $this->_getReadStreamSeekableStatus(); + $this->_seekReadStreamToPosition($this->_offset); + } + } + + return $this->_reader; + } + + /** Get the resource for writing */ + private function _getWriteHandle() + { + if (!isset($this->_writer)) { + if (!$this->_writer = fopen($this->_path, $this->_mode)) { + throw new Swift_IoException( + 'Unable to open file for writing ['.$this->_path.']' + ); + } + } + + return $this->_writer; + } + + /** Force a reload of the resource for reading */ + private function _resetReadHandle() + { + if (isset($this->_reader)) { + fclose($this->_reader); + $this->_reader = null; + } + } + + /** Check if ReadOnly Stream is seekable */ + private function _getReadStreamSeekableStatus() + { + $metas = stream_get_meta_data($this->_reader); + $this->_seekable = $metas['seekable']; + } + + /** Streams in a readOnly stream ensuring copy if needed */ + private function _seekReadStreamToPosition($offset) + { + if ($this->_seekable === null) { + $this->_getReadStreamSeekableStatus(); + } + if ($this->_seekable === false) { + $currentPos = ftell($this->_reader); + if ($currentPos < $offset) { + $toDiscard = $offset - $currentPos; + fread($this->_reader, $toDiscard); + + return; + } + $this->_copyReadStream(); + } + fseek($this->_reader, $offset, SEEK_SET); + } + + /** Copy a readOnly Stream to ensure seekability */ + private function _copyReadStream() + { + if ($tmpFile = fopen('php://temp/maxmemory:4096', 'w+b')) { + /* We have opened a php:// Stream Should work without problem */ + } elseif (function_exists('sys_get_temp_dir') && is_writable(sys_get_temp_dir()) && ($tmpFile = tmpfile())) { + /* We have opened a tmpfile */ + } else { + throw new Swift_IoException('Unable to copy the file to make it seekable, sys_temp_dir is not writable, php://memory not available'); + } + $currentPos = ftell($this->_reader); + fclose($this->_reader); + $source = fopen($this->_path, 'rb'); + if (!$source) { + throw new Swift_IoException('Unable to open file for copying ['.$this->_path.']'); + } + fseek($tmpFile, 0, SEEK_SET); + while (!feof($source)) { + fwrite($tmpFile, fread($source, 4096)); + } + fseek($tmpFile, $currentPos, SEEK_SET); + fclose($source); + $this->_reader = $tmpFile; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/TemporaryFileByteStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/TemporaryFileByteStream.php new file mode 100644 index 0000000000..1c9a80c037 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/TemporaryFileByteStream.php @@ -0,0 +1,42 @@ +getPath())) === false) { + throw new Swift_IoException('Failed to get temporary file content.'); + } + + return $content; + } + + public function __destruct() + { + if (file_exists($this->getPath())) { + @unlink($this->getPath()); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader.php new file mode 100644 index 0000000000..3d5e854a88 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader.php @@ -0,0 +1,67 @@ + + */ +interface Swift_CharacterReader +{ + const MAP_TYPE_INVALID = 0x01; + const MAP_TYPE_FIXED_LEN = 0x02; + const MAP_TYPE_POSITIONS = 0x03; + + /** + * Returns the complete character map. + * + * @param string $string + * @param int $startOffset + * @param array $currentMap + * @param mixed $ignoredChars + * + * @return int + */ + public function getCharPositions($string, $startOffset, &$currentMap, &$ignoredChars); + + /** + * Returns the mapType, see constants. + * + * @return int + */ + public function getMapType(); + + /** + * Returns an integer which specifies how many more bytes to read. + * + * A positive integer indicates the number of more bytes to fetch before invoking + * this method again. + * + * A value of zero means this is already a valid character. + * A value of -1 means this cannot possibly be a valid character. + * + * @param integer[] $bytes + * @param int $size + * + * @return int + */ + public function validateByteSequence($bytes, $size); + + /** + * Returns the number of bytes which should be read to start each character. + * + * For fixed width character sets this should be the number of octets-per-character. + * For multibyte character sets this will probably be 1. + * + * @return int + */ + public function getInitialByteSize(); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/GenericFixedWidthReader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/GenericFixedWidthReader.php new file mode 100644 index 0000000000..6a18e1dd1f --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/GenericFixedWidthReader.php @@ -0,0 +1,97 @@ + + */ +class Swift_CharacterReader_GenericFixedWidthReader implements Swift_CharacterReader +{ + /** + * The number of bytes in a single character. + * + * @var int + */ + private $_width; + + /** + * Creates a new GenericFixedWidthReader using $width bytes per character. + * + * @param int $width + */ + public function __construct($width) + { + $this->_width = $width; + } + + /** + * Returns the complete character map. + * + * @param string $string + * @param int $startOffset + * @param array $currentMap + * @param mixed $ignoredChars + * + * @return int + */ + public function getCharPositions($string, $startOffset, &$currentMap, &$ignoredChars) + { + $strlen = strlen($string); + // % and / are CPU intensive, so, maybe find a better way + $ignored = $strlen % $this->_width; + $ignoredChars = $ignored ? substr($string, -$ignored) : ''; + $currentMap = $this->_width; + + return ($strlen - $ignored) / $this->_width; + } + + /** + * Returns the mapType. + * + * @return int + */ + public function getMapType() + { + return self::MAP_TYPE_FIXED_LEN; + } + + /** + * Returns an integer which specifies how many more bytes to read. + * + * A positive integer indicates the number of more bytes to fetch before invoking + * this method again. + * + * A value of zero means this is already a valid character. + * A value of -1 means this cannot possibly be a valid character. + * + * @param string $bytes + * @param int $size + * + * @return int + */ + public function validateByteSequence($bytes, $size) + { + $needed = $this->_width - $size; + + return $needed > -1 ? $needed : -1; + } + + /** + * Returns the number of bytes which should be read to start each character. + * + * @return int + */ + public function getInitialByteSize() + { + return $this->_width; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/UsAsciiReader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/UsAsciiReader.php new file mode 100644 index 0000000000..67da48f6cb --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/UsAsciiReader.php @@ -0,0 +1,84 @@ + "\x07F") { + // Invalid char + $currentMap[$i + $startOffset] = $string[$i]; + } + } + + return $strlen; + } + + /** + * Returns mapType. + * + * @return int mapType + */ + public function getMapType() + { + return self::MAP_TYPE_INVALID; + } + + /** + * Returns an integer which specifies how many more bytes to read. + * + * A positive integer indicates the number of more bytes to fetch before invoking + * this method again. + * A value of zero means this is already a valid character. + * A value of -1 means this cannot possibly be a valid character. + * + * @param string $bytes + * @param int $size + * + * @return int + */ + public function validateByteSequence($bytes, $size) + { + $byte = reset($bytes); + if (1 == count($bytes) && $byte >= 0x00 && $byte <= 0x7F) { + return 0; + } + + return -1; + } + + /** + * Returns the number of bytes which should be read to start each character. + * + * @return int + */ + public function getInitialByteSize() + { + return 1; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/Utf8Reader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/Utf8Reader.php new file mode 100644 index 0000000000..7379bda258 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/Utf8Reader.php @@ -0,0 +1,176 @@ + + */ +class Swift_CharacterReader_Utf8Reader implements Swift_CharacterReader +{ + /** Pre-computed for optimization */ + private static $length_map = array( + // N=0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x0N + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x1N + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x2N + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x3N + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x4N + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x5N + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x6N + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x7N + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0x8N + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0x9N + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0xAN + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0xBN + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // 0xCN + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // 0xDN + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, // 0xEN + 4,4,4,4,4,4,4,4,5,5,5,5,6,6,0,0, // 0xFN + ); + + private static $s_length_map = array( + "\x00" => 1, "\x01" => 1, "\x02" => 1, "\x03" => 1, "\x04" => 1, "\x05" => 1, "\x06" => 1, "\x07" => 1, + "\x08" => 1, "\x09" => 1, "\x0a" => 1, "\x0b" => 1, "\x0c" => 1, "\x0d" => 1, "\x0e" => 1, "\x0f" => 1, + "\x10" => 1, "\x11" => 1, "\x12" => 1, "\x13" => 1, "\x14" => 1, "\x15" => 1, "\x16" => 1, "\x17" => 1, + "\x18" => 1, "\x19" => 1, "\x1a" => 1, "\x1b" => 1, "\x1c" => 1, "\x1d" => 1, "\x1e" => 1, "\x1f" => 1, + "\x20" => 1, "\x21" => 1, "\x22" => 1, "\x23" => 1, "\x24" => 1, "\x25" => 1, "\x26" => 1, "\x27" => 1, + "\x28" => 1, "\x29" => 1, "\x2a" => 1, "\x2b" => 1, "\x2c" => 1, "\x2d" => 1, "\x2e" => 1, "\x2f" => 1, + "\x30" => 1, "\x31" => 1, "\x32" => 1, "\x33" => 1, "\x34" => 1, "\x35" => 1, "\x36" => 1, "\x37" => 1, + "\x38" => 1, "\x39" => 1, "\x3a" => 1, "\x3b" => 1, "\x3c" => 1, "\x3d" => 1, "\x3e" => 1, "\x3f" => 1, + "\x40" => 1, "\x41" => 1, "\x42" => 1, "\x43" => 1, "\x44" => 1, "\x45" => 1, "\x46" => 1, "\x47" => 1, + "\x48" => 1, "\x49" => 1, "\x4a" => 1, "\x4b" => 1, "\x4c" => 1, "\x4d" => 1, "\x4e" => 1, "\x4f" => 1, + "\x50" => 1, "\x51" => 1, "\x52" => 1, "\x53" => 1, "\x54" => 1, "\x55" => 1, "\x56" => 1, "\x57" => 1, + "\x58" => 1, "\x59" => 1, "\x5a" => 1, "\x5b" => 1, "\x5c" => 1, "\x5d" => 1, "\x5e" => 1, "\x5f" => 1, + "\x60" => 1, "\x61" => 1, "\x62" => 1, "\x63" => 1, "\x64" => 1, "\x65" => 1, "\x66" => 1, "\x67" => 1, + "\x68" => 1, "\x69" => 1, "\x6a" => 1, "\x6b" => 1, "\x6c" => 1, "\x6d" => 1, "\x6e" => 1, "\x6f" => 1, + "\x70" => 1, "\x71" => 1, "\x72" => 1, "\x73" => 1, "\x74" => 1, "\x75" => 1, "\x76" => 1, "\x77" => 1, + "\x78" => 1, "\x79" => 1, "\x7a" => 1, "\x7b" => 1, "\x7c" => 1, "\x7d" => 1, "\x7e" => 1, "\x7f" => 1, + "\x80" => 0, "\x81" => 0, "\x82" => 0, "\x83" => 0, "\x84" => 0, "\x85" => 0, "\x86" => 0, "\x87" => 0, + "\x88" => 0, "\x89" => 0, "\x8a" => 0, "\x8b" => 0, "\x8c" => 0, "\x8d" => 0, "\x8e" => 0, "\x8f" => 0, + "\x90" => 0, "\x91" => 0, "\x92" => 0, "\x93" => 0, "\x94" => 0, "\x95" => 0, "\x96" => 0, "\x97" => 0, + "\x98" => 0, "\x99" => 0, "\x9a" => 0, "\x9b" => 0, "\x9c" => 0, "\x9d" => 0, "\x9e" => 0, "\x9f" => 0, + "\xa0" => 0, "\xa1" => 0, "\xa2" => 0, "\xa3" => 0, "\xa4" => 0, "\xa5" => 0, "\xa6" => 0, "\xa7" => 0, + "\xa8" => 0, "\xa9" => 0, "\xaa" => 0, "\xab" => 0, "\xac" => 0, "\xad" => 0, "\xae" => 0, "\xaf" => 0, + "\xb0" => 0, "\xb1" => 0, "\xb2" => 0, "\xb3" => 0, "\xb4" => 0, "\xb5" => 0, "\xb6" => 0, "\xb7" => 0, + "\xb8" => 0, "\xb9" => 0, "\xba" => 0, "\xbb" => 0, "\xbc" => 0, "\xbd" => 0, "\xbe" => 0, "\xbf" => 0, + "\xc0" => 2, "\xc1" => 2, "\xc2" => 2, "\xc3" => 2, "\xc4" => 2, "\xc5" => 2, "\xc6" => 2, "\xc7" => 2, + "\xc8" => 2, "\xc9" => 2, "\xca" => 2, "\xcb" => 2, "\xcc" => 2, "\xcd" => 2, "\xce" => 2, "\xcf" => 2, + "\xd0" => 2, "\xd1" => 2, "\xd2" => 2, "\xd3" => 2, "\xd4" => 2, "\xd5" => 2, "\xd6" => 2, "\xd7" => 2, + "\xd8" => 2, "\xd9" => 2, "\xda" => 2, "\xdb" => 2, "\xdc" => 2, "\xdd" => 2, "\xde" => 2, "\xdf" => 2, + "\xe0" => 3, "\xe1" => 3, "\xe2" => 3, "\xe3" => 3, "\xe4" => 3, "\xe5" => 3, "\xe6" => 3, "\xe7" => 3, + "\xe8" => 3, "\xe9" => 3, "\xea" => 3, "\xeb" => 3, "\xec" => 3, "\xed" => 3, "\xee" => 3, "\xef" => 3, + "\xf0" => 4, "\xf1" => 4, "\xf2" => 4, "\xf3" => 4, "\xf4" => 4, "\xf5" => 4, "\xf6" => 4, "\xf7" => 4, + "\xf8" => 5, "\xf9" => 5, "\xfa" => 5, "\xfb" => 5, "\xfc" => 6, "\xfd" => 6, "\xfe" => 0, "\xff" => 0, + ); + + /** + * Returns the complete character map. + * + * @param string $string + * @param int $startOffset + * @param array $currentMap + * @param mixed $ignoredChars + * + * @return int + */ + public function getCharPositions($string, $startOffset, &$currentMap, &$ignoredChars) + { + if (!isset($currentMap['i']) || !isset($currentMap['p'])) { + $currentMap['p'] = $currentMap['i'] = array(); + } + + $strlen = strlen($string); + $charPos = count($currentMap['p']); + $foundChars = 0; + $invalid = false; + for ($i = 0; $i < $strlen; ++$i) { + $char = $string[$i]; + $size = self::$s_length_map[$char]; + if ($size == 0) { + /* char is invalid, we must wait for a resync */ + $invalid = true; + continue; + } else { + if ($invalid == true) { + /* We mark the chars as invalid and start a new char */ + $currentMap['p'][$charPos + $foundChars] = $startOffset + $i; + $currentMap['i'][$charPos + $foundChars] = true; + ++$foundChars; + $invalid = false; + } + if (($i + $size) > $strlen) { + $ignoredChars = substr($string, $i); + break; + } + for ($j = 1; $j < $size; ++$j) { + $char = $string[$i + $j]; + if ($char > "\x7F" && $char < "\xC0") { + // Valid - continue parsing + } else { + /* char is invalid, we must wait for a resync */ + $invalid = true; + continue 2; + } + } + /* Ok we got a complete char here */ + $currentMap['p'][$charPos + $foundChars] = $startOffset + $i + $size; + $i += $j - 1; + ++$foundChars; + } + } + + return $foundChars; + } + + /** + * Returns mapType. + * + * @return int mapType + */ + public function getMapType() + { + return self::MAP_TYPE_POSITIONS; + } + + /** + * Returns an integer which specifies how many more bytes to read. + * + * A positive integer indicates the number of more bytes to fetch before invoking + * this method again. + * A value of zero means this is already a valid character. + * A value of -1 means this cannot possibly be a valid character. + * + * @param string $bytes + * @param int $size + * + * @return int + */ + public function validateByteSequence($bytes, $size) + { + if ($size < 1) { + return -1; + } + $needed = self::$length_map[$bytes[0]] - $size; + + return $needed > -1 ? $needed : -1; + } + + /** + * Returns the number of bytes which should be read to start each character. + * + * @return int + */ + public function getInitialByteSize() + { + return 1; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReaderFactory.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReaderFactory.php new file mode 100644 index 0000000000..15b6c6920f --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReaderFactory.php @@ -0,0 +1,26 @@ +init(); + } + + public function __wakeup() + { + $this->init(); + } + + public function init() + { + if (count(self::$_map) > 0) { + return; + } + + $prefix = 'Swift_CharacterReader_'; + + $singleByte = array( + 'class' => $prefix.'GenericFixedWidthReader', + 'constructor' => array(1), + ); + + $doubleByte = array( + 'class' => $prefix.'GenericFixedWidthReader', + 'constructor' => array(2), + ); + + $fourBytes = array( + 'class' => $prefix.'GenericFixedWidthReader', + 'constructor' => array(4), + ); + + // Utf-8 + self::$_map['utf-?8'] = array( + 'class' => $prefix.'Utf8Reader', + 'constructor' => array(), + ); + + //7-8 bit charsets + self::$_map['(us-)?ascii'] = $singleByte; + self::$_map['(iso|iec)-?8859-?[0-9]+'] = $singleByte; + self::$_map['windows-?125[0-9]'] = $singleByte; + self::$_map['cp-?[0-9]+'] = $singleByte; + self::$_map['ansi'] = $singleByte; + self::$_map['macintosh'] = $singleByte; + self::$_map['koi-?7'] = $singleByte; + self::$_map['koi-?8-?.+'] = $singleByte; + self::$_map['mik'] = $singleByte; + self::$_map['(cork|t1)'] = $singleByte; + self::$_map['v?iscii'] = $singleByte; + + //16 bits + self::$_map['(ucs-?2|utf-?16)'] = $doubleByte; + + //32 bits + self::$_map['(ucs-?4|utf-?32)'] = $fourBytes; + + // Fallback + self::$_map['.*'] = $singleByte; + } + + /** + * Returns a CharacterReader suitable for the charset applied. + * + * @param string $charset + * + * @return Swift_CharacterReader + */ + public function getReaderFor($charset) + { + $charset = trim(strtolower($charset)); + foreach (self::$_map as $pattern => $spec) { + $re = '/^'.$pattern.'$/D'; + if (preg_match($re, $charset)) { + if (!array_key_exists($pattern, self::$_loaded)) { + $reflector = new ReflectionClass($spec['class']); + if ($reflector->getConstructor()) { + $reader = $reflector->newInstanceArgs($spec['constructor']); + } else { + $reader = $reflector->newInstance(); + } + self::$_loaded[$pattern] = $reader; + } + + return self::$_loaded[$pattern]; + } + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream.php new file mode 100644 index 0000000000..717924f5d6 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream.php @@ -0,0 +1,89 @@ +setCharacterReaderFactory($factory); + $this->setCharacterSet($charset); + } + + /** + * Set the character set used in this CharacterStream. + * + * @param string $charset + */ + public function setCharacterSet($charset) + { + $this->_charset = $charset; + $this->_charReader = null; + } + + /** + * Set the CharacterReaderFactory for multi charset support. + * + * @param Swift_CharacterReaderFactory $factory + */ + public function setCharacterReaderFactory(Swift_CharacterReaderFactory $factory) + { + $this->_charReaderFactory = $factory; + } + + /** + * Overwrite this character stream using the byte sequence in the byte stream. + * + * @param Swift_OutputByteStream $os output stream to read from + */ + public function importByteStream(Swift_OutputByteStream $os) + { + if (!isset($this->_charReader)) { + $this->_charReader = $this->_charReaderFactory + ->getReaderFor($this->_charset); + } + + $startLength = $this->_charReader->getInitialByteSize(); + while (false !== $bytes = $os->read($startLength)) { + $c = array(); + for ($i = 0, $len = strlen($bytes); $i < $len; ++$i) { + $c[] = self::$_byteMap[$bytes[$i]]; + } + $size = count($c); + $need = $this->_charReader + ->validateByteSequence($c, $size); + if ($need > 0 && + false !== $bytes = $os->read($need)) { + for ($i = 0, $len = strlen($bytes); $i < $len; ++$i) { + $c[] = self::$_byteMap[$bytes[$i]]; + } + } + $this->_array[] = $c; + ++$this->_array_size; + } + } + + /** + * Import a string a bytes into this CharacterStream, overwriting any existing + * data in the stream. + * + * @param string $string + */ + public function importString($string) + { + $this->flushContents(); + $this->write($string); + } + + /** + * Read $length characters from the stream and move the internal pointer + * $length further into the stream. + * + * @param int $length + * + * @return string + */ + public function read($length) + { + if ($this->_offset == $this->_array_size) { + return false; + } + + // Don't use array slice + $arrays = array(); + $end = $length + $this->_offset; + for ($i = $this->_offset; $i < $end; ++$i) { + if (!isset($this->_array[$i])) { + break; + } + $arrays[] = $this->_array[$i]; + } + $this->_offset += $i - $this->_offset; // Limit function calls + $chars = false; + foreach ($arrays as $array) { + $chars .= implode('', array_map('chr', $array)); + } + + return $chars; + } + + /** + * Read $length characters from the stream and return a 1-dimensional array + * containing there octet values. + * + * @param int $length + * + * @return integer[] + */ + public function readBytes($length) + { + if ($this->_offset == $this->_array_size) { + return false; + } + $arrays = array(); + $end = $length + $this->_offset; + for ($i = $this->_offset; $i < $end; ++$i) { + if (!isset($this->_array[$i])) { + break; + } + $arrays[] = $this->_array[$i]; + } + $this->_offset += ($i - $this->_offset); // Limit function calls + + return call_user_func_array('array_merge', $arrays); + } + + /** + * Write $chars to the end of the stream. + * + * @param string $chars + */ + public function write($chars) + { + if (!isset($this->_charReader)) { + $this->_charReader = $this->_charReaderFactory->getReaderFor( + $this->_charset); + } + + $startLength = $this->_charReader->getInitialByteSize(); + + $fp = fopen('php://memory', 'w+b'); + fwrite($fp, $chars); + unset($chars); + fseek($fp, 0, SEEK_SET); + + $buffer = array(0); + $buf_pos = 1; + $buf_len = 1; + $has_datas = true; + do { + $bytes = array(); + // Buffer Filing + if ($buf_len - $buf_pos < $startLength) { + $buf = array_splice($buffer, $buf_pos); + $new = $this->_reloadBuffer($fp, 100); + if ($new) { + $buffer = array_merge($buf, $new); + $buf_len = count($buffer); + $buf_pos = 0; + } else { + $has_datas = false; + } + } + if ($buf_len - $buf_pos > 0) { + $size = 0; + for ($i = 0; $i < $startLength && isset($buffer[$buf_pos]); ++$i) { + ++$size; + $bytes[] = $buffer[$buf_pos++]; + } + $need = $this->_charReader->validateByteSequence( + $bytes, $size); + if ($need > 0) { + if ($buf_len - $buf_pos < $need) { + $new = $this->_reloadBuffer($fp, $need); + + if ($new) { + $buffer = array_merge($buffer, $new); + $buf_len = count($buffer); + } + } + for ($i = 0; $i < $need && isset($buffer[$buf_pos]); ++$i) { + $bytes[] = $buffer[$buf_pos++]; + } + } + $this->_array[] = $bytes; + ++$this->_array_size; + } + } while ($has_datas); + + fclose($fp); + } + + /** + * Move the internal pointer to $charOffset in the stream. + * + * @param int $charOffset + */ + public function setPointer($charOffset) + { + if ($charOffset > $this->_array_size) { + $charOffset = $this->_array_size; + } elseif ($charOffset < 0) { + $charOffset = 0; + } + $this->_offset = $charOffset; + } + + /** + * Empty the stream and reset the internal pointer. + */ + public function flushContents() + { + $this->_offset = 0; + $this->_array = array(); + $this->_array_size = 0; + } + + private function _reloadBuffer($fp, $len) + { + if (!feof($fp) && ($bytes = fread($fp, $len)) !== false) { + $buf = array(); + for ($i = 0, $len = strlen($bytes); $i < $len; ++$i) { + $buf[] = self::$_byteMap[$bytes[$i]]; + } + + return $buf; + } + + return false; + } + + private static function _initializeMaps() + { + if (!isset(self::$_charMap)) { + self::$_charMap = array(); + for ($byte = 0; $byte < 256; ++$byte) { + self::$_charMap[$byte] = chr($byte); + } + self::$_byteMap = array_flip(self::$_charMap); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream/NgCharacterStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream/NgCharacterStream.php new file mode 100644 index 0000000000..58bd140fd2 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream/NgCharacterStream.php @@ -0,0 +1,267 @@ + + */ +class Swift_CharacterStream_NgCharacterStream implements Swift_CharacterStream +{ + /** + * The char reader (lazy-loaded) for the current charset. + * + * @var Swift_CharacterReader + */ + private $_charReader; + + /** + * A factory for creating CharacterReader instances. + * + * @var Swift_CharacterReaderFactory + */ + private $_charReaderFactory; + + /** + * The character set this stream is using. + * + * @var string + */ + private $_charset; + + /** + * The data's stored as-is. + * + * @var string + */ + private $_datas = ''; + + /** + * Number of bytes in the stream. + * + * @var int + */ + private $_datasSize = 0; + + /** + * Map. + * + * @var mixed + */ + private $_map; + + /** + * Map Type. + * + * @var int + */ + private $_mapType = 0; + + /** + * Number of characters in the stream. + * + * @var int + */ + private $_charCount = 0; + + /** + * Position in the stream. + * + * @var int + */ + private $_currentPos = 0; + + /** + * Constructor. + * + * @param Swift_CharacterReaderFactory $factory + * @param string $charset + */ + public function __construct(Swift_CharacterReaderFactory $factory, $charset) + { + $this->setCharacterReaderFactory($factory); + $this->setCharacterSet($charset); + } + + /* -- Changing parameters of the stream -- */ + + /** + * Set the character set used in this CharacterStream. + * + * @param string $charset + */ + public function setCharacterSet($charset) + { + $this->_charset = $charset; + $this->_charReader = null; + $this->_mapType = 0; + } + + /** + * Set the CharacterReaderFactory for multi charset support. + * + * @param Swift_CharacterReaderFactory $factory + */ + public function setCharacterReaderFactory(Swift_CharacterReaderFactory $factory) + { + $this->_charReaderFactory = $factory; + } + + /** + * @see Swift_CharacterStream::flushContents() + */ + public function flushContents() + { + $this->_datas = null; + $this->_map = null; + $this->_charCount = 0; + $this->_currentPos = 0; + $this->_datasSize = 0; + } + + /** + * @see Swift_CharacterStream::importByteStream() + * + * @param Swift_OutputByteStream $os + */ + public function importByteStream(Swift_OutputByteStream $os) + { + $this->flushContents(); + $blocks = 512; + $os->setReadPointer(0); + while (false !== ($read = $os->read($blocks))) { + $this->write($read); + } + } + + /** + * @see Swift_CharacterStream::importString() + * + * @param string $string + */ + public function importString($string) + { + $this->flushContents(); + $this->write($string); + } + + /** + * @see Swift_CharacterStream::read() + * + * @param int $length + * + * @return string + */ + public function read($length) + { + if ($this->_currentPos >= $this->_charCount) { + return false; + } + $ret = false; + $length = $this->_currentPos + $length > $this->_charCount ? $this->_charCount - $this->_currentPos : $length; + switch ($this->_mapType) { + case Swift_CharacterReader::MAP_TYPE_FIXED_LEN: + $len = $length * $this->_map; + $ret = substr($this->_datas, + $this->_currentPos * $this->_map, + $len); + $this->_currentPos += $length; + break; + + case Swift_CharacterReader::MAP_TYPE_INVALID: + $ret = ''; + for (; $this->_currentPos < $length; ++$this->_currentPos) { + if (isset($this->_map[$this->_currentPos])) { + $ret .= '?'; + } else { + $ret .= $this->_datas[$this->_currentPos]; + } + } + break; + + case Swift_CharacterReader::MAP_TYPE_POSITIONS: + $end = $this->_currentPos + $length; + $end = $end > $this->_charCount ? $this->_charCount : $end; + $ret = ''; + $start = 0; + if ($this->_currentPos > 0) { + $start = $this->_map['p'][$this->_currentPos - 1]; + } + $to = $start; + for (; $this->_currentPos < $end; ++$this->_currentPos) { + if (isset($this->_map['i'][$this->_currentPos])) { + $ret .= substr($this->_datas, $start, $to - $start).'?'; + $start = $this->_map['p'][$this->_currentPos]; + } else { + $to = $this->_map['p'][$this->_currentPos]; + } + } + $ret .= substr($this->_datas, $start, $to - $start); + break; + } + + return $ret; + } + + /** + * @see Swift_CharacterStream::readBytes() + * + * @param int $length + * + * @return int[] + */ + public function readBytes($length) + { + $read = $this->read($length); + if ($read !== false) { + $ret = array_map('ord', str_split($read, 1)); + + return $ret; + } + + return false; + } + + /** + * @see Swift_CharacterStream::setPointer() + * + * @param int $charOffset + */ + public function setPointer($charOffset) + { + if ($this->_charCount < $charOffset) { + $charOffset = $this->_charCount; + } + $this->_currentPos = $charOffset; + } + + /** + * @see Swift_CharacterStream::write() + * + * @param string $chars + */ + public function write($chars) + { + if (!isset($this->_charReader)) { + $this->_charReader = $this->_charReaderFactory->getReaderFor( + $this->_charset); + $this->_map = array(); + $this->_mapType = $this->_charReader->getMapType(); + } + $ignored = ''; + $this->_datas .= $chars; + $this->_charCount += $this->_charReader->getCharPositions(substr($this->_datas, $this->_datasSize), $this->_datasSize, $this->_map, $ignored); + if ($ignored !== false) { + $this->_datasSize = strlen($this->_datas) - strlen($ignored); + } else { + $this->_datasSize = strlen($this->_datas); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ConfigurableSpool.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ConfigurableSpool.php new file mode 100644 index 0000000000..4ae5bacfc4 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ConfigurableSpool.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Base class for Spools (implements time and message limits). + * + * @author Fabien Potencier + */ +abstract class Swift_ConfigurableSpool implements Swift_Spool +{ + /** The maximum number of messages to send per flush */ + private $_message_limit; + + /** The time limit per flush */ + private $_time_limit; + + /** + * Sets the maximum number of messages to send per flush. + * + * @param int $limit + */ + public function setMessageLimit($limit) + { + $this->_message_limit = (int) $limit; + } + + /** + * Gets the maximum number of messages to send per flush. + * + * @return int The limit + */ + public function getMessageLimit() + { + return $this->_message_limit; + } + + /** + * Sets the time limit (in seconds) per flush. + * + * @param int $limit The limit + */ + public function setTimeLimit($limit) + { + $this->_time_limit = (int) $limit; + } + + /** + * Gets the time limit (in seconds) per flush. + * + * @return int The limit + */ + public function getTimeLimit() + { + return $this->_time_limit; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/DependencyContainer.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/DependencyContainer.php new file mode 100644 index 0000000000..8c1074a3a3 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/DependencyContainer.php @@ -0,0 +1,373 @@ +_store); + } + + /** + * Test if an item is registered in this container with the given name. + * + * @see register() + * + * @param string $itemName + * + * @return bool + */ + public function has($itemName) + { + return array_key_exists($itemName, $this->_store) + && isset($this->_store[$itemName]['lookupType']); + } + + /** + * Lookup the item with the given $itemName. + * + * @see register() + * + * @param string $itemName + * + * @throws Swift_DependencyException If the dependency is not found + * + * @return mixed + */ + public function lookup($itemName) + { + if (!$this->has($itemName)) { + throw new Swift_DependencyException( + 'Cannot lookup dependency "'.$itemName.'" since it is not registered.' + ); + } + + switch ($this->_store[$itemName]['lookupType']) { + case self::TYPE_ALIAS: + return $this->_createAlias($itemName); + case self::TYPE_VALUE: + return $this->_getValue($itemName); + case self::TYPE_INSTANCE: + return $this->_createNewInstance($itemName); + case self::TYPE_SHARED: + return $this->_createSharedInstance($itemName); + } + } + + /** + * Create an array of arguments passed to the constructor of $itemName. + * + * @param string $itemName + * + * @return array + */ + public function createDependenciesFor($itemName) + { + $args = array(); + if (isset($this->_store[$itemName]['args'])) { + $args = $this->_resolveArgs($this->_store[$itemName]['args']); + } + + return $args; + } + + /** + * Register a new dependency with $itemName. + * + * This method returns the current DependencyContainer instance because it + * requires the use of the fluid interface to set the specific details for the + * dependency. + * + * @see asNewInstanceOf(), asSharedInstanceOf(), asValue() + * + * @param string $itemName + * + * @return Swift_DependencyContainer + */ + public function register($itemName) + { + $this->_store[$itemName] = array(); + $this->_endPoint = &$this->_store[$itemName]; + + return $this; + } + + /** + * Specify the previously registered item as a literal value. + * + * {@link register()} must be called before this will work. + * + * @param mixed $value + * + * @return Swift_DependencyContainer + */ + public function asValue($value) + { + $endPoint = &$this->_getEndPoint(); + $endPoint['lookupType'] = self::TYPE_VALUE; + $endPoint['value'] = $value; + + return $this; + } + + /** + * Specify the previously registered item as an alias of another item. + * + * @param string $lookup + * + * @return Swift_DependencyContainer + */ + public function asAliasOf($lookup) + { + $endPoint = &$this->_getEndPoint(); + $endPoint['lookupType'] = self::TYPE_ALIAS; + $endPoint['ref'] = $lookup; + + return $this; + } + + /** + * Specify the previously registered item as a new instance of $className. + * + * {@link register()} must be called before this will work. + * Any arguments can be set with {@link withDependencies()}, + * {@link addConstructorValue()} or {@link addConstructorLookup()}. + * + * @see withDependencies(), addConstructorValue(), addConstructorLookup() + * + * @param string $className + * + * @return Swift_DependencyContainer + */ + public function asNewInstanceOf($className) + { + $endPoint = &$this->_getEndPoint(); + $endPoint['lookupType'] = self::TYPE_INSTANCE; + $endPoint['className'] = $className; + + return $this; + } + + /** + * Specify the previously registered item as a shared instance of $className. + * + * {@link register()} must be called before this will work. + * + * @param string $className + * + * @return Swift_DependencyContainer + */ + public function asSharedInstanceOf($className) + { + $endPoint = &$this->_getEndPoint(); + $endPoint['lookupType'] = self::TYPE_SHARED; + $endPoint['className'] = $className; + + return $this; + } + + /** + * Specify a list of injected dependencies for the previously registered item. + * + * This method takes an array of lookup names. + * + * @see addConstructorValue(), addConstructorLookup() + * + * @param array $lookups + * + * @return Swift_DependencyContainer + */ + public function withDependencies(array $lookups) + { + $endPoint = &$this->_getEndPoint(); + $endPoint['args'] = array(); + foreach ($lookups as $lookup) { + $this->addConstructorLookup($lookup); + } + + return $this; + } + + /** + * Specify a literal (non looked up) value for the constructor of the + * previously registered item. + * + * @see withDependencies(), addConstructorLookup() + * + * @param mixed $value + * + * @return Swift_DependencyContainer + */ + public function addConstructorValue($value) + { + $endPoint = &$this->_getEndPoint(); + if (!isset($endPoint['args'])) { + $endPoint['args'] = array(); + } + $endPoint['args'][] = array('type' => 'value', 'item' => $value); + + return $this; + } + + /** + * Specify a dependency lookup for the constructor of the previously + * registered item. + * + * @see withDependencies(), addConstructorValue() + * + * @param string $lookup + * + * @return Swift_DependencyContainer + */ + public function addConstructorLookup($lookup) + { + $endPoint = &$this->_getEndPoint(); + if (!isset($this->_endPoint['args'])) { + $endPoint['args'] = array(); + } + $endPoint['args'][] = array('type' => 'lookup', 'item' => $lookup); + + return $this; + } + + /** Get the literal value with $itemName */ + private function _getValue($itemName) + { + return $this->_store[$itemName]['value']; + } + + /** Resolve an alias to another item */ + private function _createAlias($itemName) + { + return $this->lookup($this->_store[$itemName]['ref']); + } + + /** Create a fresh instance of $itemName */ + private function _createNewInstance($itemName) + { + $reflector = new ReflectionClass($this->_store[$itemName]['className']); + if ($reflector->getConstructor()) { + return $reflector->newInstanceArgs( + $this->createDependenciesFor($itemName) + ); + } + + return $reflector->newInstance(); + } + + /** Create and register a shared instance of $itemName */ + private function _createSharedInstance($itemName) + { + if (!isset($this->_store[$itemName]['instance'])) { + $this->_store[$itemName]['instance'] = $this->_createNewInstance($itemName); + } + + return $this->_store[$itemName]['instance']; + } + + /** Get the current endpoint in the store */ + private function &_getEndPoint() + { + if (!isset($this->_endPoint)) { + throw new BadMethodCallException( + 'Component must first be registered by calling register()' + ); + } + + return $this->_endPoint; + } + + /** Get an argument list with dependencies resolved */ + private function _resolveArgs(array $args) + { + $resolved = array(); + foreach ($args as $argDefinition) { + switch ($argDefinition['type']) { + case 'lookup': + $resolved[] = $this->_lookupRecursive($argDefinition['item']); + break; + case 'value': + $resolved[] = $argDefinition['item']; + break; + } + } + + return $resolved; + } + + /** Resolve a single dependency with an collections */ + private function _lookupRecursive($item) + { + if (is_array($item)) { + $collection = array(); + foreach ($item as $k => $v) { + $collection[$k] = $this->_lookupRecursive($v); + } + + return $collection; + } + + return $this->lookup($item); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/DependencyException.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/DependencyException.php new file mode 100644 index 0000000000..799d38d833 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/DependencyException.php @@ -0,0 +1,27 @@ +createDependenciesFor('mime.embeddedfile') + ); + + $this->setBody($data); + $this->setFilename($filename); + if ($contentType) { + $this->setContentType($contentType); + } + } + + /** + * Create a new EmbeddedFile. + * + * @param string|Swift_OutputByteStream $data + * @param string $filename + * @param string $contentType + * + * @return Swift_Mime_EmbeddedFile + */ + public static function newInstance($data = null, $filename = null, $contentType = null) + { + return new self($data, $filename, $contentType); + } + + /** + * Create a new EmbeddedFile from a filesystem path. + * + * @param string $path + * + * @return Swift_Mime_EmbeddedFile + */ + public static function fromPath($path) + { + return self::newInstance()->setFile( + new Swift_ByteStream_FileByteStream($path) + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder.php new file mode 100644 index 0000000000..2073abca1b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder.php @@ -0,0 +1,28 @@ += $maxLineLength || 76 < $maxLineLength) { + $maxLineLength = 76; + } + + $encodedString = base64_encode($string); + $firstLine = ''; + + if (0 != $firstLineOffset) { + $firstLine = substr( + $encodedString, 0, $maxLineLength - $firstLineOffset + )."\r\n"; + $encodedString = substr( + $encodedString, $maxLineLength - $firstLineOffset + ); + } + + return $firstLine.trim(chunk_split($encodedString, $maxLineLength, "\r\n")); + } + + /** + * Does nothing. + */ + public function charsetChanged($charset) + { + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/QpEncoder.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/QpEncoder.php new file mode 100644 index 0000000000..8a81fe3970 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/QpEncoder.php @@ -0,0 +1,300 @@ + '=00', 1 => '=01', 2 => '=02', 3 => '=03', 4 => '=04', + 5 => '=05', 6 => '=06', 7 => '=07', 8 => '=08', 9 => '=09', + 10 => '=0A', 11 => '=0B', 12 => '=0C', 13 => '=0D', 14 => '=0E', + 15 => '=0F', 16 => '=10', 17 => '=11', 18 => '=12', 19 => '=13', + 20 => '=14', 21 => '=15', 22 => '=16', 23 => '=17', 24 => '=18', + 25 => '=19', 26 => '=1A', 27 => '=1B', 28 => '=1C', 29 => '=1D', + 30 => '=1E', 31 => '=1F', 32 => '=20', 33 => '=21', 34 => '=22', + 35 => '=23', 36 => '=24', 37 => '=25', 38 => '=26', 39 => '=27', + 40 => '=28', 41 => '=29', 42 => '=2A', 43 => '=2B', 44 => '=2C', + 45 => '=2D', 46 => '=2E', 47 => '=2F', 48 => '=30', 49 => '=31', + 50 => '=32', 51 => '=33', 52 => '=34', 53 => '=35', 54 => '=36', + 55 => '=37', 56 => '=38', 57 => '=39', 58 => '=3A', 59 => '=3B', + 60 => '=3C', 61 => '=3D', 62 => '=3E', 63 => '=3F', 64 => '=40', + 65 => '=41', 66 => '=42', 67 => '=43', 68 => '=44', 69 => '=45', + 70 => '=46', 71 => '=47', 72 => '=48', 73 => '=49', 74 => '=4A', + 75 => '=4B', 76 => '=4C', 77 => '=4D', 78 => '=4E', 79 => '=4F', + 80 => '=50', 81 => '=51', 82 => '=52', 83 => '=53', 84 => '=54', + 85 => '=55', 86 => '=56', 87 => '=57', 88 => '=58', 89 => '=59', + 90 => '=5A', 91 => '=5B', 92 => '=5C', 93 => '=5D', 94 => '=5E', + 95 => '=5F', 96 => '=60', 97 => '=61', 98 => '=62', 99 => '=63', + 100 => '=64', 101 => '=65', 102 => '=66', 103 => '=67', 104 => '=68', + 105 => '=69', 106 => '=6A', 107 => '=6B', 108 => '=6C', 109 => '=6D', + 110 => '=6E', 111 => '=6F', 112 => '=70', 113 => '=71', 114 => '=72', + 115 => '=73', 116 => '=74', 117 => '=75', 118 => '=76', 119 => '=77', + 120 => '=78', 121 => '=79', 122 => '=7A', 123 => '=7B', 124 => '=7C', + 125 => '=7D', 126 => '=7E', 127 => '=7F', 128 => '=80', 129 => '=81', + 130 => '=82', 131 => '=83', 132 => '=84', 133 => '=85', 134 => '=86', + 135 => '=87', 136 => '=88', 137 => '=89', 138 => '=8A', 139 => '=8B', + 140 => '=8C', 141 => '=8D', 142 => '=8E', 143 => '=8F', 144 => '=90', + 145 => '=91', 146 => '=92', 147 => '=93', 148 => '=94', 149 => '=95', + 150 => '=96', 151 => '=97', 152 => '=98', 153 => '=99', 154 => '=9A', + 155 => '=9B', 156 => '=9C', 157 => '=9D', 158 => '=9E', 159 => '=9F', + 160 => '=A0', 161 => '=A1', 162 => '=A2', 163 => '=A3', 164 => '=A4', + 165 => '=A5', 166 => '=A6', 167 => '=A7', 168 => '=A8', 169 => '=A9', + 170 => '=AA', 171 => '=AB', 172 => '=AC', 173 => '=AD', 174 => '=AE', + 175 => '=AF', 176 => '=B0', 177 => '=B1', 178 => '=B2', 179 => '=B3', + 180 => '=B4', 181 => '=B5', 182 => '=B6', 183 => '=B7', 184 => '=B8', + 185 => '=B9', 186 => '=BA', 187 => '=BB', 188 => '=BC', 189 => '=BD', + 190 => '=BE', 191 => '=BF', 192 => '=C0', 193 => '=C1', 194 => '=C2', + 195 => '=C3', 196 => '=C4', 197 => '=C5', 198 => '=C6', 199 => '=C7', + 200 => '=C8', 201 => '=C9', 202 => '=CA', 203 => '=CB', 204 => '=CC', + 205 => '=CD', 206 => '=CE', 207 => '=CF', 208 => '=D0', 209 => '=D1', + 210 => '=D2', 211 => '=D3', 212 => '=D4', 213 => '=D5', 214 => '=D6', + 215 => '=D7', 216 => '=D8', 217 => '=D9', 218 => '=DA', 219 => '=DB', + 220 => '=DC', 221 => '=DD', 222 => '=DE', 223 => '=DF', 224 => '=E0', + 225 => '=E1', 226 => '=E2', 227 => '=E3', 228 => '=E4', 229 => '=E5', + 230 => '=E6', 231 => '=E7', 232 => '=E8', 233 => '=E9', 234 => '=EA', + 235 => '=EB', 236 => '=EC', 237 => '=ED', 238 => '=EE', 239 => '=EF', + 240 => '=F0', 241 => '=F1', 242 => '=F2', 243 => '=F3', 244 => '=F4', + 245 => '=F5', 246 => '=F6', 247 => '=F7', 248 => '=F8', 249 => '=F9', + 250 => '=FA', 251 => '=FB', 252 => '=FC', 253 => '=FD', 254 => '=FE', + 255 => '=FF', + ); + + protected static $_safeMapShare = array(); + + /** + * A map of non-encoded ascii characters. + * + * @var string[] + */ + protected $_safeMap = array(); + + /** + * Creates a new QpEncoder for the given CharacterStream. + * + * @param Swift_CharacterStream $charStream to use for reading characters + * @param Swift_StreamFilter $filter if input should be canonicalized + */ + public function __construct(Swift_CharacterStream $charStream, Swift_StreamFilter $filter = null) + { + $this->_charStream = $charStream; + if (!isset(self::$_safeMapShare[$this->getSafeMapShareId()])) { + $this->initSafeMap(); + self::$_safeMapShare[$this->getSafeMapShareId()] = $this->_safeMap; + } else { + $this->_safeMap = self::$_safeMapShare[$this->getSafeMapShareId()]; + } + $this->_filter = $filter; + } + + public function __sleep() + { + return array('_charStream', '_filter'); + } + + public function __wakeup() + { + if (!isset(self::$_safeMapShare[$this->getSafeMapShareId()])) { + $this->initSafeMap(); + self::$_safeMapShare[$this->getSafeMapShareId()] = $this->_safeMap; + } else { + $this->_safeMap = self::$_safeMapShare[$this->getSafeMapShareId()]; + } + } + + protected function getSafeMapShareId() + { + return get_class($this); + } + + protected function initSafeMap() + { + foreach (array_merge( + array(0x09, 0x20), range(0x21, 0x3C), range(0x3E, 0x7E)) as $byte) { + $this->_safeMap[$byte] = chr($byte); + } + } + + /** + * Takes an unencoded string and produces a QP encoded string from it. + * + * QP encoded strings have a maximum line length of 76 characters. + * If the first line needs to be shorter, indicate the difference with + * $firstLineOffset. + * + * @param string $string to encode + * @param int $firstLineOffset, optional + * @param int $maxLineLength, optional 0 indicates the default of 76 chars + * + * @return string + */ + public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0) + { + if ($maxLineLength > 76 || $maxLineLength <= 0) { + $maxLineLength = 76; + } + + $thisLineLength = $maxLineLength - $firstLineOffset; + + $lines = array(); + $lNo = 0; + $lines[$lNo] = ''; + $currentLine = &$lines[$lNo++]; + $size = $lineLen = 0; + + $this->_charStream->flushContents(); + $this->_charStream->importString($string); + + // Fetching more than 4 chars at one is slower, as is fetching fewer bytes + // Conveniently 4 chars is the UTF-8 safe number since UTF-8 has up to 6 + // bytes per char and (6 * 4 * 3 = 72 chars per line) * =NN is 3 bytes + while (false !== $bytes = $this->_nextSequence()) { + // If we're filtering the input + if (isset($this->_filter)) { + // If we can't filter because we need more bytes + while ($this->_filter->shouldBuffer($bytes)) { + // Then collect bytes into the buffer + if (false === $moreBytes = $this->_nextSequence(1)) { + break; + } + + foreach ($moreBytes as $b) { + $bytes[] = $b; + } + } + // And filter them + $bytes = $this->_filter->filter($bytes); + } + + $enc = $this->_encodeByteSequence($bytes, $size); + + $i = strpos($enc, '=0D=0A'); + $newLineLength = $lineLen + ($i === false ? $size : $i); + + if ($currentLine && $newLineLength >= $thisLineLength) { + $lines[$lNo] = ''; + $currentLine = &$lines[$lNo++]; + $thisLineLength = $maxLineLength; + $lineLen = 0; + } + + $currentLine .= $enc; + + if ($i === false) { + $lineLen += $size; + } else { + // 6 is the length of '=0D=0A'. + $lineLen = $size - strrpos($enc, '=0D=0A') - 6; + } + } + + return $this->_standardize(implode("=\r\n", $lines)); + } + + /** + * Updates the charset used. + * + * @param string $charset + */ + public function charsetChanged($charset) + { + $this->_charStream->setCharacterSet($charset); + } + + /** + * Encode the given byte array into a verbatim QP form. + * + * @param integer[] $bytes + * @param int $size + * + * @return string + */ + protected function _encodeByteSequence(array $bytes, &$size) + { + $ret = ''; + $size = 0; + foreach ($bytes as $b) { + if (isset($this->_safeMap[$b])) { + $ret .= $this->_safeMap[$b]; + ++$size; + } else { + $ret .= self::$_qpMap[$b]; + $size += 3; + } + } + + return $ret; + } + + /** + * Get the next sequence of bytes to read from the char stream. + * + * @param int $size number of bytes to read + * + * @return integer[] + */ + protected function _nextSequence($size = 4) + { + return $this->_charStream->readBytes($size); + } + + /** + * Make sure CRLF is correct and HT/SPACE are in valid places. + * + * @param string $string + * + * @return string + */ + protected function _standardize($string) + { + $string = str_replace(array("\t=0D=0A", ' =0D=0A', '=0D=0A'), + array("=09\r\n", "=20\r\n", "\r\n"), $string + ); + switch ($end = ord(substr($string, -1))) { + case 0x09: + case 0x20: + $string = substr_replace($string, self::$_qpMap[$end], -1); + } + + return $string; + } + + /** + * Make a deep copy of object. + */ + public function __clone() + { + $this->_charStream = clone $this->_charStream; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/Rfc2231Encoder.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/Rfc2231Encoder.php new file mode 100644 index 0000000000..b0215e8838 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/Rfc2231Encoder.php @@ -0,0 +1,92 @@ +_charStream = $charStream; + } + + /** + * Takes an unencoded string and produces a string encoded according to + * RFC 2231 from it. + * + * @param string $string + * @param int $firstLineOffset + * @param int $maxLineLength optional, 0 indicates the default of 75 bytes + * + * @return string + */ + public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0) + { + $lines = array(); + $lineCount = 0; + $lines[] = ''; + $currentLine = &$lines[$lineCount++]; + + if (0 >= $maxLineLength) { + $maxLineLength = 75; + } + + $this->_charStream->flushContents(); + $this->_charStream->importString($string); + + $thisLineLength = $maxLineLength - $firstLineOffset; + + while (false !== $char = $this->_charStream->read(4)) { + $encodedChar = rawurlencode($char); + if (0 != strlen($currentLine) + && strlen($currentLine.$encodedChar) > $thisLineLength) { + $lines[] = ''; + $currentLine = &$lines[$lineCount++]; + $thisLineLength = $maxLineLength; + } + $currentLine .= $encodedChar; + } + + return implode("\r\n", $lines); + } + + /** + * Updates the charset used. + * + * @param string $charset + */ + public function charsetChanged($charset) + { + $this->_charStream->setCharacterSet($charset); + } + + /** + * Make a deep copy of object. + */ + public function __clone() + { + $this->_charStream = clone $this->_charStream; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoding.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoding.php new file mode 100644 index 0000000000..253977b608 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoding.php @@ -0,0 +1,64 @@ +lookup($key); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/CommandEvent.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/CommandEvent.php new file mode 100644 index 0000000000..7dc381d984 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/CommandEvent.php @@ -0,0 +1,65 @@ +_command = $command; + $this->_successCodes = $successCodes; + } + + /** + * Get the command which was sent to the server. + * + * @return string + */ + public function getCommand() + { + return $this->_command; + } + + /** + * Get the numeric response codes which indicate success for this command. + * + * @return integer[] + */ + public function getSuccessCodes() + { + return $this->_successCodes; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/CommandListener.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/CommandListener.php new file mode 100644 index 0000000000..7545404e76 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/CommandListener.php @@ -0,0 +1,24 @@ +_source = $source; + } + + /** + * Get the source object of this event. + * + * @return object + */ + public function getSource() + { + return $this->_source; + } + + /** + * Prevent this Event from bubbling any further up the stack. + * + * @param bool $cancel, optional + */ + public function cancelBubble($cancel = true) + { + $this->_bubbleCancelled = $cancel; + } + + /** + * Returns true if this Event will not bubble any further up the stack. + * + * @return bool + */ + public function bubbleCancelled() + { + return $this->_bubbleCancelled; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/ResponseEvent.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/ResponseEvent.php new file mode 100644 index 0000000000..2e92ba9404 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/ResponseEvent.php @@ -0,0 +1,65 @@ +_response = $response; + $this->_valid = $valid; + } + + /** + * Get the response which was received from the server. + * + * @return string + */ + public function getResponse() + { + return $this->_response; + } + + /** + * Get the success status of this Event. + * + * @return bool + */ + public function isValid() + { + return $this->_valid; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/ResponseListener.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/ResponseListener.php new file mode 100644 index 0000000000..c40919d210 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/ResponseListener.php @@ -0,0 +1,24 @@ +_message = $message; + $this->_result = self::RESULT_PENDING; + } + + /** + * Get the Transport used to send the Message. + * + * @return Swift_Transport + */ + public function getTransport() + { + return $this->getSource(); + } + + /** + * Get the Message being sent. + * + * @return Swift_Mime_Message + */ + public function getMessage() + { + return $this->_message; + } + + /** + * Set the array of addresses that failed in sending. + * + * @param array $recipients + */ + public function setFailedRecipients($recipients) + { + $this->_failedRecipients = $recipients; + } + + /** + * Get an recipient addresses which were not accepted for delivery. + * + * @return string[] + */ + public function getFailedRecipients() + { + return $this->_failedRecipients; + } + + /** + * Set the result of sending. + * + * @param int $result + */ + public function setResult($result) + { + $this->_result = $result; + } + + /** + * Get the result of this Event. + * + * The return value is a bitmask from + * {@see RESULT_PENDING, RESULT_SUCCESS, RESULT_TENTATIVE, RESULT_FAILED} + * + * @return int + */ + public function getResult() + { + return $this->_result; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/SendListener.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/SendListener.php new file mode 100644 index 0000000000..d922e1bfa9 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/SendListener.php @@ -0,0 +1,31 @@ +_eventMap = array( + 'Swift_Events_CommandEvent' => 'Swift_Events_CommandListener', + 'Swift_Events_ResponseEvent' => 'Swift_Events_ResponseListener', + 'Swift_Events_SendEvent' => 'Swift_Events_SendListener', + 'Swift_Events_TransportChangeEvent' => 'Swift_Events_TransportChangeListener', + 'Swift_Events_TransportExceptionEvent' => 'Swift_Events_TransportExceptionListener', + ); + } + + /** + * Create a new SendEvent for $source and $message. + * + * @param Swift_Transport $source + * @param Swift_Mime_Message + * + * @return Swift_Events_SendEvent + */ + public function createSendEvent(Swift_Transport $source, Swift_Mime_Message $message) + { + return new Swift_Events_SendEvent($source, $message); + } + + /** + * Create a new CommandEvent for $source and $command. + * + * @param Swift_Transport $source + * @param string $command That will be executed + * @param array $successCodes That are needed + * + * @return Swift_Events_CommandEvent + */ + public function createCommandEvent(Swift_Transport $source, $command, $successCodes = array()) + { + return new Swift_Events_CommandEvent($source, $command, $successCodes); + } + + /** + * Create a new ResponseEvent for $source and $response. + * + * @param Swift_Transport $source + * @param string $response + * @param bool $valid If the response is valid + * + * @return Swift_Events_ResponseEvent + */ + public function createResponseEvent(Swift_Transport $source, $response, $valid) + { + return new Swift_Events_ResponseEvent($source, $response, $valid); + } + + /** + * Create a new TransportChangeEvent for $source. + * + * @param Swift_Transport $source + * + * @return Swift_Events_TransportChangeEvent + */ + public function createTransportChangeEvent(Swift_Transport $source) + { + return new Swift_Events_TransportChangeEvent($source); + } + + /** + * Create a new TransportExceptionEvent for $source. + * + * @param Swift_Transport $source + * @param Swift_TransportException $ex + * + * @return Swift_Events_TransportExceptionEvent + */ + public function createTransportExceptionEvent(Swift_Transport $source, Swift_TransportException $ex) + { + return new Swift_Events_TransportExceptionEvent($source, $ex); + } + + /** + * Bind an event listener to this dispatcher. + * + * @param Swift_Events_EventListener $listener + */ + public function bindEventListener(Swift_Events_EventListener $listener) + { + foreach ($this->_listeners as $l) { + // Already loaded + if ($l === $listener) { + return; + } + } + $this->_listeners[] = $listener; + } + + /** + * Dispatch the given Event to all suitable listeners. + * + * @param Swift_Events_EventObject $evt + * @param string $target method + */ + public function dispatchEvent(Swift_Events_EventObject $evt, $target) + { + $this->_prepareBubbleQueue($evt); + $this->_bubble($evt, $target); + } + + /** Queue listeners on a stack ready for $evt to be bubbled up it */ + private function _prepareBubbleQueue(Swift_Events_EventObject $evt) + { + $this->_bubbleQueue = array(); + $evtClass = get_class($evt); + foreach ($this->_listeners as $listener) { + if (array_key_exists($evtClass, $this->_eventMap) + && ($listener instanceof $this->_eventMap[$evtClass])) { + $this->_bubbleQueue[] = $listener; + } + } + } + + /** Bubble $evt up the stack calling $target() on each listener */ + private function _bubble(Swift_Events_EventObject $evt, $target) + { + if (!$evt->bubbleCancelled() && $listener = array_shift($this->_bubbleQueue)) { + $listener->$target($evt); + $this->_bubble($evt, $target); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportChangeEvent.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportChangeEvent.php new file mode 100644 index 0000000000..a8972fda9a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportChangeEvent.php @@ -0,0 +1,27 @@ +getSource(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportChangeListener.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportChangeListener.php new file mode 100644 index 0000000000..253165de89 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportChangeListener.php @@ -0,0 +1,45 @@ +_exception = $ex; + } + + /** + * Get the TransportException thrown. + * + * @return Swift_TransportException + */ + public function getException() + { + return $this->_exception; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportExceptionListener.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportExceptionListener.php new file mode 100644 index 0000000000..cc3c099379 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportExceptionListener.php @@ -0,0 +1,24 @@ +createDependenciesFor('transport.failover') + ); + + $this->setTransports($transports); + } + + /** + * Create a new FailoverTransport instance. + * + * @param Swift_Transport[] $transports + * + * @return Swift_FailoverTransport + */ + public static function newInstance($transports = array()) + { + return new self($transports); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/FileSpool.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/FileSpool.php new file mode 100644 index 0000000000..c82c5dbf11 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/FileSpool.php @@ -0,0 +1,208 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Stores Messages on the filesystem. + * + * @author Fabien Potencier + * @author Xavier De Cock + */ +class Swift_FileSpool extends Swift_ConfigurableSpool +{ + /** The spool directory */ + private $_path; + + /** + * File WriteRetry Limit. + * + * @var int + */ + private $_retryLimit = 10; + + /** + * Create a new FileSpool. + * + * @param string $path + * + * @throws Swift_IoException + */ + public function __construct($path) + { + $this->_path = $path; + + if (!file_exists($this->_path)) { + if (!mkdir($this->_path, 0777, true)) { + throw new Swift_IoException(sprintf('Unable to create path "%s".', $this->_path)); + } + } + } + + /** + * Tests if this Spool mechanism has started. + * + * @return bool + */ + public function isStarted() + { + return true; + } + + /** + * Starts this Spool mechanism. + */ + public function start() + { + } + + /** + * Stops this Spool mechanism. + */ + public function stop() + { + } + + /** + * Allow to manage the enqueuing retry limit. + * + * Default, is ten and allows over 64^20 different fileNames + * + * @param int $limit + */ + public function setRetryLimit($limit) + { + $this->_retryLimit = $limit; + } + + /** + * Queues a message. + * + * @param Swift_Mime_Message $message The message to store + * + * @throws Swift_IoException + * + * @return bool + */ + public function queueMessage(Swift_Mime_Message $message) + { + $ser = serialize($message); + $fileName = $this->_path.'/'.$this->getRandomString(10); + for ($i = 0; $i < $this->_retryLimit; ++$i) { + /* We try an exclusive creation of the file. This is an atomic operation, it avoid locking mechanism */ + $fp = @fopen($fileName.'.message', 'x'); + if (false !== $fp) { + if (false === fwrite($fp, $ser)) { + return false; + } + + return fclose($fp); + } else { + /* The file already exists, we try a longer fileName */ + $fileName .= $this->getRandomString(1); + } + } + + throw new Swift_IoException(sprintf('Unable to create a file for enqueuing Message in "%s".', $this->_path)); + } + + /** + * Execute a recovery if for any reason a process is sending for too long. + * + * @param int $timeout in second Defaults is for very slow smtp responses + */ + public function recover($timeout = 900) + { + foreach (new DirectoryIterator($this->_path) as $file) { + $file = $file->getRealPath(); + + if (substr($file, -16) == '.message.sending') { + $lockedtime = filectime($file); + if ((time() - $lockedtime) > $timeout) { + rename($file, substr($file, 0, -8)); + } + } + } + } + + /** + * Sends messages using the given transport instance. + * + * @param Swift_Transport $transport A transport instance + * @param string[] $failedRecipients An array of failures by-reference + * + * @return int The number of sent e-mail's + */ + public function flushQueue(Swift_Transport $transport, &$failedRecipients = null) + { + $directoryIterator = new DirectoryIterator($this->_path); + + /* Start the transport only if there are queued files to send */ + if (!$transport->isStarted()) { + foreach ($directoryIterator as $file) { + if (substr($file->getRealPath(), -8) == '.message') { + $transport->start(); + break; + } + } + } + + $failedRecipients = (array) $failedRecipients; + $count = 0; + $time = time(); + foreach ($directoryIterator as $file) { + $file = $file->getRealPath(); + + if (substr($file, -8) != '.message') { + continue; + } + + /* We try a rename, it's an atomic operation, and avoid locking the file */ + if (rename($file, $file.'.sending')) { + $message = unserialize(file_get_contents($file.'.sending')); + + $count += $transport->send($message, $failedRecipients); + + unlink($file.'.sending'); + } else { + /* This message has just been catched by another process */ + continue; + } + + if ($this->getMessageLimit() && $count >= $this->getMessageLimit()) { + break; + } + + if ($this->getTimeLimit() && (time() - $time) >= $this->getTimeLimit()) { + break; + } + } + + return $count; + } + + /** + * Returns a random string needed to generate a fileName for the queue. + * + * @param int $count + * + * @return string + */ + protected function getRandomString($count) + { + // This string MUST stay FS safe, avoid special chars + $base = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-'; + $ret = ''; + $strlen = strlen($base); + for ($i = 0; $i < $count; ++$i) { + $ret .= $base[((int) rand(0, $strlen - 1))]; + } + + return $ret; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/FileStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/FileStream.php new file mode 100644 index 0000000000..0b24db1ce4 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/FileStream.php @@ -0,0 +1,24 @@ +setFile( + new Swift_ByteStream_FileByteStream($path) + ); + + return $image; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/InputByteStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/InputByteStream.php new file mode 100644 index 0000000000..56efc7593a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/InputByteStream.php @@ -0,0 +1,75 @@ +_stream = $stream; + } + + /** + * Set a string into the cache under $itemKey for the namespace $nsKey. + * + * @see MODE_WRITE, MODE_APPEND + * + * @param string $nsKey + * @param string $itemKey + * @param string $string + * @param int $mode + */ + public function setString($nsKey, $itemKey, $string, $mode) + { + $this->_prepareCache($nsKey); + switch ($mode) { + case self::MODE_WRITE: + $this->_contents[$nsKey][$itemKey] = $string; + break; + case self::MODE_APPEND: + if (!$this->hasKey($nsKey, $itemKey)) { + $this->_contents[$nsKey][$itemKey] = ''; + } + $this->_contents[$nsKey][$itemKey] .= $string; + break; + default: + throw new Swift_SwiftException( + 'Invalid mode ['.$mode.'] used to set nsKey='. + $nsKey.', itemKey='.$itemKey + ); + } + } + + /** + * Set a ByteStream into the cache under $itemKey for the namespace $nsKey. + * + * @see MODE_WRITE, MODE_APPEND + * + * @param string $nsKey + * @param string $itemKey + * @param Swift_OutputByteStream $os + * @param int $mode + */ + public function importFromByteStream($nsKey, $itemKey, Swift_OutputByteStream $os, $mode) + { + $this->_prepareCache($nsKey); + switch ($mode) { + case self::MODE_WRITE: + $this->clearKey($nsKey, $itemKey); + case self::MODE_APPEND: + if (!$this->hasKey($nsKey, $itemKey)) { + $this->_contents[$nsKey][$itemKey] = ''; + } + while (false !== $bytes = $os->read(8192)) { + $this->_contents[$nsKey][$itemKey] .= $bytes; + } + break; + default: + throw new Swift_SwiftException( + 'Invalid mode ['.$mode.'] used to set nsKey='. + $nsKey.', itemKey='.$itemKey + ); + } + } + + /** + * Provides a ByteStream which when written to, writes data to $itemKey. + * + * NOTE: The stream will always write in append mode. + * + * @param string $nsKey + * @param string $itemKey + * @param Swift_InputByteStream $writeThrough + * + * @return Swift_InputByteStream + */ + public function getInputByteStream($nsKey, $itemKey, Swift_InputByteStream $writeThrough = null) + { + $is = clone $this->_stream; + $is->setKeyCache($this); + $is->setNsKey($nsKey); + $is->setItemKey($itemKey); + if (isset($writeThrough)) { + $is->setWriteThroughStream($writeThrough); + } + + return $is; + } + + /** + * Get data back out of the cache as a string. + * + * @param string $nsKey + * @param string $itemKey + * + * @return string + */ + public function getString($nsKey, $itemKey) + { + $this->_prepareCache($nsKey); + if ($this->hasKey($nsKey, $itemKey)) { + return $this->_contents[$nsKey][$itemKey]; + } + } + + /** + * Get data back out of the cache as a ByteStream. + * + * @param string $nsKey + * @param string $itemKey + * @param Swift_InputByteStream $is to write the data to + */ + public function exportToByteStream($nsKey, $itemKey, Swift_InputByteStream $is) + { + $this->_prepareCache($nsKey); + $is->write($this->getString($nsKey, $itemKey)); + } + + /** + * Check if the given $itemKey exists in the namespace $nsKey. + * + * @param string $nsKey + * @param string $itemKey + * + * @return bool + */ + public function hasKey($nsKey, $itemKey) + { + $this->_prepareCache($nsKey); + + return array_key_exists($itemKey, $this->_contents[$nsKey]); + } + + /** + * Clear data for $itemKey in the namespace $nsKey if it exists. + * + * @param string $nsKey + * @param string $itemKey + */ + public function clearKey($nsKey, $itemKey) + { + unset($this->_contents[$nsKey][$itemKey]); + } + + /** + * Clear all data in the namespace $nsKey if it exists. + * + * @param string $nsKey + */ + public function clearAll($nsKey) + { + unset($this->_contents[$nsKey]); + } + + /** + * Initialize the namespace of $nsKey if needed. + * + * @param string $nsKey + */ + private function _prepareCache($nsKey) + { + if (!array_key_exists($nsKey, $this->_contents)) { + $this->_contents[$nsKey] = array(); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/DiskKeyCache.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/DiskKeyCache.php new file mode 100644 index 0000000000..453f50a15d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/DiskKeyCache.php @@ -0,0 +1,321 @@ +_stream = $stream; + $this->_path = $path; + + if (function_exists('get_magic_quotes_runtime') && @get_magic_quotes_runtime() == 1) { + $this->_quotes = true; + } + } + + /** + * Set a string into the cache under $itemKey for the namespace $nsKey. + * + * @see MODE_WRITE, MODE_APPEND + * + * @param string $nsKey + * @param string $itemKey + * @param string $string + * @param int $mode + * + * @throws Swift_IoException + */ + public function setString($nsKey, $itemKey, $string, $mode) + { + $this->_prepareCache($nsKey); + switch ($mode) { + case self::MODE_WRITE: + $fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_START); + break; + case self::MODE_APPEND: + $fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_END); + break; + default: + throw new Swift_SwiftException( + 'Invalid mode ['.$mode.'] used to set nsKey='. + $nsKey.', itemKey='.$itemKey + ); + break; + } + fwrite($fp, $string); + $this->_freeHandle($nsKey, $itemKey); + } + + /** + * Set a ByteStream into the cache under $itemKey for the namespace $nsKey. + * + * @see MODE_WRITE, MODE_APPEND + * + * @param string $nsKey + * @param string $itemKey + * @param Swift_OutputByteStream $os + * @param int $mode + * + * @throws Swift_IoException + */ + public function importFromByteStream($nsKey, $itemKey, Swift_OutputByteStream $os, $mode) + { + $this->_prepareCache($nsKey); + switch ($mode) { + case self::MODE_WRITE: + $fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_START); + break; + case self::MODE_APPEND: + $fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_END); + break; + default: + throw new Swift_SwiftException( + 'Invalid mode ['.$mode.'] used to set nsKey='. + $nsKey.', itemKey='.$itemKey + ); + break; + } + while (false !== $bytes = $os->read(8192)) { + fwrite($fp, $bytes); + } + $this->_freeHandle($nsKey, $itemKey); + } + + /** + * Provides a ByteStream which when written to, writes data to $itemKey. + * + * NOTE: The stream will always write in append mode. + * + * @param string $nsKey + * @param string $itemKey + * @param Swift_InputByteStream $writeThrough + * + * @return Swift_InputByteStream + */ + public function getInputByteStream($nsKey, $itemKey, Swift_InputByteStream $writeThrough = null) + { + $is = clone $this->_stream; + $is->setKeyCache($this); + $is->setNsKey($nsKey); + $is->setItemKey($itemKey); + if (isset($writeThrough)) { + $is->setWriteThroughStream($writeThrough); + } + + return $is; + } + + /** + * Get data back out of the cache as a string. + * + * @param string $nsKey + * @param string $itemKey + * + * @throws Swift_IoException + * + * @return string + */ + public function getString($nsKey, $itemKey) + { + $this->_prepareCache($nsKey); + if ($this->hasKey($nsKey, $itemKey)) { + $fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_START); + if ($this->_quotes) { + ini_set('magic_quotes_runtime', 0); + } + $str = ''; + while (!feof($fp) && false !== $bytes = fread($fp, 8192)) { + $str .= $bytes; + } + if ($this->_quotes) { + ini_set('magic_quotes_runtime', 1); + } + $this->_freeHandle($nsKey, $itemKey); + + return $str; + } + } + + /** + * Get data back out of the cache as a ByteStream. + * + * @param string $nsKey + * @param string $itemKey + * @param Swift_InputByteStream $is to write the data to + */ + public function exportToByteStream($nsKey, $itemKey, Swift_InputByteStream $is) + { + if ($this->hasKey($nsKey, $itemKey)) { + $fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_START); + if ($this->_quotes) { + ini_set('magic_quotes_runtime', 0); + } + while (!feof($fp) && false !== $bytes = fread($fp, 8192)) { + $is->write($bytes); + } + if ($this->_quotes) { + ini_set('magic_quotes_runtime', 1); + } + $this->_freeHandle($nsKey, $itemKey); + } + } + + /** + * Check if the given $itemKey exists in the namespace $nsKey. + * + * @param string $nsKey + * @param string $itemKey + * + * @return bool + */ + public function hasKey($nsKey, $itemKey) + { + return is_file($this->_path.'/'.$nsKey.'/'.$itemKey); + } + + /** + * Clear data for $itemKey in the namespace $nsKey if it exists. + * + * @param string $nsKey + * @param string $itemKey + */ + public function clearKey($nsKey, $itemKey) + { + if ($this->hasKey($nsKey, $itemKey)) { + $this->_freeHandle($nsKey, $itemKey); + unlink($this->_path.'/'.$nsKey.'/'.$itemKey); + } + } + + /** + * Clear all data in the namespace $nsKey if it exists. + * + * @param string $nsKey + */ + public function clearAll($nsKey) + { + if (array_key_exists($nsKey, $this->_keys)) { + foreach ($this->_keys[$nsKey] as $itemKey => $null) { + $this->clearKey($nsKey, $itemKey); + } + if (is_dir($this->_path.'/'.$nsKey)) { + rmdir($this->_path.'/'.$nsKey); + } + unset($this->_keys[$nsKey]); + } + } + + /** + * Initialize the namespace of $nsKey if needed. + * + * @param string $nsKey + */ + private function _prepareCache($nsKey) + { + $cacheDir = $this->_path.'/'.$nsKey; + if (!is_dir($cacheDir)) { + if (!mkdir($cacheDir)) { + throw new Swift_IoException('Failed to create cache directory '.$cacheDir); + } + $this->_keys[$nsKey] = array(); + } + } + + /** + * Get a file handle on the cache item. + * + * @param string $nsKey + * @param string $itemKey + * @param int $position + * + * @return resource + */ + private function _getHandle($nsKey, $itemKey, $position) + { + if (!isset($this->_keys[$nsKey][$itemKey])) { + $openMode = $this->hasKey($nsKey, $itemKey) ? 'r+b' : 'w+b'; + $fp = fopen($this->_path.'/'.$nsKey.'/'.$itemKey, $openMode); + $this->_keys[$nsKey][$itemKey] = $fp; + } + if (self::POSITION_START == $position) { + fseek($this->_keys[$nsKey][$itemKey], 0, SEEK_SET); + } elseif (self::POSITION_END == $position) { + fseek($this->_keys[$nsKey][$itemKey], 0, SEEK_END); + } + + return $this->_keys[$nsKey][$itemKey]; + } + + private function _freeHandle($nsKey, $itemKey) + { + $fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_CURRENT); + fclose($fp); + $this->_keys[$nsKey][$itemKey] = null; + } + + /** + * Destructor. + */ + public function __destruct() + { + foreach ($this->_keys as $nsKey => $null) { + $this->clearAll($nsKey); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/KeyCacheInputStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/KeyCacheInputStream.php new file mode 100644 index 0000000000..af80bdca6b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/KeyCacheInputStream.php @@ -0,0 +1,51 @@ +_keyCache = $keyCache; + } + + /** + * Specify a stream to write through for each write(). + * + * @param Swift_InputByteStream $is + */ + public function setWriteThroughStream(Swift_InputByteStream $is) + { + $this->_writeThrough = $is; + } + + /** + * Writes $bytes to the end of the stream. + * + * @param string $bytes + * @param Swift_InputByteStream $is optional + */ + public function write($bytes, Swift_InputByteStream $is = null) + { + $this->_keyCache->setString( + $this->_nsKey, $this->_itemKey, $bytes, Swift_KeyCache::MODE_APPEND + ); + if (isset($is)) { + $is->write($bytes); + } + if (isset($this->_writeThrough)) { + $this->_writeThrough->write($bytes); + } + } + + /** + * Not used. + */ + public function commit() + { + } + + /** + * Not used. + */ + public function bind(Swift_InputByteStream $is) + { + } + + /** + * Not used. + */ + public function unbind(Swift_InputByteStream $is) + { + } + + /** + * Flush the contents of the stream (empty it) and set the internal pointer + * to the beginning. + */ + public function flushBuffers() + { + $this->_keyCache->clearKey($this->_nsKey, $this->_itemKey); + } + + /** + * Set the nsKey which will be written to. + * + * @param string $nsKey + */ + public function setNsKey($nsKey) + { + $this->_nsKey = $nsKey; + } + + /** + * Set the itemKey which will be written to. + * + * @param string $itemKey + */ + public function setItemKey($itemKey) + { + $this->_itemKey = $itemKey; + } + + /** + * Any implementation should be cloneable, allowing the clone to access a + * separate $nsKey and $itemKey. + */ + public function __clone() + { + $this->_writeThrough = null; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/LoadBalancedTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/LoadBalancedTransport.php new file mode 100644 index 0000000000..fdba9df50d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/LoadBalancedTransport.php @@ -0,0 +1,45 @@ +createDependenciesFor('transport.loadbalanced') + ); + + $this->setTransports($transports); + } + + /** + * Create a new LoadBalancedTransport instance. + * + * @param array $transports + * + * @return Swift_LoadBalancedTransport + */ + public static function newInstance($transports = array()) + { + return new self($transports); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MailTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MailTransport.php new file mode 100644 index 0000000000..994df3ec2b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MailTransport.php @@ -0,0 +1,47 @@ +createDependenciesFor('transport.mail') + ); + + $this->setExtraParams($extraParams); + } + + /** + * Create a new MailTransport instance. + * + * @param string $extraParams To be passed to mail() + * + * @return Swift_MailTransport + */ + public static function newInstance($extraParams = '-f%s') + { + return new self($extraParams); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer.php new file mode 100644 index 0000000000..641154627b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer.php @@ -0,0 +1,114 @@ +_transport = $transport; + } + + /** + * Create a new Mailer instance. + * + * @param Swift_Transport $transport + * + * @return Swift_Mailer + */ + public static function newInstance(Swift_Transport $transport) + { + return new self($transport); + } + + /** + * Create a new class instance of one of the message services. + * + * For example 'mimepart' would create a 'message.mimepart' instance + * + * @param string $service + * + * @return object + */ + public function createMessage($service = 'message') + { + return Swift_DependencyContainer::getInstance() + ->lookup('message.'.$service); + } + + /** + * Send the given Message like it would be sent in a mail client. + * + * All recipients (with the exception of Bcc) will be able to see the other + * recipients this message was sent to. + * + * Recipient/sender data will be retrieved from the Message object. + * + * The return value is the number of recipients who were accepted for + * delivery. + * + * @param Swift_Mime_Message $message + * @param array $failedRecipients An array of failures by-reference + * + * @return int The number of successful recipients. Can be 0 which indicates failure + */ + public function send(Swift_Mime_Message $message, &$failedRecipients = null) + { + $failedRecipients = (array) $failedRecipients; + + if (!$this->_transport->isStarted()) { + $this->_transport->start(); + } + + $sent = 0; + + try { + $sent = $this->_transport->send($message, $failedRecipients); + } catch (Swift_RfcComplianceException $e) { + foreach ($message->getTo() as $address => $name) { + $failedRecipients[] = $address; + } + } + + return $sent; + } + + /** + * Register a plugin using a known unique key (e.g. myPlugin). + * + * @param Swift_Events_EventListener $plugin + */ + public function registerPlugin(Swift_Events_EventListener $plugin) + { + $this->_transport->registerPlugin($plugin); + } + + /** + * The Transport used to send messages. + * + * @return Swift_Transport + */ + public function getTransport() + { + return $this->_transport; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer/ArrayRecipientIterator.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer/ArrayRecipientIterator.php new file mode 100644 index 0000000000..e3e6cad05b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer/ArrayRecipientIterator.php @@ -0,0 +1,55 @@ +_recipients = $recipients; + } + + /** + * Returns true only if there are more recipients to send to. + * + * @return bool + */ + public function hasNext() + { + return !empty($this->_recipients); + } + + /** + * Returns an array where the keys are the addresses of recipients and the + * values are the names. e.g. ('foo@bar' => 'Foo') or ('foo@bar' => NULL). + * + * @return array + */ + public function nextRecipient() + { + return array_splice($this->_recipients, 0, 1); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer/RecipientIterator.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer/RecipientIterator.php new file mode 100644 index 0000000000..650f3ec3df --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer/RecipientIterator.php @@ -0,0 +1,32 @@ + 'Foo') or ('foo@bar' => NULL). + * + * @return array + */ + public function nextRecipient(); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MemorySpool.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MemorySpool.php new file mode 100644 index 0000000000..2cafb67517 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MemorySpool.php @@ -0,0 +1,110 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Stores Messages in memory. + * + * @author Fabien Potencier + */ +class Swift_MemorySpool implements Swift_Spool +{ + protected $messages = array(); + private $flushRetries = 3; + + /** + * Tests if this Transport mechanism has started. + * + * @return bool + */ + public function isStarted() + { + return true; + } + + /** + * Starts this Transport mechanism. + */ + public function start() + { + } + + /** + * Stops this Transport mechanism. + */ + public function stop() + { + } + + /** + * @param int $retries + */ + public function setFlushRetries($retries) + { + $this->flushRetries = $retries; + } + + /** + * Stores a message in the queue. + * + * @param Swift_Mime_Message $message The message to store + * + * @return bool Whether the operation has succeeded + */ + public function queueMessage(Swift_Mime_Message $message) + { + //clone the message to make sure it is not changed while in the queue + $this->messages[] = clone $message; + + return true; + } + + /** + * Sends messages using the given transport instance. + * + * @param Swift_Transport $transport A transport instance + * @param string[] $failedRecipients An array of failures by-reference + * + * @return int The number of sent emails + */ + public function flushQueue(Swift_Transport $transport, &$failedRecipients = null) + { + if (!$this->messages) { + return 0; + } + + if (!$transport->isStarted()) { + $transport->start(); + } + + $count = 0; + $retries = $this->flushRetries; + while ($retries--) { + try { + while ($message = array_pop($this->messages)) { + $count += $transport->send($message, $failedRecipients); + } + } catch (Swift_TransportException $exception) { + if ($retries) { + // re-queue the message at the end of the queue to give a chance + // to the other messages to be sent, in case the failure was due to + // this message and not just the transport failing + array_unshift($this->messages, $message); + + // wait half a second before we try again + usleep(500000); + } else { + throw $exception; + } + } + } + + return $count; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Message.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Message.php new file mode 100644 index 0000000000..a6bb659300 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Message.php @@ -0,0 +1,291 @@ +createDependenciesFor('mime.message') + ); + + if (!isset($charset)) { + $charset = Swift_DependencyContainer::getInstance() + ->lookup('properties.charset'); + } + $this->setSubject($subject); + $this->setBody($body); + $this->setCharset($charset); + if ($contentType) { + $this->setContentType($contentType); + } + } + + /** + * Create a new Message. + * + * @param string $subject + * @param string $body + * @param string $contentType + * @param string $charset + * + * @return $this + */ + public static function newInstance($subject = null, $body = null, $contentType = null, $charset = null) + { + return new self($subject, $body, $contentType, $charset); + } + + /** + * Add a MimePart to this Message. + * + * @param string|Swift_OutputByteStream $body + * @param string $contentType + * @param string $charset + * + * @return $this + */ + public function addPart($body, $contentType = null, $charset = null) + { + return $this->attach(Swift_MimePart::newInstance( + $body, $contentType, $charset + )); + } + + /** + * Detach a signature handler from a message. + * + * @param Swift_Signer $signer + * + * @return $this + */ + public function attachSigner(Swift_Signer $signer) + { + if ($signer instanceof Swift_Signers_HeaderSigner) { + $this->headerSigners[] = $signer; + } elseif ($signer instanceof Swift_Signers_BodySigner) { + $this->bodySigners[] = $signer; + } + + return $this; + } + + /** + * Attach a new signature handler to the message. + * + * @param Swift_Signer $signer + * + * @return $this + */ + public function detachSigner(Swift_Signer $signer) + { + if ($signer instanceof Swift_Signers_HeaderSigner) { + foreach ($this->headerSigners as $k => $headerSigner) { + if ($headerSigner === $signer) { + unset($this->headerSigners[$k]); + + return $this; + } + } + } elseif ($signer instanceof Swift_Signers_BodySigner) { + foreach ($this->bodySigners as $k => $bodySigner) { + if ($bodySigner === $signer) { + unset($this->bodySigners[$k]); + + return $this; + } + } + } + + return $this; + } + + /** + * Get this message as a complete string. + * + * @return string + */ + public function toString() + { + if (empty($this->headerSigners) && empty($this->bodySigners)) { + return parent::toString(); + } + + $this->saveMessage(); + + $this->doSign(); + + $string = parent::toString(); + + $this->restoreMessage(); + + return $string; + } + + /** + * Write this message to a {@link Swift_InputByteStream}. + * + * @param Swift_InputByteStream $is + */ + public function toByteStream(Swift_InputByteStream $is) + { + if (empty($this->headerSigners) && empty($this->bodySigners)) { + parent::toByteStream($is); + + return; + } + + $this->saveMessage(); + + $this->doSign(); + + parent::toByteStream($is); + + $this->restoreMessage(); + } + + public function __wakeup() + { + Swift_DependencyContainer::getInstance()->createDependenciesFor('mime.message'); + } + + /** + * loops through signers and apply the signatures. + */ + protected function doSign() + { + foreach ($this->bodySigners as $signer) { + $altered = $signer->getAlteredHeaders(); + $this->saveHeaders($altered); + $signer->signMessage($this); + } + + foreach ($this->headerSigners as $signer) { + $altered = $signer->getAlteredHeaders(); + $this->saveHeaders($altered); + $signer->reset(); + + $signer->setHeaders($this->getHeaders()); + + $signer->startBody(); + $this->_bodyToByteStream($signer); + $signer->endBody(); + + $signer->addSignature($this->getHeaders()); + } + } + + /** + * save the message before any signature is applied. + */ + protected function saveMessage() + { + $this->savedMessage = array('headers' => array()); + $this->savedMessage['body'] = $this->getBody(); + $this->savedMessage['children'] = $this->getChildren(); + if (count($this->savedMessage['children']) > 0 && $this->getBody() != '') { + $this->setChildren(array_merge(array($this->_becomeMimePart()), $this->savedMessage['children'])); + $this->setBody(''); + } + } + + /** + * save the original headers. + * + * @param array $altered + */ + protected function saveHeaders(array $altered) + { + foreach ($altered as $head) { + $lc = strtolower($head); + + if (!isset($this->savedMessage['headers'][$lc])) { + $this->savedMessage['headers'][$lc] = $this->getHeaders()->getAll($head); + } + } + } + + /** + * Remove or restore altered headers. + */ + protected function restoreHeaders() + { + foreach ($this->savedMessage['headers'] as $name => $savedValue) { + $headers = $this->getHeaders()->getAll($name); + + foreach ($headers as $key => $value) { + if (!isset($savedValue[$key])) { + $this->getHeaders()->remove($name, $key); + } + } + } + } + + /** + * Restore message body. + */ + protected function restoreMessage() + { + $this->setBody($this->savedMessage['body']); + $this->setChildren($this->savedMessage['children']); + + $this->restoreHeaders(); + $this->savedMessage = array(); + } + + /** + * Clone Message Signers. + * + * @see Swift_Mime_SimpleMimeEntity::__clone() + */ + public function __clone() + { + parent::__clone(); + foreach ($this->bodySigners as $key => $bodySigner) { + $this->bodySigners[$key] = clone $bodySigner; + } + + foreach ($this->headerSigners as $key => $headerSigner) { + $this->headerSigners[$key] = clone $headerSigner; + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Attachment.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Attachment.php new file mode 100644 index 0000000000..46a5e8da94 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Attachment.php @@ -0,0 +1,149 @@ +setDisposition('attachment'); + $this->setContentType('application/octet-stream'); + $this->_mimeTypes = $mimeTypes; + } + + /** + * Get the nesting level used for this attachment. + * + * Always returns {@link LEVEL_MIXED}. + * + * @return int + */ + public function getNestingLevel() + { + return self::LEVEL_MIXED; + } + + /** + * Get the Content-Disposition of this attachment. + * + * By default attachments have a disposition of "attachment". + * + * @return string + */ + public function getDisposition() + { + return $this->_getHeaderFieldModel('Content-Disposition'); + } + + /** + * Set the Content-Disposition of this attachment. + * + * @param string $disposition + * + * @return Swift_Mime_Attachment + */ + public function setDisposition($disposition) + { + if (!$this->_setHeaderFieldModel('Content-Disposition', $disposition)) { + $this->getHeaders()->addParameterizedHeader('Content-Disposition', $disposition); + } + + return $this; + } + + /** + * Get the filename of this attachment when downloaded. + * + * @return string + */ + public function getFilename() + { + return $this->_getHeaderParameter('Content-Disposition', 'filename'); + } + + /** + * Set the filename of this attachment. + * + * @param string $filename + * + * @return Swift_Mime_Attachment + */ + public function setFilename($filename) + { + $this->_setHeaderParameter('Content-Disposition', 'filename', $filename); + $this->_setHeaderParameter('Content-Type', 'name', $filename); + + return $this; + } + + /** + * Get the file size of this attachment. + * + * @return int + */ + public function getSize() + { + return $this->_getHeaderParameter('Content-Disposition', 'size'); + } + + /** + * Set the file size of this attachment. + * + * @param int $size + * + * @return Swift_Mime_Attachment + */ + public function setSize($size) + { + $this->_setHeaderParameter('Content-Disposition', 'size', $size); + + return $this; + } + + /** + * Set the file that this attachment is for. + * + * @param Swift_FileStream $file + * @param string $contentType optional + * + * @return Swift_Mime_Attachment + */ + public function setFile(Swift_FileStream $file, $contentType = null) + { + $this->setFilename(basename($file->getPath())); + $this->setBody($file, $contentType); + if (!isset($contentType)) { + $extension = strtolower(substr($file->getPath(), strrpos($file->getPath(), '.') + 1)); + + if (array_key_exists($extension, $this->_mimeTypes)) { + $this->setContentType($this->_mimeTypes[$extension]); + } + } + + return $this; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/CharsetObserver.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/CharsetObserver.php new file mode 100644 index 0000000000..b49c3a873b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/CharsetObserver.php @@ -0,0 +1,24 @@ += $maxLineLength || 76 < $maxLineLength) { + $maxLineLength = 76; + } + + $remainder = 0; + $base64ReadBufferRemainderBytes = null; + + // To reduce memory usage, the output buffer is streamed to the input buffer like so: + // Output Stream => base64encode => wrap line length => Input Stream + // HOWEVER it's important to note that base64_encode() should only be passed whole triplets of data (except for the final chunk of data) + // otherwise it will assume the input data has *ended* and it will incorrectly pad/terminate the base64 data mid-stream. + // We use $base64ReadBufferRemainderBytes to carry over 1-2 "remainder" bytes from the each chunk from OutputStream and pre-pend those onto the + // chunk of bytes read in the next iteration. + // When the OutputStream is empty, we must flush any remainder bytes. + while (true) { + $readBytes = $os->read(8192); + $atEOF = ($readBytes === false); + + if ($atEOF) { + $streamTheseBytes = $base64ReadBufferRemainderBytes; + } else { + $streamTheseBytes = $base64ReadBufferRemainderBytes.$readBytes; + } + $base64ReadBufferRemainderBytes = null; + $bytesLength = strlen($streamTheseBytes); + + if ($bytesLength === 0) { // no data left to encode + break; + } + + // if we're not on the last block of the ouput stream, make sure $streamTheseBytes ends with a complete triplet of data + // and carry over remainder 1-2 bytes to the next loop iteration + if (!$atEOF) { + $excessBytes = $bytesLength % 3; + if ($excessBytes !== 0) { + $base64ReadBufferRemainderBytes = substr($streamTheseBytes, -$excessBytes); + $streamTheseBytes = substr($streamTheseBytes, 0, $bytesLength - $excessBytes); + } + } + + $encoded = base64_encode($streamTheseBytes); + $encodedTransformed = ''; + $thisMaxLineLength = $maxLineLength - $remainder - $firstLineOffset; + + while ($thisMaxLineLength < strlen($encoded)) { + $encodedTransformed .= substr($encoded, 0, $thisMaxLineLength)."\r\n"; + $firstLineOffset = 0; + $encoded = substr($encoded, $thisMaxLineLength); + $thisMaxLineLength = $maxLineLength; + $remainder = 0; + } + + if (0 < $remainingLength = strlen($encoded)) { + $remainder += $remainingLength; + $encodedTransformed .= $encoded; + $encoded = null; + } + + $is->write($encodedTransformed); + + if ($atEOF) { + break; + } + } + } + + /** + * Get the name of this encoding scheme. + * Returns the string 'base64'. + * + * @return string + */ + public function getName() + { + return 'base64'; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/NativeQpContentEncoder.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/NativeQpContentEncoder.php new file mode 100644 index 0000000000..710b5ac9ed --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/NativeQpContentEncoder.php @@ -0,0 +1,123 @@ +charset = $charset ? $charset : 'utf-8'; + } + + /** + * Notify this observer that the entity's charset has changed. + * + * @param string $charset + */ + public function charsetChanged($charset) + { + $this->charset = $charset; + } + + /** + * Encode $in to $out. + * + * @param Swift_OutputByteStream $os to read from + * @param Swift_InputByteStream $is to write to + * @param int $firstLineOffset + * @param int $maxLineLength 0 indicates the default length for this encoding + * + * @throws RuntimeException + */ + public function encodeByteStream(Swift_OutputByteStream $os, Swift_InputByteStream $is, $firstLineOffset = 0, $maxLineLength = 0) + { + if ($this->charset !== 'utf-8') { + throw new RuntimeException( + sprintf('Charset "%s" not supported. NativeQpContentEncoder only supports "utf-8"', $this->charset)); + } + + $string = ''; + + while (false !== $bytes = $os->read(8192)) { + $string .= $bytes; + } + + $is->write($this->encodeString($string)); + } + + /** + * Get the MIME name of this content encoding scheme. + * + * @return string + */ + public function getName() + { + return 'quoted-printable'; + } + + /** + * Encode a given string to produce an encoded string. + * + * @param string $string + * @param int $firstLineOffset if first line needs to be shorter + * @param int $maxLineLength 0 indicates the default length for this encoding + * + * @throws RuntimeException + * + * @return string + */ + public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0) + { + if ($this->charset !== 'utf-8') { + throw new RuntimeException( + sprintf('Charset "%s" not supported. NativeQpContentEncoder only supports "utf-8"', $this->charset)); + } + + return $this->_standardize(quoted_printable_encode($string)); + } + + /** + * Make sure CRLF is correct and HT/SPACE are in valid places. + * + * @param string $string + * + * @return string + */ + protected function _standardize($string) + { + // transform CR or LF to CRLF + $string = preg_replace('~=0D(?!=0A)|(?_name = $name; + $this->_canonical = $canonical; + } + + /** + * Encode a given string to produce an encoded string. + * + * @param string $string + * @param int $firstLineOffset ignored + * @param int $maxLineLength - 0 means no wrapping will occur + * + * @return string + */ + public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0) + { + if ($this->_canonical) { + $string = $this->_canonicalize($string); + } + + return $this->_safeWordWrap($string, $maxLineLength, "\r\n"); + } + + /** + * Encode stream $in to stream $out. + * + * @param Swift_OutputByteStream $os + * @param Swift_InputByteStream $is + * @param int $firstLineOffset ignored + * @param int $maxLineLength optional, 0 means no wrapping will occur + */ + public function encodeByteStream(Swift_OutputByteStream $os, Swift_InputByteStream $is, $firstLineOffset = 0, $maxLineLength = 0) + { + $leftOver = ''; + while (false !== $bytes = $os->read(8192)) { + $toencode = $leftOver.$bytes; + if ($this->_canonical) { + $toencode = $this->_canonicalize($toencode); + } + $wrapped = $this->_safeWordWrap($toencode, $maxLineLength, "\r\n"); + $lastLinePos = strrpos($wrapped, "\r\n"); + $leftOver = substr($wrapped, $lastLinePos); + $wrapped = substr($wrapped, 0, $lastLinePos); + + $is->write($wrapped); + } + if (strlen($leftOver)) { + $is->write($leftOver); + } + } + + /** + * Get the name of this encoding scheme. + * + * @return string + */ + public function getName() + { + return $this->_name; + } + + /** + * Not used. + */ + public function charsetChanged($charset) + { + } + + /** + * A safer (but weaker) wordwrap for unicode. + * + * @param string $string + * @param int $length + * @param string $le + * + * @return string + */ + private function _safeWordwrap($string, $length = 75, $le = "\r\n") + { + if (0 >= $length) { + return $string; + } + + $originalLines = explode($le, $string); + + $lines = array(); + $lineCount = 0; + + foreach ($originalLines as $originalLine) { + $lines[] = ''; + $currentLine = &$lines[$lineCount++]; + + //$chunks = preg_split('/(?<=[\ \t,\.!\?\-&\+\/])/', $originalLine); + $chunks = preg_split('/(?<=\s)/', $originalLine); + + foreach ($chunks as $chunk) { + if (0 != strlen($currentLine) + && strlen($currentLine.$chunk) > $length) { + $lines[] = ''; + $currentLine = &$lines[$lineCount++]; + } + $currentLine .= $chunk; + } + } + + return implode("\r\n", $lines); + } + + /** + * Canonicalize string input (fix CRLF). + * + * @param string $string + * + * @return string + */ + private function _canonicalize($string) + { + return str_replace( + array("\r\n", "\r", "\n"), + array("\n", "\n", "\r\n"), + $string + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoder.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoder.php new file mode 100644 index 0000000000..5cc907b8e7 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoder.php @@ -0,0 +1,134 @@ +_dotEscape = $dotEscape; + parent::__construct($charStream, $filter); + } + + public function __sleep() + { + return array('_charStream', '_filter', '_dotEscape'); + } + + protected function getSafeMapShareId() + { + return get_class($this).($this->_dotEscape ? '.dotEscape' : ''); + } + + protected function initSafeMap() + { + parent::initSafeMap(); + if ($this->_dotEscape) { + /* Encode . as =2e for buggy remote servers */ + unset($this->_safeMap[0x2e]); + } + } + + /** + * Encode stream $in to stream $out. + * + * QP encoded strings have a maximum line length of 76 characters. + * If the first line needs to be shorter, indicate the difference with + * $firstLineOffset. + * + * @param Swift_OutputByteStream $os output stream + * @param Swift_InputByteStream $is input stream + * @param int $firstLineOffset + * @param int $maxLineLength + */ + public function encodeByteStream(Swift_OutputByteStream $os, Swift_InputByteStream $is, $firstLineOffset = 0, $maxLineLength = 0) + { + if ($maxLineLength > 76 || $maxLineLength <= 0) { + $maxLineLength = 76; + } + + $thisLineLength = $maxLineLength - $firstLineOffset; + + $this->_charStream->flushContents(); + $this->_charStream->importByteStream($os); + + $currentLine = ''; + $prepend = ''; + $size = $lineLen = 0; + + while (false !== $bytes = $this->_nextSequence()) { + // If we're filtering the input + if (isset($this->_filter)) { + // If we can't filter because we need more bytes + while ($this->_filter->shouldBuffer($bytes)) { + // Then collect bytes into the buffer + if (false === $moreBytes = $this->_nextSequence(1)) { + break; + } + + foreach ($moreBytes as $b) { + $bytes[] = $b; + } + } + // And filter them + $bytes = $this->_filter->filter($bytes); + } + + $enc = $this->_encodeByteSequence($bytes, $size); + + $i = strpos($enc, '=0D=0A'); + $newLineLength = $lineLen + ($i === false ? $size : $i); + + if ($currentLine && $newLineLength >= $thisLineLength) { + $is->write($prepend.$this->_standardize($currentLine)); + $currentLine = ''; + $prepend = "=\r\n"; + $thisLineLength = $maxLineLength; + $lineLen = 0; + } + + $currentLine .= $enc; + + if ($i === false) { + $lineLen += $size; + } else { + // 6 is the length of '=0D=0A'. + $lineLen = $size - strrpos($enc, '=0D=0A') - 6; + } + } + if (strlen($currentLine)) { + $is->write($prepend.$this->_standardize($currentLine)); + } + } + + /** + * Get the name of this encoding scheme. + * Returns the string 'quoted-printable'. + * + * @return string + */ + public function getName() + { + return 'quoted-printable'; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoderProxy.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoderProxy.php new file mode 100644 index 0000000000..3214e1cf3f --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoderProxy.php @@ -0,0 +1,98 @@ + + */ +class Swift_Mime_ContentEncoder_QpContentEncoderProxy implements Swift_Mime_ContentEncoder +{ + /** + * @var Swift_Mime_ContentEncoder_QpContentEncoder + */ + private $safeEncoder; + + /** + * @var Swift_Mime_ContentEncoder_NativeQpContentEncoder + */ + private $nativeEncoder; + + /** + * @var null|string + */ + private $charset; + + /** + * Constructor. + * + * @param Swift_Mime_ContentEncoder_QpContentEncoder $safeEncoder + * @param Swift_Mime_ContentEncoder_NativeQpContentEncoder $nativeEncoder + * @param string|null $charset + */ + public function __construct(Swift_Mime_ContentEncoder_QpContentEncoder $safeEncoder, Swift_Mime_ContentEncoder_NativeQpContentEncoder $nativeEncoder, $charset) + { + $this->safeEncoder = $safeEncoder; + $this->nativeEncoder = $nativeEncoder; + $this->charset = $charset; + } + + /** + * Make a deep copy of object. + */ + public function __clone() + { + $this->safeEncoder = clone $this->safeEncoder; + $this->nativeEncoder = clone $this->nativeEncoder; + } + + /** + * {@inheritdoc} + */ + public function charsetChanged($charset) + { + $this->charset = $charset; + $this->safeEncoder->charsetChanged($charset); + } + + /** + * {@inheritdoc} + */ + public function encodeByteStream(Swift_OutputByteStream $os, Swift_InputByteStream $is, $firstLineOffset = 0, $maxLineLength = 0) + { + $this->getEncoder()->encodeByteStream($os, $is, $firstLineOffset, $maxLineLength); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'quoted-printable'; + } + + /** + * {@inheritdoc} + */ + public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0) + { + return $this->getEncoder()->encodeString($string, $firstLineOffset, $maxLineLength); + } + + /** + * @return Swift_Mime_ContentEncoder + */ + private function getEncoder() + { + return 'utf-8' === $this->charset ? $this->nativeEncoder : $this->safeEncoder; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/RawContentEncoder.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/RawContentEncoder.php new file mode 100644 index 0000000000..0b8526e38c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/RawContentEncoder.php @@ -0,0 +1,64 @@ + + */ +class Swift_Mime_ContentEncoder_RawContentEncoder implements Swift_Mime_ContentEncoder +{ + /** + * Encode a given string to produce an encoded string. + * + * @param string $string + * @param int $firstLineOffset ignored + * @param int $maxLineLength ignored + * + * @return string + */ + public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0) + { + return $string; + } + + /** + * Encode stream $in to stream $out. + * + * @param Swift_OutputByteStream $in + * @param Swift_InputByteStream $out + * @param int $firstLineOffset ignored + * @param int $maxLineLength ignored + */ + public function encodeByteStream(Swift_OutputByteStream $os, Swift_InputByteStream $is, $firstLineOffset = 0, $maxLineLength = 0) + { + while (false !== ($bytes = $os->read(8192))) { + $is->write($bytes); + } + } + + /** + * Get the name of this encoding scheme. + * + * @return string + */ + public function getName() + { + return 'raw'; + } + + /** + * Not used. + */ + public function charsetChanged($charset) + { + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/EmbeddedFile.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/EmbeddedFile.php new file mode 100644 index 0000000000..6af757124d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/EmbeddedFile.php @@ -0,0 +1,45 @@ +setDisposition('inline'); + $this->setId($this->getId()); + } + + /** + * Get the nesting level of this EmbeddedFile. + * + * Returns {@see LEVEL_RELATED}. + * + * @return int + */ + public function getNestingLevel() + { + return self::LEVEL_RELATED; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/EncodingObserver.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/EncodingObserver.php new file mode 100644 index 0000000000..cc44a6efb9 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/EncodingObserver.php @@ -0,0 +1,24 @@ +init(); + } + + public function __wakeup() + { + $this->init(); + } + + protected function init() + { + if (count(self::$_specials) > 0) { + return; + } + + self::$_specials = array( + '(', ')', '<', '>', '[', ']', + ':', ';', '@', ',', '.', '"', + ); + + /*** Refer to RFC 2822 for ABNF grammar ***/ + + // All basic building blocks + self::$_grammar['NO-WS-CTL'] = '[\x01-\x08\x0B\x0C\x0E-\x19\x7F]'; + self::$_grammar['WSP'] = '[ \t]'; + self::$_grammar['CRLF'] = '(?:\r\n)'; + self::$_grammar['FWS'] = '(?:(?:'.self::$_grammar['WSP'].'*'. + self::$_grammar['CRLF'].')?'.self::$_grammar['WSP'].')'; + self::$_grammar['text'] = '[\x00-\x08\x0B\x0C\x0E-\x7F]'; + self::$_grammar['quoted-pair'] = '(?:\\\\'.self::$_grammar['text'].')'; + self::$_grammar['ctext'] = '(?:'.self::$_grammar['NO-WS-CTL']. + '|[\x21-\x27\x2A-\x5B\x5D-\x7E])'; + // Uses recursive PCRE (?1) -- could be a weak point?? + self::$_grammar['ccontent'] = '(?:'.self::$_grammar['ctext'].'|'. + self::$_grammar['quoted-pair'].'|(?1))'; + self::$_grammar['comment'] = '(\((?:'.self::$_grammar['FWS'].'|'. + self::$_grammar['ccontent'].')*'.self::$_grammar['FWS'].'?\))'; + self::$_grammar['CFWS'] = '(?:(?:'.self::$_grammar['FWS'].'?'. + self::$_grammar['comment'].')*(?:(?:'.self::$_grammar['FWS'].'?'. + self::$_grammar['comment'].')|'.self::$_grammar['FWS'].'))'; + self::$_grammar['qtext'] = '(?:'.self::$_grammar['NO-WS-CTL']. + '|[\x21\x23-\x5B\x5D-\x7E])'; + self::$_grammar['qcontent'] = '(?:'.self::$_grammar['qtext'].'|'. + self::$_grammar['quoted-pair'].')'; + self::$_grammar['quoted-string'] = '(?:'.self::$_grammar['CFWS'].'?"'. + '('.self::$_grammar['FWS'].'?'.self::$_grammar['qcontent'].')*'. + self::$_grammar['FWS'].'?"'.self::$_grammar['CFWS'].'?)'; + self::$_grammar['atext'] = '[a-zA-Z0-9!#\$%&\'\*\+\-\/=\?\^_`\{\}\|~]'; + self::$_grammar['atom'] = '(?:'.self::$_grammar['CFWS'].'?'. + self::$_grammar['atext'].'+'.self::$_grammar['CFWS'].'?)'; + self::$_grammar['dot-atom-text'] = '(?:'.self::$_grammar['atext'].'+'. + '(\.'.self::$_grammar['atext'].'+)*)'; + self::$_grammar['dot-atom'] = '(?:'.self::$_grammar['CFWS'].'?'. + self::$_grammar['dot-atom-text'].'+'.self::$_grammar['CFWS'].'?)'; + self::$_grammar['word'] = '(?:'.self::$_grammar['atom'].'|'. + self::$_grammar['quoted-string'].')'; + self::$_grammar['phrase'] = '(?:'.self::$_grammar['word'].'+?)'; + self::$_grammar['no-fold-quote'] = '(?:"(?:'.self::$_grammar['qtext']. + '|'.self::$_grammar['quoted-pair'].')*")'; + self::$_grammar['dtext'] = '(?:'.self::$_grammar['NO-WS-CTL']. + '|[\x21-\x5A\x5E-\x7E])'; + self::$_grammar['no-fold-literal'] = '(?:\[(?:'.self::$_grammar['dtext']. + '|'.self::$_grammar['quoted-pair'].')*\])'; + + // Message IDs + self::$_grammar['id-left'] = '(?:'.self::$_grammar['dot-atom-text'].'|'. + self::$_grammar['no-fold-quote'].')'; + self::$_grammar['id-right'] = '(?:'.self::$_grammar['dot-atom-text'].'|'. + self::$_grammar['no-fold-literal'].')'; + + // Addresses, mailboxes and paths + self::$_grammar['local-part'] = '(?:'.self::$_grammar['dot-atom'].'|'. + self::$_grammar['quoted-string'].')'; + self::$_grammar['dcontent'] = '(?:'.self::$_grammar['dtext'].'|'. + self::$_grammar['quoted-pair'].')'; + self::$_grammar['domain-literal'] = '(?:'.self::$_grammar['CFWS'].'?\[('. + self::$_grammar['FWS'].'?'.self::$_grammar['dcontent'].')*?'. + self::$_grammar['FWS'].'?\]'.self::$_grammar['CFWS'].'?)'; + self::$_grammar['domain'] = '(?:'.self::$_grammar['dot-atom'].'|'. + self::$_grammar['domain-literal'].')'; + self::$_grammar['addr-spec'] = '(?:'.self::$_grammar['local-part'].'@'. + self::$_grammar['domain'].')'; + } + + /** + * Get the grammar defined for $name token. + * + * @param string $name exactly as written in the RFC + * + * @return string + */ + public function getDefinition($name) + { + if (array_key_exists($name, self::$_grammar)) { + return self::$_grammar[$name]; + } + + throw new Swift_RfcComplianceException( + "No such grammar '".$name."' defined." + ); + } + + /** + * Returns the tokens defined in RFC 2822 (and some related RFCs). + * + * @return array + */ + public function getGrammarDefinitions() + { + return self::$_grammar; + } + + /** + * Returns the current special characters used in the syntax which need to be escaped. + * + * @return array + */ + public function getSpecials() + { + return self::$_specials; + } + + /** + * Escape special characters in a string (convert to quoted-pairs). + * + * @param string $token + * @param string[] $include additional chars to escape + * @param string[] $exclude chars from escaping + * + * @return string + */ + public function escapeSpecials($token, $include = array(), $exclude = array()) + { + foreach (array_merge(array('\\'), array_diff(self::$_specials, $exclude), $include) as $char) { + $token = str_replace($char, '\\'.$char, $token); + } + + return $token; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Header.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Header.php new file mode 100644 index 0000000000..a8ddd27216 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Header.php @@ -0,0 +1,93 @@ +getName(), "\r\n"); + mb_internal_encoding($old); + + return $newstring; + } + + return parent::encodeString($string, $firstLineOffset, $maxLineLength); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderEncoder/QpHeaderEncoder.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderEncoder/QpHeaderEncoder.php new file mode 100644 index 0000000000..510dd6637b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderEncoder/QpHeaderEncoder.php @@ -0,0 +1,65 @@ +_safeMap[$byte] = chr($byte); + } + } + + /** + * Get the name of this encoding scheme. + * + * Returns the string 'Q'. + * + * @return string + */ + public function getName() + { + return 'Q'; + } + + /** + * Takes an unencoded string and produces a QP encoded string from it. + * + * @param string $string string to encode + * @param int $firstLineOffset optional + * @param int $maxLineLength optional, 0 indicates the default of 76 chars + * + * @return string + */ + public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0) + { + return str_replace(array(' ', '=20', "=\r\n"), array('_', '_', "\r\n"), + parent::encodeString($string, $firstLineOffset, $maxLineLength) + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderFactory.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderFactory.php new file mode 100644 index 0000000000..c65f26d721 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderFactory.php @@ -0,0 +1,78 @@ +setGrammar($grammar); + } + + /** + * Set the character set used in this Header. + * + * @param string $charset + */ + public function setCharset($charset) + { + $this->clearCachedValueIf($charset != $this->_charset); + $this->_charset = $charset; + if (isset($this->_encoder)) { + $this->_encoder->charsetChanged($charset); + } + } + + /** + * Get the character set used in this Header. + * + * @return string + */ + public function getCharset() + { + return $this->_charset; + } + + /** + * Set the language used in this Header. + * + * For example, for US English, 'en-us'. + * This can be unspecified. + * + * @param string $lang + */ + public function setLanguage($lang) + { + $this->clearCachedValueIf($this->_lang != $lang); + $this->_lang = $lang; + } + + /** + * Get the language used in this Header. + * + * @return string + */ + public function getLanguage() + { + return $this->_lang; + } + + /** + * Set the encoder used for encoding the header. + * + * @param Swift_Mime_HeaderEncoder $encoder + */ + public function setEncoder(Swift_Mime_HeaderEncoder $encoder) + { + $this->_encoder = $encoder; + $this->setCachedValue(null); + } + + /** + * Get the encoder used for encoding this Header. + * + * @return Swift_Mime_HeaderEncoder + */ + public function getEncoder() + { + return $this->_encoder; + } + + /** + * Set the grammar used for the header. + * + * @param Swift_Mime_Grammar $grammar + */ + public function setGrammar(Swift_Mime_Grammar $grammar) + { + $this->_grammar = $grammar; + $this->setCachedValue(null); + } + + /** + * Get the grammar used for this Header. + * + * @return Swift_Mime_Grammar + */ + public function getGrammar() + { + return $this->_grammar; + } + + /** + * Get the name of this header (e.g. charset). + * + * @return string + */ + public function getFieldName() + { + return $this->_name; + } + + /** + * Set the maximum length of lines in the header (excluding EOL). + * + * @param int $lineLength + */ + public function setMaxLineLength($lineLength) + { + $this->clearCachedValueIf($this->_lineLength != $lineLength); + $this->_lineLength = $lineLength; + } + + /** + * Get the maximum permitted length of lines in this Header. + * + * @return int + */ + public function getMaxLineLength() + { + return $this->_lineLength; + } + + /** + * Get this Header rendered as a RFC 2822 compliant string. + * + * @throws Swift_RfcComplianceException + * + * @return string + */ + public function toString() + { + return $this->_tokensToString($this->toTokens()); + } + + /** + * Returns a string representation of this object. + * + * @return string + * + * @see toString() + */ + public function __toString() + { + return $this->toString(); + } + + // -- Points of extension + + /** + * Set the name of this Header field. + * + * @param string $name + */ + protected function setFieldName($name) + { + $this->_name = $name; + } + + /** + * Produces a compliant, formatted RFC 2822 'phrase' based on the string given. + * + * @param Swift_Mime_Header $header + * @param string $string as displayed + * @param string $charset of the text + * @param Swift_Mime_HeaderEncoder $encoder + * @param bool $shorten the first line to make remove for header name + * + * @return string + */ + protected function createPhrase(Swift_Mime_Header $header, $string, $charset, Swift_Mime_HeaderEncoder $encoder = null, $shorten = false) + { + // Treat token as exactly what was given + $phraseStr = $string; + // If it's not valid + if (!preg_match('/^'.$this->getGrammar()->getDefinition('phrase').'$/D', $phraseStr)) { + // .. but it is just ascii text, try escaping some characters + // and make it a quoted-string + if (preg_match('/^'.$this->getGrammar()->getDefinition('text').'*$/D', $phraseStr)) { + $phraseStr = $this->getGrammar()->escapeSpecials( + $phraseStr, array('"'), $this->getGrammar()->getSpecials() + ); + $phraseStr = '"'.$phraseStr.'"'; + } else { + // ... otherwise it needs encoding + // Determine space remaining on line if first line + if ($shorten) { + $usedLength = strlen($header->getFieldName().': '); + } else { + $usedLength = 0; + } + $phraseStr = $this->encodeWords($header, $string, $usedLength); + } + } + + return $phraseStr; + } + + /** + * Encode needed word tokens within a string of input. + * + * @param Swift_Mime_Header $header + * @param string $input + * @param string $usedLength optional + * + * @return string + */ + protected function encodeWords(Swift_Mime_Header $header, $input, $usedLength = -1) + { + $value = ''; + + $tokens = $this->getEncodableWordTokens($input); + + foreach ($tokens as $token) { + // See RFC 2822, Sect 2.2 (really 2.2 ??) + if ($this->tokenNeedsEncoding($token)) { + // Don't encode starting WSP + $firstChar = substr($token, 0, 1); + switch ($firstChar) { + case ' ': + case "\t": + $value .= $firstChar; + $token = substr($token, 1); + } + + if (-1 == $usedLength) { + $usedLength = strlen($header->getFieldName().': ') + strlen($value); + } + $value .= $this->getTokenAsEncodedWord($token, $usedLength); + + $header->setMaxLineLength(76); // Forcefully override + } else { + $value .= $token; + } + } + + return $value; + } + + /** + * Test if a token needs to be encoded or not. + * + * @param string $token + * + * @return bool + */ + protected function tokenNeedsEncoding($token) + { + return preg_match('~[\x00-\x08\x10-\x19\x7F-\xFF\r\n]~', $token); + } + + /** + * Splits a string into tokens in blocks of words which can be encoded quickly. + * + * @param string $string + * + * @return string[] + */ + protected function getEncodableWordTokens($string) + { + $tokens = array(); + + $encodedToken = ''; + // Split at all whitespace boundaries + foreach (preg_split('~(?=[\t ])~', $string) as $token) { + if ($this->tokenNeedsEncoding($token)) { + $encodedToken .= $token; + } else { + if (strlen($encodedToken) > 0) { + $tokens[] = $encodedToken; + $encodedToken = ''; + } + $tokens[] = $token; + } + } + if (strlen($encodedToken)) { + $tokens[] = $encodedToken; + } + + return $tokens; + } + + /** + * Get a token as an encoded word for safe insertion into headers. + * + * @param string $token token to encode + * @param int $firstLineOffset optional + * + * @return string + */ + protected function getTokenAsEncodedWord($token, $firstLineOffset = 0) + { + // Adjust $firstLineOffset to account for space needed for syntax + $charsetDecl = $this->_charset; + if (isset($this->_lang)) { + $charsetDecl .= '*'.$this->_lang; + } + $encodingWrapperLength = strlen( + '=?'.$charsetDecl.'?'.$this->_encoder->getName().'??=' + ); + + if ($firstLineOffset >= 75) { + //Does this logic need to be here? + $firstLineOffset = 0; + } + + $encodedTextLines = explode("\r\n", + $this->_encoder->encodeString( + $token, $firstLineOffset, 75 - $encodingWrapperLength, $this->_charset + ) + ); + + if (strtolower($this->_charset) !== 'iso-2022-jp') { + // special encoding for iso-2022-jp using mb_encode_mimeheader + foreach ($encodedTextLines as $lineNum => $line) { + $encodedTextLines[$lineNum] = '=?'.$charsetDecl. + '?'.$this->_encoder->getName(). + '?'.$line.'?='; + } + } + + return implode("\r\n ", $encodedTextLines); + } + + /** + * Generates tokens from the given string which include CRLF as individual tokens. + * + * @param string $token + * + * @return string[] + */ + protected function generateTokenLines($token) + { + return preg_split('~(\r\n)~', $token, -1, PREG_SPLIT_DELIM_CAPTURE); + } + + /** + * Set a value into the cache. + * + * @param string $value + */ + protected function setCachedValue($value) + { + $this->_cachedValue = $value; + } + + /** + * Get the value in the cache. + * + * @return string + */ + protected function getCachedValue() + { + return $this->_cachedValue; + } + + /** + * Clear the cached value if $condition is met. + * + * @param bool $condition + */ + protected function clearCachedValueIf($condition) + { + if ($condition) { + $this->setCachedValue(null); + } + } + + /** + * Generate a list of all tokens in the final header. + * + * @param string $string The string to tokenize + * + * @return array An array of tokens as strings + */ + protected function toTokens($string = null) + { + if (is_null($string)) { + $string = $this->getFieldBody(); + } + + $tokens = array(); + + // Generate atoms; split at all invisible boundaries followed by WSP + foreach (preg_split('~(?=[ \t])~', $string) as $token) { + $newTokens = $this->generateTokenLines($token); + foreach ($newTokens as $newToken) { + $tokens[] = $newToken; + } + } + + return $tokens; + } + + /** + * Takes an array of tokens which appear in the header and turns them into + * an RFC 2822 compliant string, adding FWSP where needed. + * + * @param string[] $tokens + * + * @return string + */ + private function _tokensToString(array $tokens) + { + $lineCount = 0; + $headerLines = array(); + $headerLines[] = $this->_name.': '; + $currentLine = &$headerLines[$lineCount++]; + + // Build all tokens back into compliant header + foreach ($tokens as $i => $token) { + // Line longer than specified maximum or token was just a new line + if (("\r\n" == $token) || + ($i > 0 && strlen($currentLine.$token) > $this->_lineLength) + && 0 < strlen($currentLine)) { + $headerLines[] = ''; + $currentLine = &$headerLines[$lineCount++]; + } + + // Append token to the line + if ("\r\n" != $token) { + $currentLine .= $token; + } + } + + // Implode with FWS (RFC 2822, 2.2.3) + return implode("\r\n", $headerLines)."\r\n"; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/DateHeader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/DateHeader.php new file mode 100644 index 0000000000..4fd6674296 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/DateHeader.php @@ -0,0 +1,125 @@ + + * + * + * + * @param string $name of Header + * @param Swift_Mime_Grammar $grammar + */ + public function __construct($name, Swift_Mime_Grammar $grammar) + { + $this->setFieldName($name); + parent::__construct($grammar); + } + + /** + * Get the type of Header that this instance represents. + * + * @see TYPE_TEXT, TYPE_PARAMETERIZED, TYPE_MAILBOX + * @see TYPE_DATE, TYPE_ID, TYPE_PATH + * + * @return int + */ + public function getFieldType() + { + return self::TYPE_DATE; + } + + /** + * Set the model for the field body. + * + * This method takes a UNIX timestamp. + * + * @param int $model + */ + public function setFieldBodyModel($model) + { + $this->setTimestamp($model); + } + + /** + * Get the model for the field body. + * + * This method returns a UNIX timestamp. + * + * @return mixed + */ + public function getFieldBodyModel() + { + return $this->getTimestamp(); + } + + /** + * Get the UNIX timestamp of the Date in this Header. + * + * @return int + */ + public function getTimestamp() + { + return $this->_timestamp; + } + + /** + * Set the UNIX timestamp of the Date in this Header. + * + * @param int $timestamp + */ + public function setTimestamp($timestamp) + { + if (!is_null($timestamp)) { + $timestamp = (int) $timestamp; + } + $this->clearCachedValueIf($this->_timestamp != $timestamp); + $this->_timestamp = $timestamp; + } + + /** + * Get the string value of the body in this Header. + * + * This is not necessarily RFC 2822 compliant since folding white space will + * not be added at this stage (see {@link toString()} for that). + * + * @see toString() + * + * @return string + */ + public function getFieldBody() + { + if (!$this->getCachedValue()) { + if (isset($this->_timestamp)) { + $this->setCachedValue(date('r', $this->_timestamp)); + } + } + + return $this->getCachedValue(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/IdentificationHeader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/IdentificationHeader.php new file mode 100644 index 0000000000..b114506b4a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/IdentificationHeader.php @@ -0,0 +1,180 @@ +setFieldName($name); + parent::__construct($grammar); + } + + /** + * Get the type of Header that this instance represents. + * + * @see TYPE_TEXT, TYPE_PARAMETERIZED, TYPE_MAILBOX + * @see TYPE_DATE, TYPE_ID, TYPE_PATH + * + * @return int + */ + public function getFieldType() + { + return self::TYPE_ID; + } + + /** + * Set the model for the field body. + * + * This method takes a string ID, or an array of IDs. + * + * @param mixed $model + * + * @throws Swift_RfcComplianceException + */ + public function setFieldBodyModel($model) + { + $this->setId($model); + } + + /** + * Get the model for the field body. + * + * This method returns an array of IDs + * + * @return array + */ + public function getFieldBodyModel() + { + return $this->getIds(); + } + + /** + * Set the ID used in the value of this header. + * + * @param string|array $id + * + * @throws Swift_RfcComplianceException + */ + public function setId($id) + { + $this->setIds(is_array($id) ? $id : array($id)); + } + + /** + * Get the ID used in the value of this Header. + * + * If multiple IDs are set only the first is returned. + * + * @return string + */ + public function getId() + { + if (count($this->_ids) > 0) { + return $this->_ids[0]; + } + } + + /** + * Set a collection of IDs to use in the value of this Header. + * + * @param string[] $ids + * + * @throws Swift_RfcComplianceException + */ + public function setIds(array $ids) + { + $actualIds = array(); + + foreach ($ids as $id) { + $this->_assertValidId($id); + $actualIds[] = $id; + } + + $this->clearCachedValueIf($this->_ids != $actualIds); + $this->_ids = $actualIds; + } + + /** + * Get the list of IDs used in this Header. + * + * @return string[] + */ + public function getIds() + { + return $this->_ids; + } + + /** + * Get the string value of the body in this Header. + * + * This is not necessarily RFC 2822 compliant since folding white space will + * not be added at this stage (see {@see toString()} for that). + * + * @see toString() + * + * @throws Swift_RfcComplianceException + * + * @return string + */ + public function getFieldBody() + { + if (!$this->getCachedValue()) { + $angleAddrs = array(); + + foreach ($this->_ids as $id) { + $angleAddrs[] = '<'.$id.'>'; + } + + $this->setCachedValue(implode(' ', $angleAddrs)); + } + + return $this->getCachedValue(); + } + + /** + * Throws an Exception if the id passed does not comply with RFC 2822. + * + * @param string $id + * + * @throws Swift_RfcComplianceException + */ + private function _assertValidId($id) + { + if (!preg_match( + '/^'.$this->getGrammar()->getDefinition('id-left').'@'. + $this->getGrammar()->getDefinition('id-right').'$/D', + $id + )) { + throw new Swift_RfcComplianceException( + 'Invalid ID given <'.$id.'>' + ); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/MailboxHeader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/MailboxHeader.php new file mode 100644 index 0000000000..e54b1c6624 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/MailboxHeader.php @@ -0,0 +1,353 @@ +setFieldName($name); + $this->setEncoder($encoder); + parent::__construct($grammar); + } + + /** + * Get the type of Header that this instance represents. + * + * @see TYPE_TEXT, TYPE_PARAMETERIZED, TYPE_MAILBOX + * @see TYPE_DATE, TYPE_ID, TYPE_PATH + * + * @return int + */ + public function getFieldType() + { + return self::TYPE_MAILBOX; + } + + /** + * Set the model for the field body. + * + * This method takes a string, or an array of addresses. + * + * @param mixed $model + * + * @throws Swift_RfcComplianceException + */ + public function setFieldBodyModel($model) + { + $this->setNameAddresses($model); + } + + /** + * Get the model for the field body. + * + * This method returns an associative array like {@link getNameAddresses()} + * + * @throws Swift_RfcComplianceException + * + * @return array + */ + public function getFieldBodyModel() + { + return $this->getNameAddresses(); + } + + /** + * Set a list of mailboxes to be shown in this Header. + * + * The mailboxes can be a simple array of addresses, or an array of + * key=>value pairs where (email => personalName). + * Example: + * + * setNameAddresses(array( + * 'chris@swiftmailer.org' => 'Chris Corbyn', + * 'mark@swiftmailer.org' //No associated personal name + * )); + * ?> + * + * + * @see __construct() + * @see setAddresses() + * @see setValue() + * + * @param string|string[] $mailboxes + * + * @throws Swift_RfcComplianceException + */ + public function setNameAddresses($mailboxes) + { + $this->_mailboxes = $this->normalizeMailboxes((array) $mailboxes); + $this->setCachedValue(null); //Clear any cached value + } + + /** + * Get the full mailbox list of this Header as an array of valid RFC 2822 strings. + * + * Example: + * + * 'Chris Corbyn', + * 'mark@swiftmailer.org' => 'Mark Corbyn') + * ); + * print_r($header->getNameAddressStrings()); + * // array ( + * // 0 => Chris Corbyn , + * // 1 => Mark Corbyn + * // ) + * ?> + * + * + * @see getNameAddresses() + * @see toString() + * + * @throws Swift_RfcComplianceException + * + * @return string[] + */ + public function getNameAddressStrings() + { + return $this->_createNameAddressStrings($this->getNameAddresses()); + } + + /** + * Get all mailboxes in this Header as key=>value pairs. + * + * The key is the address and the value is the name (or null if none set). + * Example: + * + * 'Chris Corbyn', + * 'mark@swiftmailer.org' => 'Mark Corbyn') + * ); + * print_r($header->getNameAddresses()); + * // array ( + * // chris@swiftmailer.org => Chris Corbyn, + * // mark@swiftmailer.org => Mark Corbyn + * // ) + * ?> + * + * + * @see getAddresses() + * @see getNameAddressStrings() + * + * @return string[] + */ + public function getNameAddresses() + { + return $this->_mailboxes; + } + + /** + * Makes this Header represent a list of plain email addresses with no names. + * + * Example: + * + * setAddresses( + * array('one@domain.tld', 'two@domain.tld', 'three@domain.tld') + * ); + * ?> + * + * + * @see setNameAddresses() + * @see setValue() + * + * @param string[] $addresses + * + * @throws Swift_RfcComplianceException + */ + public function setAddresses($addresses) + { + $this->setNameAddresses(array_values((array) $addresses)); + } + + /** + * Get all email addresses in this Header. + * + * @see getNameAddresses() + * + * @return string[] + */ + public function getAddresses() + { + return array_keys($this->_mailboxes); + } + + /** + * Remove one or more addresses from this Header. + * + * @param string|string[] $addresses + */ + public function removeAddresses($addresses) + { + $this->setCachedValue(null); + foreach ((array) $addresses as $address) { + unset($this->_mailboxes[$address]); + } + } + + /** + * Get the string value of the body in this Header. + * + * This is not necessarily RFC 2822 compliant since folding white space will + * not be added at this stage (see {@link toString()} for that). + * + * @see toString() + * + * @throws Swift_RfcComplianceException + * + * @return string + */ + public function getFieldBody() + { + // Compute the string value of the header only if needed + if (is_null($this->getCachedValue())) { + $this->setCachedValue($this->createMailboxListString($this->_mailboxes)); + } + + return $this->getCachedValue(); + } + + // -- Points of extension + + /** + * Normalizes a user-input list of mailboxes into consistent key=>value pairs. + * + * @param string[] $mailboxes + * + * @return string[] + */ + protected function normalizeMailboxes(array $mailboxes) + { + $actualMailboxes = array(); + + foreach ($mailboxes as $key => $value) { + if (is_string($key)) { + //key is email addr + $address = $key; + $name = $value; + } else { + $address = $value; + $name = null; + } + $this->_assertValidAddress($address); + $actualMailboxes[$address] = $name; + } + + return $actualMailboxes; + } + + /** + * Produces a compliant, formatted display-name based on the string given. + * + * @param string $displayName as displayed + * @param bool $shorten the first line to make remove for header name + * + * @return string + */ + protected function createDisplayNameString($displayName, $shorten = false) + { + return $this->createPhrase($this, $displayName, $this->getCharset(), $this->getEncoder(), $shorten); + } + + /** + * Creates a string form of all the mailboxes in the passed array. + * + * @param string[] $mailboxes + * + * @throws Swift_RfcComplianceException + * + * @return string + */ + protected function createMailboxListString(array $mailboxes) + { + return implode(', ', $this->_createNameAddressStrings($mailboxes)); + } + + /** + * Redefine the encoding requirements for mailboxes. + * + * All "specials" must be encoded as the full header value will not be quoted + * + * @see RFC 2822 3.2.1 + * + * @param string $token + * + * @return bool + */ + protected function tokenNeedsEncoding($token) + { + return preg_match('/[()<>\[\]:;@\,."]/', $token) || parent::tokenNeedsEncoding($token); + } + + /** + * Return an array of strings conforming the the name-addr spec of RFC 2822. + * + * @param string[] $mailboxes + * + * @return string[] + */ + private function _createNameAddressStrings(array $mailboxes) + { + $strings = array(); + + foreach ($mailboxes as $email => $name) { + $mailboxStr = $email; + if (!is_null($name)) { + $nameStr = $this->createDisplayNameString($name, empty($strings)); + $mailboxStr = $nameStr.' <'.$mailboxStr.'>'; + } + $strings[] = $mailboxStr; + } + + return $strings; + } + + /** + * Throws an Exception if the address passed does not comply with RFC 2822. + * + * @param string $address + * + * @throws Swift_RfcComplianceException If invalid. + */ + private function _assertValidAddress($address) + { + if (!preg_match('/^'.$this->getGrammar()->getDefinition('addr-spec').'$/D', + $address)) { + throw new Swift_RfcComplianceException( + 'Address in mailbox given ['.$address. + '] does not comply with RFC 2822, 3.6.2.' + ); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/OpenDKIMHeader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/OpenDKIMHeader.php new file mode 100644 index 0000000000..d749550079 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/OpenDKIMHeader.php @@ -0,0 +1,133 @@ + + */ +class Swift_Mime_Headers_OpenDKIMHeader implements Swift_Mime_Header +{ + /** + * The value of this Header. + * + * @var string + */ + private $_value; + + /** + * The name of this Header. + * + * @var string + */ + private $_fieldName; + + /** + * @param string $name + */ + public function __construct($name) + { + $this->_fieldName = $name; + } + + /** + * Get the type of Header that this instance represents. + * + * @see TYPE_TEXT, TYPE_PARAMETERIZED, TYPE_MAILBOX + * @see TYPE_DATE, TYPE_ID, TYPE_PATH + * + * @return int + */ + public function getFieldType() + { + return self::TYPE_TEXT; + } + + /** + * Set the model for the field body. + * + * This method takes a string for the field value. + * + * @param string $model + */ + public function setFieldBodyModel($model) + { + $this->setValue($model); + } + + /** + * Get the model for the field body. + * + * This method returns a string. + * + * @return string + */ + public function getFieldBodyModel() + { + return $this->getValue(); + } + + /** + * Get the (unencoded) value of this header. + * + * @return string + */ + public function getValue() + { + return $this->_value; + } + + /** + * Set the (unencoded) value of this header. + * + * @param string $value + */ + public function setValue($value) + { + $this->_value = $value; + } + + /** + * Get the value of this header prepared for rendering. + * + * @return string + */ + public function getFieldBody() + { + return $this->_value; + } + + /** + * Get this Header rendered as a RFC 2822 compliant string. + * + * @return string + */ + public function toString() + { + return $this->_fieldName.': '.$this->_value; + } + + /** + * Set the Header FieldName. + * + * @see Swift_Mime_Header::getFieldName() + */ + public function getFieldName() + { + return $this->_fieldName; + } + + /** + * Ignored. + */ + public function setCharset($charset) + { + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/ParameterizedHeader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/ParameterizedHeader.php new file mode 100644 index 0000000000..c506daec14 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/ParameterizedHeader.php @@ -0,0 +1,258 @@ +_paramEncoder = $paramEncoder; + } + + /** + * Get the type of Header that this instance represents. + * + * @see TYPE_TEXT, TYPE_PARAMETERIZED, TYPE_MAILBOX + * @see TYPE_DATE, TYPE_ID, TYPE_PATH + * + * @return int + */ + public function getFieldType() + { + return self::TYPE_PARAMETERIZED; + } + + /** + * Set the character set used in this Header. + * + * @param string $charset + */ + public function setCharset($charset) + { + parent::setCharset($charset); + if (isset($this->_paramEncoder)) { + $this->_paramEncoder->charsetChanged($charset); + } + } + + /** + * Set the value of $parameter. + * + * @param string $parameter + * @param string $value + */ + public function setParameter($parameter, $value) + { + $this->setParameters(array_merge($this->getParameters(), array($parameter => $value))); + } + + /** + * Get the value of $parameter. + * + * @param string $parameter + * + * @return string + */ + public function getParameter($parameter) + { + $params = $this->getParameters(); + + return array_key_exists($parameter, $params) ? $params[$parameter] : null; + } + + /** + * Set an associative array of parameter names mapped to values. + * + * @param string[] $parameters + */ + public function setParameters(array $parameters) + { + $this->clearCachedValueIf($this->_params != $parameters); + $this->_params = $parameters; + } + + /** + * Returns an associative array of parameter names mapped to values. + * + * @return string[] + */ + public function getParameters() + { + return $this->_params; + } + + /** + * Get the value of this header prepared for rendering. + * + * @return string + */ + public function getFieldBody() //TODO: Check caching here + { + $body = parent::getFieldBody(); + foreach ($this->_params as $name => $value) { + if (!is_null($value)) { + // Add the parameter + $body .= '; '.$this->_createParameter($name, $value); + } + } + + return $body; + } + + /** + * Generate a list of all tokens in the final header. + * + * This doesn't need to be overridden in theory, but it is for implementation + * reasons to prevent potential breakage of attributes. + * + * @param string $string The string to tokenize + * + * @return array An array of tokens as strings + */ + protected function toTokens($string = null) + { + $tokens = parent::toTokens(parent::getFieldBody()); + + // Try creating any parameters + foreach ($this->_params as $name => $value) { + if (!is_null($value)) { + // Add the semi-colon separator + $tokens[count($tokens) - 1] .= ';'; + $tokens = array_merge($tokens, $this->generateTokenLines( + ' '.$this->_createParameter($name, $value) + )); + } + } + + return $tokens; + } + + /** + * Render a RFC 2047 compliant header parameter from the $name and $value. + * + * @param string $name + * @param string $value + * + * @return string + */ + private function _createParameter($name, $value) + { + $origValue = $value; + + $encoded = false; + // Allow room for parameter name, indices, "=" and DQUOTEs + $maxValueLength = $this->getMaxLineLength() - strlen($name.'=*N"";') - 1; + $firstLineOffset = 0; + + // If it's not already a valid parameter value... + if (!preg_match('/^'.self::TOKEN_REGEX.'$/D', $value)) { + // TODO: text, or something else?? + // ... and it's not ascii + if (!preg_match('/^'.$this->getGrammar()->getDefinition('text').'*$/D', $value)) { + $encoded = true; + // Allow space for the indices, charset and language + $maxValueLength = $this->getMaxLineLength() - strlen($name.'*N*="";') - 1; + $firstLineOffset = strlen( + $this->getCharset()."'".$this->getLanguage()."'" + ); + } + } + + // Encode if we need to + if ($encoded || strlen($value) > $maxValueLength) { + if (isset($this->_paramEncoder)) { + $value = $this->_paramEncoder->encodeString( + $origValue, $firstLineOffset, $maxValueLength, $this->getCharset() + ); + } else { + // We have to go against RFC 2183/2231 in some areas for interoperability + $value = $this->getTokenAsEncodedWord($origValue); + $encoded = false; + } + } + + $valueLines = isset($this->_paramEncoder) ? explode("\r\n", $value) : array($value); + + // Need to add indices + if (count($valueLines) > 1) { + $paramLines = array(); + foreach ($valueLines as $i => $line) { + $paramLines[] = $name.'*'.$i. + $this->_getEndOfParameterValue($line, true, $i == 0); + } + + return implode(";\r\n ", $paramLines); + } else { + return $name.$this->_getEndOfParameterValue( + $valueLines[0], $encoded, true + ); + } + } + + /** + * Returns the parameter value from the "=" and beyond. + * + * @param string $value to append + * @param bool $encoded + * @param bool $firstLine + * + * @return string + */ + private function _getEndOfParameterValue($value, $encoded = false, $firstLine = false) + { + if (!preg_match('/^'.self::TOKEN_REGEX.'$/D', $value)) { + $value = '"'.$value.'"'; + } + $prepend = '='; + if ($encoded) { + $prepend = '*='; + if ($firstLine) { + $prepend = '*='.$this->getCharset()."'".$this->getLanguage(). + "'"; + } + } + + return $prepend.$value; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/PathHeader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/PathHeader.php new file mode 100644 index 0000000000..2fffc7b4aa --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/PathHeader.php @@ -0,0 +1,143 @@ +setFieldName($name); + parent::__construct($grammar); + } + + /** + * Get the type of Header that this instance represents. + * + * @see TYPE_TEXT, TYPE_PARAMETERIZED, TYPE_MAILBOX + * @see TYPE_DATE, TYPE_ID, TYPE_PATH + * + * @return int + */ + public function getFieldType() + { + return self::TYPE_PATH; + } + + /** + * Set the model for the field body. + * This method takes a string for an address. + * + * @param string $model + * + * @throws Swift_RfcComplianceException + */ + public function setFieldBodyModel($model) + { + $this->setAddress($model); + } + + /** + * Get the model for the field body. + * This method returns a string email address. + * + * @return mixed + */ + public function getFieldBodyModel() + { + return $this->getAddress(); + } + + /** + * Set the Address which should appear in this Header. + * + * @param string $address + * + * @throws Swift_RfcComplianceException + */ + public function setAddress($address) + { + if (is_null($address)) { + $this->_address = null; + } elseif ('' == $address) { + $this->_address = ''; + } else { + $this->_assertValidAddress($address); + $this->_address = $address; + } + $this->setCachedValue(null); + } + + /** + * Get the address which is used in this Header (if any). + * + * Null is returned if no address is set. + * + * @return string + */ + public function getAddress() + { + return $this->_address; + } + + /** + * Get the string value of the body in this Header. + * + * This is not necessarily RFC 2822 compliant since folding white space will + * not be added at this stage (see {@link toString()} for that). + * + * @see toString() + * + * @return string + */ + public function getFieldBody() + { + if (!$this->getCachedValue()) { + if (isset($this->_address)) { + $this->setCachedValue('<'.$this->_address.'>'); + } + } + + return $this->getCachedValue(); + } + + /** + * Throws an Exception if the address passed does not comply with RFC 2822. + * + * @param string $address + * + * @throws Swift_RfcComplianceException If address is invalid + */ + private function _assertValidAddress($address) + { + if (!preg_match('/^'.$this->getGrammar()->getDefinition('addr-spec').'$/D', + $address)) { + throw new Swift_RfcComplianceException( + 'Address set in PathHeader does not comply with addr-spec of RFC 2822.' + ); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/UnstructuredHeader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/UnstructuredHeader.php new file mode 100644 index 0000000000..86177f14a1 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/UnstructuredHeader.php @@ -0,0 +1,112 @@ +setFieldName($name); + $this->setEncoder($encoder); + parent::__construct($grammar); + } + + /** + * Get the type of Header that this instance represents. + * + * @see TYPE_TEXT, TYPE_PARAMETERIZED, TYPE_MAILBOX + * @see TYPE_DATE, TYPE_ID, TYPE_PATH + * + * @return int + */ + public function getFieldType() + { + return self::TYPE_TEXT; + } + + /** + * Set the model for the field body. + * + * This method takes a string for the field value. + * + * @param string $model + */ + public function setFieldBodyModel($model) + { + $this->setValue($model); + } + + /** + * Get the model for the field body. + * + * This method returns a string. + * + * @return string + */ + public function getFieldBodyModel() + { + return $this->getValue(); + } + + /** + * Get the (unencoded) value of this header. + * + * @return string + */ + public function getValue() + { + return $this->_value; + } + + /** + * Set the (unencoded) value of this header. + * + * @param string $value + */ + public function setValue($value) + { + $this->clearCachedValueIf($this->_value != $value); + $this->_value = $value; + } + + /** + * Get the value of this header prepared for rendering. + * + * @return string + */ + public function getFieldBody() + { + if (!$this->getCachedValue()) { + $this->setCachedValue( + $this->encodeWords($this, $this->_value) + ); + } + + return $this->getCachedValue(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Message.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Message.php new file mode 100644 index 0000000000..9b36d21627 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Message.php @@ -0,0 +1,223 @@ + 'Real Name'). + * + * If the second parameter is provided and the first is a string, then $name + * is associated with the address. + * + * @param mixed $address + * @param string $name optional + */ + public function setSender($address, $name = null); + + /** + * Get the sender address for this message. + * + * This has a higher significance than the From address. + * + * @return string + */ + public function getSender(); + + /** + * Set the From address of this message. + * + * It is permissible for multiple From addresses to be set using an array. + * + * If multiple From addresses are used, you SHOULD set the Sender address and + * according to RFC 2822, MUST set the sender address. + * + * An array can be used if display names are to be provided: i.e. + * array('email@address.com' => 'Real Name'). + * + * If the second parameter is provided and the first is a string, then $name + * is associated with the address. + * + * @param mixed $addresses + * @param string $name optional + */ + public function setFrom($addresses, $name = null); + + /** + * Get the From address(es) of this message. + * + * This method always returns an associative array where the keys are the + * addresses. + * + * @return string[] + */ + public function getFrom(); + + /** + * Set the Reply-To address(es). + * + * Any replies from the receiver will be sent to this address. + * + * It is permissible for multiple reply-to addresses to be set using an array. + * + * This method has the same synopsis as {@link setFrom()} and {@link setTo()}. + * + * If the second parameter is provided and the first is a string, then $name + * is associated with the address. + * + * @param mixed $addresses + * @param string $name optional + */ + public function setReplyTo($addresses, $name = null); + + /** + * Get the Reply-To addresses for this message. + * + * This method always returns an associative array where the keys provide the + * email addresses. + * + * @return string[] + */ + public function getReplyTo(); + + /** + * Set the To address(es). + * + * Recipients set in this field will receive a copy of this message. + * + * This method has the same synopsis as {@link setFrom()} and {@link setCc()}. + * + * If the second parameter is provided and the first is a string, then $name + * is associated with the address. + * + * @param mixed $addresses + * @param string $name optional + */ + public function setTo($addresses, $name = null); + + /** + * Get the To addresses for this message. + * + * This method always returns an associative array, whereby the keys provide + * the actual email addresses. + * + * @return string[] + */ + public function getTo(); + + /** + * Set the Cc address(es). + * + * Recipients set in this field will receive a 'carbon-copy' of this message. + * + * This method has the same synopsis as {@link setFrom()} and {@link setTo()}. + * + * @param mixed $addresses + * @param string $name optional + */ + public function setCc($addresses, $name = null); + + /** + * Get the Cc addresses for this message. + * + * This method always returns an associative array, whereby the keys provide + * the actual email addresses. + * + * @return string[] + */ + public function getCc(); + + /** + * Set the Bcc address(es). + * + * Recipients set in this field will receive a 'blind-carbon-copy' of this + * message. + * + * In other words, they will get the message, but any other recipients of the + * message will have no such knowledge of their receipt of it. + * + * This method has the same synopsis as {@link setFrom()} and {@link setTo()}. + * + * @param mixed $addresses + * @param string $name optional + */ + public function setBcc($addresses, $name = null); + + /** + * Get the Bcc addresses for this message. + * + * This method always returns an associative array, whereby the keys provide + * the actual email addresses. + * + * @return string[] + */ + public function getBcc(); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/MimeEntity.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/MimeEntity.php new file mode 100644 index 0000000000..30f460cdcd --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/MimeEntity.php @@ -0,0 +1,117 @@ +setContentType('text/plain'); + if (!is_null($charset)) { + $this->setCharset($charset); + } + } + + /** + * Set the body of this entity, either as a string, or as an instance of + * {@link Swift_OutputByteStream}. + * + * @param mixed $body + * @param string $contentType optional + * @param string $charset optional + * + * @return Swift_Mime_MimePart + */ + public function setBody($body, $contentType = null, $charset = null) + { + if (isset($charset)) { + $this->setCharset($charset); + } + $body = $this->_convertString($body); + + parent::setBody($body, $contentType); + + return $this; + } + + /** + * Get the character set of this entity. + * + * @return string + */ + public function getCharset() + { + return $this->_getHeaderParameter('Content-Type', 'charset'); + } + + /** + * Set the character set of this entity. + * + * @param string $charset + * + * @return Swift_Mime_MimePart + */ + public function setCharset($charset) + { + $this->_setHeaderParameter('Content-Type', 'charset', $charset); + if ($charset !== $this->_userCharset) { + $this->_clearCache(); + } + $this->_userCharset = $charset; + parent::charsetChanged($charset); + + return $this; + } + + /** + * Get the format of this entity (i.e. flowed or fixed). + * + * @return string + */ + public function getFormat() + { + return $this->_getHeaderParameter('Content-Type', 'format'); + } + + /** + * Set the format of this entity (flowed or fixed). + * + * @param string $format + * + * @return Swift_Mime_MimePart + */ + public function setFormat($format) + { + $this->_setHeaderParameter('Content-Type', 'format', $format); + $this->_userFormat = $format; + + return $this; + } + + /** + * Test if delsp is being used for this entity. + * + * @return bool + */ + public function getDelSp() + { + return 'yes' == $this->_getHeaderParameter('Content-Type', 'delsp') ? true : false; + } + + /** + * Turn delsp on or off for this entity. + * + * @param bool $delsp + * + * @return Swift_Mime_MimePart + */ + public function setDelSp($delsp = true) + { + $this->_setHeaderParameter('Content-Type', 'delsp', $delsp ? 'yes' : null); + $this->_userDelSp = $delsp; + + return $this; + } + + /** + * Get the nesting level of this entity. + * + * @see LEVEL_TOP, LEVEL_ALTERNATIVE, LEVEL_MIXED, LEVEL_RELATED + * + * @return int + */ + public function getNestingLevel() + { + return $this->_nestingLevel; + } + + /** + * Receive notification that the charset has changed on this document, or a + * parent document. + * + * @param string $charset + */ + public function charsetChanged($charset) + { + $this->setCharset($charset); + } + + /** Fix the content-type and encoding of this entity */ + protected function _fixHeaders() + { + parent::_fixHeaders(); + if (count($this->getChildren())) { + $this->_setHeaderParameter('Content-Type', 'charset', null); + $this->_setHeaderParameter('Content-Type', 'format', null); + $this->_setHeaderParameter('Content-Type', 'delsp', null); + } else { + $this->setCharset($this->_userCharset); + $this->setFormat($this->_userFormat); + $this->setDelSp($this->_userDelSp); + } + } + + /** Set the nesting level of this entity */ + protected function _setNestingLevel($level) + { + $this->_nestingLevel = $level; + } + + /** Encode charset when charset is not utf-8 */ + protected function _convertString($string) + { + $charset = strtolower($this->getCharset()); + if (!in_array($charset, array('utf-8', 'iso-8859-1', 'iso-8859-15', ''))) { + // mb_convert_encoding must be the first one to check, since iconv cannot convert some words. + if (function_exists('mb_convert_encoding')) { + $string = mb_convert_encoding($string, $charset, 'utf-8'); + } elseif (function_exists('iconv')) { + $string = iconv('utf-8//TRANSLIT//IGNORE', $charset, $string); + } else { + throw new Swift_SwiftException('No suitable convert encoding function (use UTF-8 as your charset or install the mbstring or iconv extension).'); + } + + return $string; + } + + return $string; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ParameterizedHeader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ParameterizedHeader.php new file mode 100644 index 0000000000..e15c6ef95b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ParameterizedHeader.php @@ -0,0 +1,34 @@ +_encoder = $encoder; + $this->_paramEncoder = $paramEncoder; + $this->_grammar = $grammar; + $this->_charset = $charset; + } + + /** + * Create a new Mailbox Header with a list of $addresses. + * + * @param string $name + * @param array|string|null $addresses + * + * @return Swift_Mime_Header + */ + public function createMailboxHeader($name, $addresses = null) + { + $header = new Swift_Mime_Headers_MailboxHeader($name, $this->_encoder, $this->_grammar); + if (isset($addresses)) { + $header->setFieldBodyModel($addresses); + } + $this->_setHeaderCharset($header); + + return $header; + } + + /** + * Create a new Date header using $timestamp (UNIX time). + * + * @param string $name + * @param int|null $timestamp + * + * @return Swift_Mime_Header + */ + public function createDateHeader($name, $timestamp = null) + { + $header = new Swift_Mime_Headers_DateHeader($name, $this->_grammar); + if (isset($timestamp)) { + $header->setFieldBodyModel($timestamp); + } + $this->_setHeaderCharset($header); + + return $header; + } + + /** + * Create a new basic text header with $name and $value. + * + * @param string $name + * @param string $value + * + * @return Swift_Mime_Header + */ + public function createTextHeader($name, $value = null) + { + $header = new Swift_Mime_Headers_UnstructuredHeader($name, $this->_encoder, $this->_grammar); + if (isset($value)) { + $header->setFieldBodyModel($value); + } + $this->_setHeaderCharset($header); + + return $header; + } + + /** + * Create a new ParameterizedHeader with $name, $value and $params. + * + * @param string $name + * @param string $value + * @param array $params + * + * @return Swift_Mime_ParameterizedHeader + */ + public function createParameterizedHeader($name, $value = null, + $params = array()) + { + $header = new Swift_Mime_Headers_ParameterizedHeader($name, $this->_encoder, strtolower($name) == 'content-disposition' ? $this->_paramEncoder : null, $this->_grammar); + if (isset($value)) { + $header->setFieldBodyModel($value); + } + foreach ($params as $k => $v) { + $header->setParameter($k, $v); + } + $this->_setHeaderCharset($header); + + return $header; + } + + /** + * Create a new ID header for Message-ID or Content-ID. + * + * @param string $name + * @param string|array $ids + * + * @return Swift_Mime_Header + */ + public function createIdHeader($name, $ids = null) + { + $header = new Swift_Mime_Headers_IdentificationHeader($name, $this->_grammar); + if (isset($ids)) { + $header->setFieldBodyModel($ids); + } + $this->_setHeaderCharset($header); + + return $header; + } + + /** + * Create a new Path header with an address (path) in it. + * + * @param string $name + * @param string $path + * + * @return Swift_Mime_Header + */ + public function createPathHeader($name, $path = null) + { + $header = new Swift_Mime_Headers_PathHeader($name, $this->_grammar); + if (isset($path)) { + $header->setFieldBodyModel($path); + } + $this->_setHeaderCharset($header); + + return $header; + } + + /** + * Notify this observer that the entity's charset has changed. + * + * @param string $charset + */ + public function charsetChanged($charset) + { + $this->_charset = $charset; + $this->_encoder->charsetChanged($charset); + $this->_paramEncoder->charsetChanged($charset); + } + + /** + * Make a deep copy of object. + */ + public function __clone() + { + $this->_encoder = clone $this->_encoder; + $this->_paramEncoder = clone $this->_paramEncoder; + } + + /** Apply the charset to the Header */ + private function _setHeaderCharset(Swift_Mime_Header $header) + { + if (isset($this->_charset)) { + $header->setCharset($this->_charset); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleHeaderSet.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleHeaderSet.php new file mode 100644 index 0000000000..e2d0e87466 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleHeaderSet.php @@ -0,0 +1,414 @@ +_factory = $factory; + if (isset($charset)) { + $this->setCharset($charset); + } + } + + /** + * Set the charset used by these headers. + * + * @param string $charset + */ + public function setCharset($charset) + { + $this->_charset = $charset; + $this->_factory->charsetChanged($charset); + $this->_notifyHeadersOfCharset($charset); + } + + /** + * Add a new Mailbox Header with a list of $addresses. + * + * @param string $name + * @param array|string $addresses + */ + public function addMailboxHeader($name, $addresses = null) + { + $this->_storeHeader($name, + $this->_factory->createMailboxHeader($name, $addresses)); + } + + /** + * Add a new Date header using $timestamp (UNIX time). + * + * @param string $name + * @param int $timestamp + */ + public function addDateHeader($name, $timestamp = null) + { + $this->_storeHeader($name, + $this->_factory->createDateHeader($name, $timestamp)); + } + + /** + * Add a new basic text header with $name and $value. + * + * @param string $name + * @param string $value + */ + public function addTextHeader($name, $value = null) + { + $this->_storeHeader($name, + $this->_factory->createTextHeader($name, $value)); + } + + /** + * Add a new ParameterizedHeader with $name, $value and $params. + * + * @param string $name + * @param string $value + * @param array $params + */ + public function addParameterizedHeader($name, $value = null, $params = array()) + { + $this->_storeHeader($name, $this->_factory->createParameterizedHeader($name, $value, $params)); + } + + /** + * Add a new ID header for Message-ID or Content-ID. + * + * @param string $name + * @param string|array $ids + */ + public function addIdHeader($name, $ids = null) + { + $this->_storeHeader($name, $this->_factory->createIdHeader($name, $ids)); + } + + /** + * Add a new Path header with an address (path) in it. + * + * @param string $name + * @param string $path + */ + public function addPathHeader($name, $path = null) + { + $this->_storeHeader($name, $this->_factory->createPathHeader($name, $path)); + } + + /** + * Returns true if at least one header with the given $name exists. + * + * If multiple headers match, the actual one may be specified by $index. + * + * @param string $name + * @param int $index + * + * @return bool + */ + public function has($name, $index = 0) + { + $lowerName = strtolower($name); + + if (!array_key_exists($lowerName, $this->_headers)) { + return false; + } + + if (func_num_args() < 2) { + // index was not specified, so we only need to check that there is at least one header value set + return (bool) count($this->_headers[$lowerName]); + } + + return array_key_exists($index, $this->_headers[$lowerName]); + } + + /** + * Set a header in the HeaderSet. + * + * The header may be a previously fetched header via {@link get()} or it may + * be one that has been created separately. + * + * If $index is specified, the header will be inserted into the set at this + * offset. + * + * @param Swift_Mime_Header $header + * @param int $index + */ + public function set(Swift_Mime_Header $header, $index = 0) + { + $this->_storeHeader($header->getFieldName(), $header, $index); + } + + /** + * Get the header with the given $name. + * + * If multiple headers match, the actual one may be specified by $index. + * Returns NULL if none present. + * + * @param string $name + * @param int $index + * + * @return Swift_Mime_Header + */ + public function get($name, $index = 0) + { + $name = strtolower($name); + + if (func_num_args() < 2) { + if ($this->has($name)) { + $values = array_values($this->_headers[$name]); + + return array_shift($values); + } + } else { + if ($this->has($name, $index)) { + return $this->_headers[$name][$index]; + } + } + } + + /** + * Get all headers with the given $name. + * + * @param string $name + * + * @return array + */ + public function getAll($name = null) + { + if (!isset($name)) { + $headers = array(); + foreach ($this->_headers as $collection) { + $headers = array_merge($headers, $collection); + } + + return $headers; + } + + $lowerName = strtolower($name); + if (!array_key_exists($lowerName, $this->_headers)) { + return array(); + } + + return $this->_headers[$lowerName]; + } + + /** + * Return the name of all Headers. + * + * @return array + */ + public function listAll() + { + $headers = $this->_headers; + if ($this->_canSort()) { + uksort($headers, array($this, '_sortHeaders')); + } + + return array_keys($headers); + } + + /** + * Remove the header with the given $name if it's set. + * + * If multiple headers match, the actual one may be specified by $index. + * + * @param string $name + * @param int $index + */ + public function remove($name, $index = 0) + { + $lowerName = strtolower($name); + unset($this->_headers[$lowerName][$index]); + } + + /** + * Remove all headers with the given $name. + * + * @param string $name + */ + public function removeAll($name) + { + $lowerName = strtolower($name); + unset($this->_headers[$lowerName]); + } + + /** + * Create a new instance of this HeaderSet. + * + * @return Swift_Mime_HeaderSet + */ + public function newInstance() + { + return new self($this->_factory); + } + + /** + * Define a list of Header names as an array in the correct order. + * + * These Headers will be output in the given order where present. + * + * @param array $sequence + */ + public function defineOrdering(array $sequence) + { + $this->_order = array_flip(array_map('strtolower', $sequence)); + } + + /** + * Set a list of header names which must always be displayed when set. + * + * Usually headers without a field value won't be output unless set here. + * + * @param array $names + */ + public function setAlwaysDisplayed(array $names) + { + $this->_required = array_flip(array_map('strtolower', $names)); + } + + /** + * Notify this observer that the entity's charset has changed. + * + * @param string $charset + */ + public function charsetChanged($charset) + { + $this->setCharset($charset); + } + + /** + * Returns a string with a representation of all headers. + * + * @return string + */ + public function toString() + { + $string = ''; + $headers = $this->_headers; + if ($this->_canSort()) { + uksort($headers, array($this, '_sortHeaders')); + } + foreach ($headers as $collection) { + foreach ($collection as $header) { + if ($this->_isDisplayed($header) || $header->getFieldBody() != '') { + $string .= $header->toString(); + } + } + } + + return $string; + } + + /** + * Returns a string representation of this object. + * + * @return string + * + * @see toString() + */ + public function __toString() + { + return $this->toString(); + } + + /** Save a Header to the internal collection */ + private function _storeHeader($name, Swift_Mime_Header $header, $offset = null) + { + if (!isset($this->_headers[strtolower($name)])) { + $this->_headers[strtolower($name)] = array(); + } + if (!isset($offset)) { + $this->_headers[strtolower($name)][] = $header; + } else { + $this->_headers[strtolower($name)][$offset] = $header; + } + } + + /** Test if the headers can be sorted */ + private function _canSort() + { + return count($this->_order) > 0; + } + + /** uksort() algorithm for Header ordering */ + private function _sortHeaders($a, $b) + { + $lowerA = strtolower($a); + $lowerB = strtolower($b); + $aPos = array_key_exists($lowerA, $this->_order) ? $this->_order[$lowerA] : -1; + $bPos = array_key_exists($lowerB, $this->_order) ? $this->_order[$lowerB] : -1; + + if (-1 === $aPos && -1 === $bPos) { + // just be sure to be determinist here + return $a > $b ? -1 : 1; + } + + if ($aPos == -1) { + return 1; + } elseif ($bPos == -1) { + return -1; + } + + return $aPos < $bPos ? -1 : 1; + } + + /** Test if the given Header is always displayed */ + private function _isDisplayed(Swift_Mime_Header $header) + { + return array_key_exists(strtolower($header->getFieldName()), $this->_required); + } + + /** Notify all Headers of the new charset */ + private function _notifyHeadersOfCharset($charset) + { + foreach ($this->_headers as $headerGroup) { + foreach ($headerGroup as $header) { + $header->setCharset($charset); + } + } + } + + /** + * Make a deep copy of object. + */ + public function __clone() + { + $this->_factory = clone $this->_factory; + foreach ($this->_headers as $groupKey => $headerGroup) { + foreach ($headerGroup as $key => $header) { + $this->_headers[$groupKey][$key] = clone $header; + } + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleMessage.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleMessage.php new file mode 100644 index 0000000000..72d40cee02 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleMessage.php @@ -0,0 +1,655 @@ +getHeaders()->defineOrdering(array( + 'Return-Path', + 'Received', + 'DKIM-Signature', + 'DomainKey-Signature', + 'Sender', + 'Message-ID', + 'Date', + 'Subject', + 'From', + 'Reply-To', + 'To', + 'Cc', + 'Bcc', + 'MIME-Version', + 'Content-Type', + 'Content-Transfer-Encoding', + )); + $this->getHeaders()->setAlwaysDisplayed(array('Date', 'Message-ID', 'From')); + $this->getHeaders()->addTextHeader('MIME-Version', '1.0'); + $this->setDate(time()); + $this->setId($this->getId()); + $this->getHeaders()->addMailboxHeader('From'); + } + + /** + * Always returns {@link LEVEL_TOP} for a message instance. + * + * @return int + */ + public function getNestingLevel() + { + return self::LEVEL_TOP; + } + + /** + * Set the subject of this message. + * + * @param string $subject + * + * @return $this + */ + public function setSubject($subject) + { + if (!$this->_setHeaderFieldModel('Subject', $subject)) { + $this->getHeaders()->addTextHeader('Subject', $subject); + } + + return $this; + } + + /** + * Get the subject of this message. + * + * @return string + */ + public function getSubject() + { + return $this->_getHeaderFieldModel('Subject'); + } + + /** + * Set the date at which this message was created. + * + * @param int $date + * + * @return $this + */ + public function setDate($date) + { + if (!$this->_setHeaderFieldModel('Date', $date)) { + $this->getHeaders()->addDateHeader('Date', $date); + } + + return $this; + } + + /** + * Get the date at which this message was created. + * + * @return int + */ + public function getDate() + { + return $this->_getHeaderFieldModel('Date'); + } + + /** + * Set the return-path (the bounce address) of this message. + * + * @param string $address + * + * @return $this + */ + public function setReturnPath($address) + { + if (!$this->_setHeaderFieldModel('Return-Path', $address)) { + $this->getHeaders()->addPathHeader('Return-Path', $address); + } + + return $this; + } + + /** + * Get the return-path (bounce address) of this message. + * + * @return string + */ + public function getReturnPath() + { + return $this->_getHeaderFieldModel('Return-Path'); + } + + /** + * Set the sender of this message. + * + * This does not override the From field, but it has a higher significance. + * + * @param string $address + * @param string $name optional + * + * @return $this + */ + public function setSender($address, $name = null) + { + if (!is_array($address) && isset($name)) { + $address = array($address => $name); + } + + if (!$this->_setHeaderFieldModel('Sender', (array) $address)) { + $this->getHeaders()->addMailboxHeader('Sender', (array) $address); + } + + return $this; + } + + /** + * Get the sender of this message. + * + * @return string + */ + public function getSender() + { + return $this->_getHeaderFieldModel('Sender'); + } + + /** + * Add a From: address to this message. + * + * If $name is passed this name will be associated with the address. + * + * @param string $address + * @param string $name optional + * + * @return $this + */ + public function addFrom($address, $name = null) + { + $current = $this->getFrom(); + $current[$address] = $name; + + return $this->setFrom($current); + } + + /** + * Set the from address of this message. + * + * You may pass an array of addresses if this message is from multiple people. + * + * If $name is passed and the first parameter is a string, this name will be + * associated with the address. + * + * @param string|array $addresses + * @param string $name optional + * + * @return $this + */ + public function setFrom($addresses, $name = null) + { + if (!is_array($addresses) && isset($name)) { + $addresses = array($addresses => $name); + } + + if (!$this->_setHeaderFieldModel('From', (array) $addresses)) { + $this->getHeaders()->addMailboxHeader('From', (array) $addresses); + } + + return $this; + } + + /** + * Get the from address of this message. + * + * @return mixed + */ + public function getFrom() + { + return $this->_getHeaderFieldModel('From'); + } + + /** + * Add a Reply-To: address to this message. + * + * If $name is passed this name will be associated with the address. + * + * @param string $address + * @param string $name optional + * + * @return $this + */ + public function addReplyTo($address, $name = null) + { + $current = $this->getReplyTo(); + $current[$address] = $name; + + return $this->setReplyTo($current); + } + + /** + * Set the reply-to address of this message. + * + * You may pass an array of addresses if replies will go to multiple people. + * + * If $name is passed and the first parameter is a string, this name will be + * associated with the address. + * + * @param mixed $addresses + * @param string $name optional + * + * @return $this + */ + public function setReplyTo($addresses, $name = null) + { + if (!is_array($addresses) && isset($name)) { + $addresses = array($addresses => $name); + } + + if (!$this->_setHeaderFieldModel('Reply-To', (array) $addresses)) { + $this->getHeaders()->addMailboxHeader('Reply-To', (array) $addresses); + } + + return $this; + } + + /** + * Get the reply-to address of this message. + * + * @return string + */ + public function getReplyTo() + { + return $this->_getHeaderFieldModel('Reply-To'); + } + + /** + * Add a To: address to this message. + * + * If $name is passed this name will be associated with the address. + * + * @param string $address + * @param string $name optional + * + * @return $this + */ + public function addTo($address, $name = null) + { + $current = $this->getTo(); + $current[$address] = $name; + + return $this->setTo($current); + } + + /** + * Set the to addresses of this message. + * + * If multiple recipients will receive the message an array should be used. + * Example: array('receiver@domain.org', 'other@domain.org' => 'A name') + * + * If $name is passed and the first parameter is a string, this name will be + * associated with the address. + * + * @param mixed $addresses + * @param string $name optional + * + * @return $this + */ + public function setTo($addresses, $name = null) + { + if (!is_array($addresses) && isset($name)) { + $addresses = array($addresses => $name); + } + + if (!$this->_setHeaderFieldModel('To', (array) $addresses)) { + $this->getHeaders()->addMailboxHeader('To', (array) $addresses); + } + + return $this; + } + + /** + * Get the To addresses of this message. + * + * @return array + */ + public function getTo() + { + return $this->_getHeaderFieldModel('To'); + } + + /** + * Add a Cc: address to this message. + * + * If $name is passed this name will be associated with the address. + * + * @param string $address + * @param string $name optional + * + * @return $this + */ + public function addCc($address, $name = null) + { + $current = $this->getCc(); + $current[$address] = $name; + + return $this->setCc($current); + } + + /** + * Set the Cc addresses of this message. + * + * If $name is passed and the first parameter is a string, this name will be + * associated with the address. + * + * @param mixed $addresses + * @param string $name optional + * + * @return $this + */ + public function setCc($addresses, $name = null) + { + if (!is_array($addresses) && isset($name)) { + $addresses = array($addresses => $name); + } + + if (!$this->_setHeaderFieldModel('Cc', (array) $addresses)) { + $this->getHeaders()->addMailboxHeader('Cc', (array) $addresses); + } + + return $this; + } + + /** + * Get the Cc address of this message. + * + * @return array + */ + public function getCc() + { + return $this->_getHeaderFieldModel('Cc'); + } + + /** + * Add a Bcc: address to this message. + * + * If $name is passed this name will be associated with the address. + * + * @param string $address + * @param string $name optional + * + * @return $this + */ + public function addBcc($address, $name = null) + { + $current = $this->getBcc(); + $current[$address] = $name; + + return $this->setBcc($current); + } + + /** + * Set the Bcc addresses of this message. + * + * If $name is passed and the first parameter is a string, this name will be + * associated with the address. + * + * @param mixed $addresses + * @param string $name optional + * + * @return $this + */ + public function setBcc($addresses, $name = null) + { + if (!is_array($addresses) && isset($name)) { + $addresses = array($addresses => $name); + } + + if (!$this->_setHeaderFieldModel('Bcc', (array) $addresses)) { + $this->getHeaders()->addMailboxHeader('Bcc', (array) $addresses); + } + + return $this; + } + + /** + * Get the Bcc addresses of this message. + * + * @return array + */ + public function getBcc() + { + return $this->_getHeaderFieldModel('Bcc'); + } + + /** + * Set the priority of this message. + * + * The value is an integer where 1 is the highest priority and 5 is the lowest. + * + * @param int $priority + * + * @return $this + */ + public function setPriority($priority) + { + $priorityMap = array( + self::PRIORITY_HIGHEST => 'Highest', + self::PRIORITY_HIGH => 'High', + self::PRIORITY_NORMAL => 'Normal', + self::PRIORITY_LOW => 'Low', + self::PRIORITY_LOWEST => 'Lowest', + ); + $pMapKeys = array_keys($priorityMap); + if ($priority > max($pMapKeys)) { + $priority = max($pMapKeys); + } elseif ($priority < min($pMapKeys)) { + $priority = min($pMapKeys); + } + if (!$this->_setHeaderFieldModel('X-Priority', + sprintf('%d (%s)', $priority, $priorityMap[$priority]))) { + $this->getHeaders()->addTextHeader('X-Priority', + sprintf('%d (%s)', $priority, $priorityMap[$priority])); + } + + return $this; + } + + /** + * Get the priority of this message. + * + * The returned value is an integer where 1 is the highest priority and 5 + * is the lowest. + * + * @return int + */ + public function getPriority() + { + list($priority) = sscanf($this->_getHeaderFieldModel('X-Priority'), + '%[1-5]' + ); + + return isset($priority) ? $priority : 3; + } + + /** + * Ask for a delivery receipt from the recipient to be sent to $addresses. + * + * @param array $addresses + * + * @return $this + */ + public function setReadReceiptTo($addresses) + { + if (!$this->_setHeaderFieldModel('Disposition-Notification-To', $addresses)) { + $this->getHeaders() + ->addMailboxHeader('Disposition-Notification-To', $addresses); + } + + return $this; + } + + /** + * Get the addresses to which a read-receipt will be sent. + * + * @return string + */ + public function getReadReceiptTo() + { + return $this->_getHeaderFieldModel('Disposition-Notification-To'); + } + + /** + * Attach a {@link Swift_Mime_MimeEntity} such as an Attachment or MimePart. + * + * @param Swift_Mime_MimeEntity $entity + * + * @return $this + */ + public function attach(Swift_Mime_MimeEntity $entity) + { + $this->setChildren(array_merge($this->getChildren(), array($entity))); + + return $this; + } + + /** + * Remove an already attached entity. + * + * @param Swift_Mime_MimeEntity $entity + * + * @return $this + */ + public function detach(Swift_Mime_MimeEntity $entity) + { + $newChildren = array(); + foreach ($this->getChildren() as $child) { + if ($entity !== $child) { + $newChildren[] = $child; + } + } + $this->setChildren($newChildren); + + return $this; + } + + /** + * Attach a {@link Swift_Mime_MimeEntity} and return it's CID source. + * This method should be used when embedding images or other data in a message. + * + * @param Swift_Mime_MimeEntity $entity + * + * @return string + */ + public function embed(Swift_Mime_MimeEntity $entity) + { + $this->attach($entity); + + return 'cid:'.$entity->getId(); + } + + /** + * Get this message as a complete string. + * + * @return string + */ + public function toString() + { + if (count($children = $this->getChildren()) > 0 && $this->getBody() != '') { + $this->setChildren(array_merge(array($this->_becomeMimePart()), $children)); + $string = parent::toString(); + $this->setChildren($children); + } else { + $string = parent::toString(); + } + + return $string; + } + + /** + * Returns a string representation of this object. + * + * @see toString() + * + * @return string + */ + public function __toString() + { + return $this->toString(); + } + + /** + * Write this message to a {@link Swift_InputByteStream}. + * + * @param Swift_InputByteStream $is + */ + public function toByteStream(Swift_InputByteStream $is) + { + if (count($children = $this->getChildren()) > 0 && $this->getBody() != '') { + $this->setChildren(array_merge(array($this->_becomeMimePart()), $children)); + parent::toByteStream($is); + $this->setChildren($children); + } else { + parent::toByteStream($is); + } + } + + /** @see Swift_Mime_SimpleMimeEntity::_getIdField() */ + protected function _getIdField() + { + return 'Message-ID'; + } + + /** Turn the body of this message into a child of itself if needed */ + protected function _becomeMimePart() + { + $part = new parent($this->getHeaders()->newInstance(), $this->getEncoder(), + $this->_getCache(), $this->_getGrammar(), $this->_userCharset + ); + $part->setContentType($this->_userContentType); + $part->setBody($this->getBody()); + $part->setFormat($this->_userFormat); + $part->setDelSp($this->_userDelSp); + $part->_setNestingLevel($this->_getTopNestingLevel()); + + return $part; + } + + /** Get the highest nesting level nested inside this message */ + private function _getTopNestingLevel() + { + $highestLevel = $this->getNestingLevel(); + foreach ($this->getChildren() as $child) { + $childLevel = $child->getNestingLevel(); + if ($highestLevel < $childLevel) { + $highestLevel = $childLevel; + } + } + + return $highestLevel; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleMimeEntity.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleMimeEntity.php new file mode 100644 index 0000000000..f169584d3f --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleMimeEntity.php @@ -0,0 +1,843 @@ + array(self::LEVEL_TOP, self::LEVEL_MIXED), + 'multipart/alternative' => array(self::LEVEL_MIXED, self::LEVEL_ALTERNATIVE), + 'multipart/related' => array(self::LEVEL_ALTERNATIVE, self::LEVEL_RELATED), + ); + + /** A set of filter rules to define what level an entity should be nested at */ + private $_compoundLevelFilters = array(); + + /** The nesting level of this entity */ + private $_nestingLevel = self::LEVEL_ALTERNATIVE; + + /** A KeyCache instance used during encoding and streaming */ + private $_cache; + + /** Direct descendants of this entity */ + private $_immediateChildren = array(); + + /** All descendants of this entity */ + private $_children = array(); + + /** The maximum line length of the body of this entity */ + private $_maxLineLength = 78; + + /** The order in which alternative mime types should appear */ + private $_alternativePartOrder = array( + 'text/plain' => 1, + 'text/html' => 2, + 'multipart/related' => 3, + ); + + /** The CID of this entity */ + private $_id; + + /** The key used for accessing the cache */ + private $_cacheKey; + + protected $_userContentType; + + /** + * Create a new SimpleMimeEntity with $headers, $encoder and $cache. + * + * @param Swift_Mime_HeaderSet $headers + * @param Swift_Mime_ContentEncoder $encoder + * @param Swift_KeyCache $cache + * @param Swift_Mime_Grammar $grammar + */ + public function __construct(Swift_Mime_HeaderSet $headers, Swift_Mime_ContentEncoder $encoder, Swift_KeyCache $cache, Swift_Mime_Grammar $grammar) + { + $this->_cacheKey = md5(uniqid(getmypid().mt_rand(), true)); + $this->_cache = $cache; + $this->_headers = $headers; + $this->_grammar = $grammar; + $this->setEncoder($encoder); + $this->_headers->defineOrdering(array('Content-Type', 'Content-Transfer-Encoding')); + + // This array specifies that, when the entire MIME document contains + // $compoundLevel, then for each child within $level, if its Content-Type + // is $contentType then it should be treated as if it's level is + // $neededLevel instead. I tried to write that unambiguously! :-\ + // Data Structure: + // array ( + // $compoundLevel => array( + // $level => array( + // $contentType => $neededLevel + // ) + // ) + // ) + + $this->_compoundLevelFilters = array( + (self::LEVEL_ALTERNATIVE + self::LEVEL_RELATED) => array( + self::LEVEL_ALTERNATIVE => array( + 'text/plain' => self::LEVEL_ALTERNATIVE, + 'text/html' => self::LEVEL_RELATED, + ), + ), + ); + + $this->_id = $this->getRandomId(); + } + + /** + * Generate a new Content-ID or Message-ID for this MIME entity. + * + * @return string + */ + public function generateId() + { + $this->setId($this->getRandomId()); + + return $this->_id; + } + + /** + * Get the {@link Swift_Mime_HeaderSet} for this entity. + * + * @return Swift_Mime_HeaderSet + */ + public function getHeaders() + { + return $this->_headers; + } + + /** + * Get the nesting level of this entity. + * + * @see LEVEL_TOP, LEVEL_MIXED, LEVEL_RELATED, LEVEL_ALTERNATIVE + * + * @return int + */ + public function getNestingLevel() + { + return $this->_nestingLevel; + } + + /** + * Get the Content-type of this entity. + * + * @return string + */ + public function getContentType() + { + return $this->_getHeaderFieldModel('Content-Type'); + } + + /** + * Set the Content-type of this entity. + * + * @param string $type + * + * @return $this + */ + public function setContentType($type) + { + $this->_setContentTypeInHeaders($type); + // Keep track of the value so that if the content-type changes automatically + // due to added child entities, it can be restored if they are later removed + $this->_userContentType = $type; + + return $this; + } + + /** + * Get the CID of this entity. + * + * The CID will only be present in headers if a Content-ID header is present. + * + * @return string + */ + public function getId() + { + $tmp = (array) $this->_getHeaderFieldModel($this->_getIdField()); + + return $this->_headers->has($this->_getIdField()) ? current($tmp) : $this->_id; + } + + /** + * Set the CID of this entity. + * + * @param string $id + * + * @return $this + */ + public function setId($id) + { + if (!$this->_setHeaderFieldModel($this->_getIdField(), $id)) { + $this->_headers->addIdHeader($this->_getIdField(), $id); + } + $this->_id = $id; + + return $this; + } + + /** + * Get the description of this entity. + * + * This value comes from the Content-Description header if set. + * + * @return string + */ + public function getDescription() + { + return $this->_getHeaderFieldModel('Content-Description'); + } + + /** + * Set the description of this entity. + * + * This method sets a value in the Content-ID header. + * + * @param string $description + * + * @return $this + */ + public function setDescription($description) + { + if (!$this->_setHeaderFieldModel('Content-Description', $description)) { + $this->_headers->addTextHeader('Content-Description', $description); + } + + return $this; + } + + /** + * Get the maximum line length of the body of this entity. + * + * @return int + */ + public function getMaxLineLength() + { + return $this->_maxLineLength; + } + + /** + * Set the maximum line length of lines in this body. + * + * Though not enforced by the library, lines should not exceed 1000 chars. + * + * @param int $length + * + * @return $this + */ + public function setMaxLineLength($length) + { + $this->_maxLineLength = $length; + + return $this; + } + + /** + * Get all children added to this entity. + * + * @return Swift_Mime_MimeEntity[] + */ + public function getChildren() + { + return $this->_children; + } + + /** + * Set all children of this entity. + * + * @param Swift_Mime_MimeEntity[] $children + * @param int $compoundLevel For internal use only + * + * @return $this + */ + public function setChildren(array $children, $compoundLevel = null) + { + // TODO: Try to refactor this logic + + $compoundLevel = isset($compoundLevel) ? $compoundLevel : $this->_getCompoundLevel($children); + $immediateChildren = array(); + $grandchildren = array(); + $newContentType = $this->_userContentType; + + foreach ($children as $child) { + $level = $this->_getNeededChildLevel($child, $compoundLevel); + if (empty($immediateChildren)) { + //first iteration + $immediateChildren = array($child); + } else { + $nextLevel = $this->_getNeededChildLevel($immediateChildren[0], $compoundLevel); + if ($nextLevel == $level) { + $immediateChildren[] = $child; + } elseif ($level < $nextLevel) { + // Re-assign immediateChildren to grandchildren + $grandchildren = array_merge($grandchildren, $immediateChildren); + // Set new children + $immediateChildren = array($child); + } else { + $grandchildren[] = $child; + } + } + } + + if ($immediateChildren) { + $lowestLevel = $this->_getNeededChildLevel($immediateChildren[0], $compoundLevel); + + // Determine which composite media type is needed to accommodate the + // immediate children + foreach ($this->_compositeRanges as $mediaType => $range) { + if ($lowestLevel > $range[0] && $lowestLevel <= $range[1]) { + $newContentType = $mediaType; + + break; + } + } + + // Put any grandchildren in a subpart + if (!empty($grandchildren)) { + $subentity = $this->_createChild(); + $subentity->_setNestingLevel($lowestLevel); + $subentity->setChildren($grandchildren, $compoundLevel); + array_unshift($immediateChildren, $subentity); + } + } + + $this->_immediateChildren = $immediateChildren; + $this->_children = $children; + $this->_setContentTypeInHeaders($newContentType); + $this->_fixHeaders(); + $this->_sortChildren(); + + return $this; + } + + /** + * Get the body of this entity as a string. + * + * @return string + */ + public function getBody() + { + return $this->_body instanceof Swift_OutputByteStream ? $this->_readStream($this->_body) : $this->_body; + } + + /** + * Set the body of this entity, either as a string, or as an instance of + * {@link Swift_OutputByteStream}. + * + * @param mixed $body + * @param string $contentType optional + * + * @return $this + */ + public function setBody($body, $contentType = null) + { + if ($body !== $this->_body) { + $this->_clearCache(); + } + + $this->_body = $body; + if (isset($contentType)) { + $this->setContentType($contentType); + } + + return $this; + } + + /** + * Get the encoder used for the body of this entity. + * + * @return Swift_Mime_ContentEncoder + */ + public function getEncoder() + { + return $this->_encoder; + } + + /** + * Set the encoder used for the body of this entity. + * + * @param Swift_Mime_ContentEncoder $encoder + * + * @return $this + */ + public function setEncoder(Swift_Mime_ContentEncoder $encoder) + { + if ($encoder !== $this->_encoder) { + $this->_clearCache(); + } + + $this->_encoder = $encoder; + $this->_setEncoding($encoder->getName()); + $this->_notifyEncoderChanged($encoder); + + return $this; + } + + /** + * Get the boundary used to separate children in this entity. + * + * @return string + */ + public function getBoundary() + { + if (!isset($this->_boundary)) { + $this->_boundary = '_=_swift_v4_'.time().'_'.md5(getmypid().mt_rand().uniqid('', true)).'_=_'; + } + + return $this->_boundary; + } + + /** + * Set the boundary used to separate children in this entity. + * + * @param string $boundary + * + * @throws Swift_RfcComplianceException + * + * @return $this + */ + public function setBoundary($boundary) + { + $this->_assertValidBoundary($boundary); + $this->_boundary = $boundary; + + return $this; + } + + /** + * Receive notification that the charset of this entity, or a parent entity + * has changed. + * + * @param string $charset + */ + public function charsetChanged($charset) + { + $this->_notifyCharsetChanged($charset); + } + + /** + * Receive notification that the encoder of this entity or a parent entity + * has changed. + * + * @param Swift_Mime_ContentEncoder $encoder + */ + public function encoderChanged(Swift_Mime_ContentEncoder $encoder) + { + $this->_notifyEncoderChanged($encoder); + } + + /** + * Get this entire entity as a string. + * + * @return string + */ + public function toString() + { + $string = $this->_headers->toString(); + $string .= $this->_bodyToString(); + + return $string; + } + + /** + * Get this entire entity as a string. + * + * @return string + */ + protected function _bodyToString() + { + $string = ''; + + if (isset($this->_body) && empty($this->_immediateChildren)) { + if ($this->_cache->hasKey($this->_cacheKey, 'body')) { + $body = $this->_cache->getString($this->_cacheKey, 'body'); + } else { + $body = "\r\n".$this->_encoder->encodeString($this->getBody(), 0, $this->getMaxLineLength()); + $this->_cache->setString($this->_cacheKey, 'body', $body, Swift_KeyCache::MODE_WRITE); + } + $string .= $body; + } + + if (!empty($this->_immediateChildren)) { + foreach ($this->_immediateChildren as $child) { + $string .= "\r\n\r\n--".$this->getBoundary()."\r\n"; + $string .= $child->toString(); + } + $string .= "\r\n\r\n--".$this->getBoundary()."--\r\n"; + } + + return $string; + } + + /** + * Returns a string representation of this object. + * + * @see toString() + * + * @return string + */ + public function __toString() + { + return $this->toString(); + } + + /** + * Write this entire entity to a {@see Swift_InputByteStream}. + * + * @param Swift_InputByteStream + */ + public function toByteStream(Swift_InputByteStream $is) + { + $is->write($this->_headers->toString()); + $is->commit(); + + $this->_bodyToByteStream($is); + } + + /** + * Write this entire entity to a {@link Swift_InputByteStream}. + * + * @param Swift_InputByteStream + */ + protected function _bodyToByteStream(Swift_InputByteStream $is) + { + if (empty($this->_immediateChildren)) { + if (isset($this->_body)) { + if ($this->_cache->hasKey($this->_cacheKey, 'body')) { + $this->_cache->exportToByteStream($this->_cacheKey, 'body', $is); + } else { + $cacheIs = $this->_cache->getInputByteStream($this->_cacheKey, 'body'); + if ($cacheIs) { + $is->bind($cacheIs); + } + + $is->write("\r\n"); + + if ($this->_body instanceof Swift_OutputByteStream) { + $this->_body->setReadPointer(0); + + $this->_encoder->encodeByteStream($this->_body, $is, 0, $this->getMaxLineLength()); + } else { + $is->write($this->_encoder->encodeString($this->getBody(), 0, $this->getMaxLineLength())); + } + + if ($cacheIs) { + $is->unbind($cacheIs); + } + } + } + } + + if (!empty($this->_immediateChildren)) { + foreach ($this->_immediateChildren as $child) { + $is->write("\r\n\r\n--".$this->getBoundary()."\r\n"); + $child->toByteStream($is); + } + $is->write("\r\n\r\n--".$this->getBoundary()."--\r\n"); + } + } + + /** + * Get the name of the header that provides the ID of this entity. + */ + protected function _getIdField() + { + return 'Content-ID'; + } + + /** + * Get the model data (usually an array or a string) for $field. + */ + protected function _getHeaderFieldModel($field) + { + if ($this->_headers->has($field)) { + return $this->_headers->get($field)->getFieldBodyModel(); + } + } + + /** + * Set the model data for $field. + */ + protected function _setHeaderFieldModel($field, $model) + { + if ($this->_headers->has($field)) { + $this->_headers->get($field)->setFieldBodyModel($model); + + return true; + } + + return false; + } + + /** + * Get the parameter value of $parameter on $field header. + */ + protected function _getHeaderParameter($field, $parameter) + { + if ($this->_headers->has($field)) { + return $this->_headers->get($field)->getParameter($parameter); + } + } + + /** + * Set the parameter value of $parameter on $field header. + */ + protected function _setHeaderParameter($field, $parameter, $value) + { + if ($this->_headers->has($field)) { + $this->_headers->get($field)->setParameter($parameter, $value); + + return true; + } + + return false; + } + + /** + * Re-evaluate what content type and encoding should be used on this entity. + */ + protected function _fixHeaders() + { + if (count($this->_immediateChildren)) { + $this->_setHeaderParameter('Content-Type', 'boundary', + $this->getBoundary() + ); + $this->_headers->remove('Content-Transfer-Encoding'); + } else { + $this->_setHeaderParameter('Content-Type', 'boundary', null); + $this->_setEncoding($this->_encoder->getName()); + } + } + + /** + * Get the KeyCache used in this entity. + * + * @return Swift_KeyCache + */ + protected function _getCache() + { + return $this->_cache; + } + + /** + * Get the grammar used for validation. + * + * @return Swift_Mime_Grammar + */ + protected function _getGrammar() + { + return $this->_grammar; + } + + /** + * Empty the KeyCache for this entity. + */ + protected function _clearCache() + { + $this->_cache->clearKey($this->_cacheKey, 'body'); + } + + /** + * Returns a random Content-ID or Message-ID. + * + * @return string + */ + protected function getRandomId() + { + $idLeft = md5(getmypid().'.'.time().'.'.uniqid(mt_rand(), true)); + $idRight = !empty($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : 'swift.generated'; + $id = $idLeft.'@'.$idRight; + + try { + $this->_assertValidId($id); + } catch (Swift_RfcComplianceException $e) { + $id = $idLeft.'@swift.generated'; + } + + return $id; + } + + private function _readStream(Swift_OutputByteStream $os) + { + $string = ''; + while (false !== $bytes = $os->read(8192)) { + $string .= $bytes; + } + + $os->setReadPointer(0); + + return $string; + } + + private function _setEncoding($encoding) + { + if (!$this->_setHeaderFieldModel('Content-Transfer-Encoding', $encoding)) { + $this->_headers->addTextHeader('Content-Transfer-Encoding', $encoding); + } + } + + private function _assertValidBoundary($boundary) + { + if (!preg_match('/^[a-z0-9\'\(\)\+_\-,\.\/:=\?\ ]{0,69}[a-z0-9\'\(\)\+_\-,\.\/:=\?]$/Di', $boundary)) { + throw new Swift_RfcComplianceException('Mime boundary set is not RFC 2046 compliant.'); + } + } + + private function _setContentTypeInHeaders($type) + { + if (!$this->_setHeaderFieldModel('Content-Type', $type)) { + $this->_headers->addParameterizedHeader('Content-Type', $type); + } + } + + private function _setNestingLevel($level) + { + $this->_nestingLevel = $level; + } + + private function _getCompoundLevel($children) + { + $level = 0; + foreach ($children as $child) { + $level |= $child->getNestingLevel(); + } + + return $level; + } + + private function _getNeededChildLevel($child, $compoundLevel) + { + $filter = array(); + foreach ($this->_compoundLevelFilters as $bitmask => $rules) { + if (($compoundLevel & $bitmask) === $bitmask) { + $filter = $rules + $filter; + } + } + + $realLevel = $child->getNestingLevel(); + $lowercaseType = strtolower($child->getContentType()); + + if (isset($filter[$realLevel]) && isset($filter[$realLevel][$lowercaseType])) { + return $filter[$realLevel][$lowercaseType]; + } + + return $realLevel; + } + + private function _createChild() + { + return new self($this->_headers->newInstance(), $this->_encoder, $this->_cache, $this->_grammar); + } + + private function _notifyEncoderChanged(Swift_Mime_ContentEncoder $encoder) + { + foreach ($this->_immediateChildren as $child) { + $child->encoderChanged($encoder); + } + } + + private function _notifyCharsetChanged($charset) + { + $this->_encoder->charsetChanged($charset); + $this->_headers->charsetChanged($charset); + foreach ($this->_immediateChildren as $child) { + $child->charsetChanged($charset); + } + } + + private function _sortChildren() + { + $shouldSort = false; + foreach ($this->_immediateChildren as $child) { + // NOTE: This include alternative parts moved into a related part + if ($child->getNestingLevel() == self::LEVEL_ALTERNATIVE) { + $shouldSort = true; + break; + } + } + + // Sort in order of preference, if there is one + if ($shouldSort) { + usort($this->_immediateChildren, array($this, '_childSortAlgorithm')); + } + } + + private function _childSortAlgorithm($a, $b) + { + $typePrefs = array(); + $types = array(strtolower($a->getContentType()), strtolower($b->getContentType())); + + foreach ($types as $type) { + $typePrefs[] = array_key_exists($type, $this->_alternativePartOrder) ? $this->_alternativePartOrder[$type] : max($this->_alternativePartOrder) + 1; + } + + return $typePrefs[0] >= $typePrefs[1] ? 1 : -1; + } + + // -- Destructor + + /** + * Empties it's own contents from the cache. + */ + public function __destruct() + { + $this->_cache->clearAll($this->_cacheKey); + } + + /** + * Throws an Exception if the id passed does not comply with RFC 2822. + * + * @param string $id + * + * @throws Swift_RfcComplianceException + */ + private function _assertValidId($id) + { + if (!preg_match('/^'.$this->_grammar->getDefinition('id-left').'@'.$this->_grammar->getDefinition('id-right').'$/D', $id)) { + throw new Swift_RfcComplianceException('Invalid ID given <'.$id.'>'); + } + } + + /** + * Make a deep copy of object. + */ + public function __clone() + { + $this->_headers = clone $this->_headers; + $this->_encoder = clone $this->_encoder; + $this->_cacheKey = md5(uniqid(getmypid().mt_rand(), true)); + $children = array(); + foreach ($this->_children as $pos => $child) { + $children[$pos] = clone $child; + } + $this->setChildren($children); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MimePart.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MimePart.php new file mode 100644 index 0000000000..215f8db348 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MimePart.php @@ -0,0 +1,59 @@ +createDependenciesFor('mime.part') + ); + + if (!isset($charset)) { + $charset = Swift_DependencyContainer::getInstance() + ->lookup('properties.charset'); + } + $this->setBody($body); + $this->setCharset($charset); + if ($contentType) { + $this->setContentType($contentType); + } + } + + /** + * Create a new MimePart. + * + * @param string $body + * @param string $contentType + * @param string $charset + * + * @return Swift_Mime_MimePart + */ + public static function newInstance($body = null, $contentType = null, $charset = null) + { + return new self($body, $contentType, $charset); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/NullTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/NullTransport.php new file mode 100644 index 0000000000..b38e1cf769 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/NullTransport.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Pretends messages have been sent, but just ignores them. + * + * @author Fabien Potencier + */ +class Swift_NullTransport extends Swift_Transport_NullTransport +{ + /** + * Create a new NullTransport. + */ + public function __construct() + { + call_user_func_array( + array($this, 'Swift_Transport_NullTransport::__construct'), + Swift_DependencyContainer::getInstance() + ->createDependenciesFor('transport.null') + ); + } + + /** + * Create a new NullTransport instance. + * + * @return Swift_NullTransport + */ + public static function newInstance() + { + return new self(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/OutputByteStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/OutputByteStream.php new file mode 100644 index 0000000000..1f26f9be52 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/OutputByteStream.php @@ -0,0 +1,46 @@ +setThreshold($threshold); + $this->setSleepTime($sleep); + $this->_sleeper = $sleeper; + } + + /** + * Set the number of emails to send before restarting. + * + * @param int $threshold + */ + public function setThreshold($threshold) + { + $this->_threshold = $threshold; + } + + /** + * Get the number of emails to send before restarting. + * + * @return int + */ + public function getThreshold() + { + return $this->_threshold; + } + + /** + * Set the number of seconds to sleep for during a restart. + * + * @param int $sleep time + */ + public function setSleepTime($sleep) + { + $this->_sleep = $sleep; + } + + /** + * Get the number of seconds to sleep for during a restart. + * + * @return int + */ + public function getSleepTime() + { + return $this->_sleep; + } + + /** + * Invoked immediately before the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function beforeSendPerformed(Swift_Events_SendEvent $evt) + { + } + + /** + * Invoked immediately after the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function sendPerformed(Swift_Events_SendEvent $evt) + { + ++$this->_counter; + if ($this->_counter >= $this->_threshold) { + $transport = $evt->getTransport(); + $transport->stop(); + if ($this->_sleep) { + $this->sleep($this->_sleep); + } + $transport->start(); + $this->_counter = 0; + } + } + + /** + * Sleep for $seconds. + * + * @param int $seconds + */ + public function sleep($seconds) + { + if (isset($this->_sleeper)) { + $this->_sleeper->sleep($seconds); + } else { + sleep($seconds); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/BandwidthMonitorPlugin.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/BandwidthMonitorPlugin.php new file mode 100644 index 0000000000..f7e18d0ebe --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/BandwidthMonitorPlugin.php @@ -0,0 +1,164 @@ +getMessage(); + $message->toByteStream($this); + } + + /** + * Invoked immediately following a command being sent. + * + * @param Swift_Events_CommandEvent $evt + */ + public function commandSent(Swift_Events_CommandEvent $evt) + { + $command = $evt->getCommand(); + $this->_out += strlen($command); + } + + /** + * Invoked immediately following a response coming back. + * + * @param Swift_Events_ResponseEvent $evt + */ + public function responseReceived(Swift_Events_ResponseEvent $evt) + { + $response = $evt->getResponse(); + $this->_in += strlen($response); + } + + /** + * Called when a message is sent so that the outgoing counter can be increased. + * + * @param string $bytes + */ + public function write($bytes) + { + $this->_out += strlen($bytes); + foreach ($this->_mirrors as $stream) { + $stream->write($bytes); + } + } + + /** + * Not used. + */ + public function commit() + { + } + + /** + * Attach $is to this stream. + * + * The stream acts as an observer, receiving all data that is written. + * All {@link write()} and {@link flushBuffers()} operations will be mirrored. + * + * @param Swift_InputByteStream $is + */ + public function bind(Swift_InputByteStream $is) + { + $this->_mirrors[] = $is; + } + + /** + * Remove an already bound stream. + * + * If $is is not bound, no errors will be raised. + * If the stream currently has any buffered data it will be written to $is + * before unbinding occurs. + * + * @param Swift_InputByteStream $is + */ + public function unbind(Swift_InputByteStream $is) + { + foreach ($this->_mirrors as $k => $stream) { + if ($is === $stream) { + unset($this->_mirrors[$k]); + } + } + } + + /** + * Not used. + */ + public function flushBuffers() + { + foreach ($this->_mirrors as $stream) { + $stream->flushBuffers(); + } + } + + /** + * Get the total number of bytes sent to the server. + * + * @return int + */ + public function getBytesOut() + { + return $this->_out; + } + + /** + * Get the total number of bytes received from the server. + * + * @return int + */ + public function getBytesIn() + { + return $this->_in; + } + + /** + * Reset the internal counters to zero. + */ + public function reset() + { + $this->_out = 0; + $this->_in = 0; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Decorator/Replacements.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Decorator/Replacements.php new file mode 100644 index 0000000000..9f9f08b567 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Decorator/Replacements.php @@ -0,0 +1,31 @@ + + * $replacements = array( + * "address1@domain.tld" => array("{a}" => "b", "{c}" => "d"), + * "address2@domain.tld" => array("{a}" => "x", "{c}" => "y") + * ) + * + * + * When using an instance of {@link Swift_Plugins_Decorator_Replacements}, + * the object should return just the array of replacements for the address + * given to {@link Swift_Plugins_Decorator_Replacements::getReplacementsFor()}. + * + * @param mixed $replacements Array or Swift_Plugins_Decorator_Replacements + */ + public function __construct($replacements) + { + $this->setReplacements($replacements); + } + + /** + * Sets replacements. + * + * @param mixed $replacements Array or Swift_Plugins_Decorator_Replacements + * + * @see __construct() + */ + public function setReplacements($replacements) + { + if (!($replacements instanceof Swift_Plugins_Decorator_Replacements)) { + $this->_replacements = (array) $replacements; + } else { + $this->_replacements = $replacements; + } + } + + /** + * Invoked immediately before the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function beforeSendPerformed(Swift_Events_SendEvent $evt) + { + $message = $evt->getMessage(); + $this->_restoreMessage($message); + $to = array_keys($message->getTo()); + $address = array_shift($to); + if ($replacements = $this->getReplacementsFor($address)) { + $body = $message->getBody(); + $search = array_keys($replacements); + $replace = array_values($replacements); + $bodyReplaced = str_replace( + $search, $replace, $body + ); + if ($body != $bodyReplaced) { + $this->_originalBody = $body; + $message->setBody($bodyReplaced); + } + + foreach ($message->getHeaders()->getAll() as $header) { + $body = $header->getFieldBodyModel(); + $count = 0; + if (is_array($body)) { + $bodyReplaced = array(); + foreach ($body as $key => $value) { + $count1 = 0; + $count2 = 0; + $key = is_string($key) ? str_replace($search, $replace, $key, $count1) : $key; + $value = is_string($value) ? str_replace($search, $replace, $value, $count2) : $value; + $bodyReplaced[$key] = $value; + + if (!$count && ($count1 || $count2)) { + $count = 1; + } + } + } else { + $bodyReplaced = str_replace($search, $replace, $body, $count); + } + + if ($count) { + $this->_originalHeaders[$header->getFieldName()] = $body; + $header->setFieldBodyModel($bodyReplaced); + } + } + + $children = (array) $message->getChildren(); + foreach ($children as $child) { + list($type) = sscanf($child->getContentType(), '%[^/]/%s'); + if ('text' == $type) { + $body = $child->getBody(); + $bodyReplaced = str_replace( + $search, $replace, $body + ); + if ($body != $bodyReplaced) { + $child->setBody($bodyReplaced); + $this->_originalChildBodies[$child->getId()] = $body; + } + } + } + $this->_lastMessage = $message; + } + } + + /** + * Find a map of replacements for the address. + * + * If this plugin was provided with a delegate instance of + * {@link Swift_Plugins_Decorator_Replacements} then the call will be + * delegated to it. Otherwise, it will attempt to find the replacements + * from the array provided in the constructor. + * + * If no replacements can be found, an empty value (NULL) is returned. + * + * @param string $address + * + * @return array + */ + public function getReplacementsFor($address) + { + if ($this->_replacements instanceof Swift_Plugins_Decorator_Replacements) { + return $this->_replacements->getReplacementsFor($address); + } + + return isset($this->_replacements[$address]) ? $this->_replacements[$address] : null; + } + + /** + * Invoked immediately after the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function sendPerformed(Swift_Events_SendEvent $evt) + { + $this->_restoreMessage($evt->getMessage()); + } + + /** Restore a changed message back to its original state */ + private function _restoreMessage(Swift_Mime_Message $message) + { + if ($this->_lastMessage === $message) { + if (isset($this->_originalBody)) { + $message->setBody($this->_originalBody); + $this->_originalBody = null; + } + if (!empty($this->_originalHeaders)) { + foreach ($message->getHeaders()->getAll() as $header) { + if (array_key_exists($header->getFieldName(), $this->_originalHeaders)) { + $header->setFieldBodyModel($this->_originalHeaders[$header->getFieldName()]); + } + } + $this->_originalHeaders = array(); + } + if (!empty($this->_originalChildBodies)) { + $children = (array) $message->getChildren(); + foreach ($children as $child) { + $id = $child->getId(); + if (array_key_exists($id, $this->_originalChildBodies)) { + $child->setBody($this->_originalChildBodies[$id]); + } + } + $this->_originalChildBodies = array(); + } + $this->_lastMessage = null; + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/ImpersonatePlugin.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/ImpersonatePlugin.php new file mode 100644 index 0000000000..7552b67a2f --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/ImpersonatePlugin.php @@ -0,0 +1,69 @@ +_sender = $sender; + } + + /** + * Invoked immediately before the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function beforeSendPerformed(Swift_Events_SendEvent $evt) + { + $message = $evt->getMessage(); + $headers = $message->getHeaders(); + + // save current recipients + $headers->addPathHeader('X-Swift-Return-Path', $message->getReturnPath()); + + // replace them with the one to send to + $message->setReturnPath($this->_sender); + } + + /** + * Invoked immediately after the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function sendPerformed(Swift_Events_SendEvent $evt) + { + $message = $evt->getMessage(); + + // restore original headers + $headers = $message->getHeaders(); + + if ($headers->has('X-Swift-Return-Path')) { + $message->setReturnPath($headers->get('X-Swift-Return-Path')->getAddress()); + $headers->removeAll('X-Swift-Return-Path'); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Logger.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Logger.php new file mode 100644 index 0000000000..d9bce8935d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Logger.php @@ -0,0 +1,36 @@ +_logger = $logger; + } + + /** + * Add a log entry. + * + * @param string $entry + */ + public function add($entry) + { + $this->_logger->add($entry); + } + + /** + * Clear the log contents. + */ + public function clear() + { + $this->_logger->clear(); + } + + /** + * Get this log as a string. + * + * @return string + */ + public function dump() + { + return $this->_logger->dump(); + } + + /** + * Invoked immediately following a command being sent. + * + * @param Swift_Events_CommandEvent $evt + */ + public function commandSent(Swift_Events_CommandEvent $evt) + { + $command = $evt->getCommand(); + $this->_logger->add(sprintf('>> %s', $command)); + } + + /** + * Invoked immediately following a response coming back. + * + * @param Swift_Events_ResponseEvent $evt + */ + public function responseReceived(Swift_Events_ResponseEvent $evt) + { + $response = $evt->getResponse(); + $this->_logger->add(sprintf('<< %s', $response)); + } + + /** + * Invoked just before a Transport is started. + * + * @param Swift_Events_TransportChangeEvent $evt + */ + public function beforeTransportStarted(Swift_Events_TransportChangeEvent $evt) + { + $transportName = get_class($evt->getSource()); + $this->_logger->add(sprintf('++ Starting %s', $transportName)); + } + + /** + * Invoked immediately after the Transport is started. + * + * @param Swift_Events_TransportChangeEvent $evt + */ + public function transportStarted(Swift_Events_TransportChangeEvent $evt) + { + $transportName = get_class($evt->getSource()); + $this->_logger->add(sprintf('++ %s started', $transportName)); + } + + /** + * Invoked just before a Transport is stopped. + * + * @param Swift_Events_TransportChangeEvent $evt + */ + public function beforeTransportStopped(Swift_Events_TransportChangeEvent $evt) + { + $transportName = get_class($evt->getSource()); + $this->_logger->add(sprintf('++ Stopping %s', $transportName)); + } + + /** + * Invoked immediately after the Transport is stopped. + * + * @param Swift_Events_TransportChangeEvent $evt + */ + public function transportStopped(Swift_Events_TransportChangeEvent $evt) + { + $transportName = get_class($evt->getSource()); + $this->_logger->add(sprintf('++ %s stopped', $transportName)); + } + + /** + * Invoked as a TransportException is thrown in the Transport system. + * + * @param Swift_Events_TransportExceptionEvent $evt + */ + public function exceptionThrown(Swift_Events_TransportExceptionEvent $evt) + { + $e = $evt->getException(); + $message = $e->getMessage(); + $code = $e->getCode(); + $this->_logger->add(sprintf('!! %s (code: %s)', $message, $code)); + $message .= PHP_EOL; + $message .= 'Log data:'.PHP_EOL; + $message .= $this->_logger->dump(); + $evt->cancelBubble(); + throw new Swift_TransportException($message, $code, $e->getPrevious()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Loggers/ArrayLogger.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Loggers/ArrayLogger.php new file mode 100644 index 0000000000..865bb0aa3b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Loggers/ArrayLogger.php @@ -0,0 +1,72 @@ +_size = $size; + } + + /** + * Add a log entry. + * + * @param string $entry + */ + public function add($entry) + { + $this->_log[] = $entry; + while (count($this->_log) > $this->_size) { + array_shift($this->_log); + } + } + + /** + * Clear the log contents. + */ + public function clear() + { + $this->_log = array(); + } + + /** + * Get this log as a string. + * + * @return string + */ + public function dump() + { + return implode(PHP_EOL, $this->_log); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Loggers/EchoLogger.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Loggers/EchoLogger.php new file mode 100644 index 0000000000..3583297ab1 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Loggers/EchoLogger.php @@ -0,0 +1,58 @@ +_isHtml = $isHtml; + } + + /** + * Add a log entry. + * + * @param string $entry + */ + public function add($entry) + { + if ($this->_isHtml) { + printf('%s%s%s', htmlspecialchars($entry, ENT_QUOTES), '
', PHP_EOL); + } else { + printf('%s%s', $entry, PHP_EOL); + } + } + + /** + * Not implemented. + */ + public function clear() + { + } + + /** + * Not implemented. + */ + public function dump() + { + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/MessageLogger.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/MessageLogger.php new file mode 100644 index 0000000000..e622cb37d1 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/MessageLogger.php @@ -0,0 +1,74 @@ +messages = array(); + } + + /** + * Get the message list. + * + * @return array + */ + public function getMessages() + { + return $this->messages; + } + + /** + * Get the message count. + * + * @return int count + */ + public function countMessages() + { + return count($this->messages); + } + + /** + * Empty the message list. + */ + public function clear() + { + $this->messages = array(); + } + + /** + * Invoked immediately before the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function beforeSendPerformed(Swift_Events_SendEvent $evt) + { + $this->messages[] = clone $evt->getMessage(); + } + + /** + * Invoked immediately after the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function sendPerformed(Swift_Events_SendEvent $evt) + { + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Pop/Pop3Connection.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Pop/Pop3Connection.php new file mode 100644 index 0000000000..fb99e4c942 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Pop/Pop3Connection.php @@ -0,0 +1,31 @@ +_host = $host; + $this->_port = $port; + $this->_crypto = $crypto; + } + + /** + * Create a new PopBeforeSmtpPlugin for $host and $port. + * + * @param string $host + * @param int $port + * @param string $crypto as "tls" or "ssl" + * + * @return Swift_Plugins_PopBeforeSmtpPlugin + */ + public static function newInstance($host, $port = 110, $crypto = null) + { + return new self($host, $port, $crypto); + } + + /** + * Set a Pop3Connection to delegate to instead of connecting directly. + * + * @param Swift_Plugins_Pop_Pop3Connection $connection + * + * @return Swift_Plugins_PopBeforeSmtpPlugin + */ + public function setConnection(Swift_Plugins_Pop_Pop3Connection $connection) + { + $this->_connection = $connection; + + return $this; + } + + /** + * Bind this plugin to a specific SMTP transport instance. + * + * @param Swift_Transport + */ + public function bindSmtp(Swift_Transport $smtp) + { + $this->_transport = $smtp; + } + + /** + * Set the connection timeout in seconds (default 10). + * + * @param int $timeout + * + * @return Swift_Plugins_PopBeforeSmtpPlugin + */ + public function setTimeout($timeout) + { + $this->_timeout = (int) $timeout; + + return $this; + } + + /** + * Set the username to use when connecting (if needed). + * + * @param string $username + * + * @return Swift_Plugins_PopBeforeSmtpPlugin + */ + public function setUsername($username) + { + $this->_username = $username; + + return $this; + } + + /** + * Set the password to use when connecting (if needed). + * + * @param string $password + * + * @return Swift_Plugins_PopBeforeSmtpPlugin + */ + public function setPassword($password) + { + $this->_password = $password; + + return $this; + } + + /** + * Connect to the POP3 host and authenticate. + * + * @throws Swift_Plugins_Pop_Pop3Exception if connection fails + */ + public function connect() + { + if (isset($this->_connection)) { + $this->_connection->connect(); + } else { + if (!isset($this->_socket)) { + if (!$socket = fsockopen( + $this->_getHostString(), $this->_port, $errno, $errstr, $this->_timeout)) { + throw new Swift_Plugins_Pop_Pop3Exception( + sprintf('Failed to connect to POP3 host [%s]: %s', $this->_host, $errstr) + ); + } + $this->_socket = $socket; + + if (false === $greeting = fgets($this->_socket)) { + throw new Swift_Plugins_Pop_Pop3Exception( + sprintf('Failed to connect to POP3 host [%s]', trim($greeting)) + ); + } + + $this->_assertOk($greeting); + + if ($this->_username) { + $this->_command(sprintf("USER %s\r\n", $this->_username)); + $this->_command(sprintf("PASS %s\r\n", $this->_password)); + } + } + } + } + + /** + * Disconnect from the POP3 host. + */ + public function disconnect() + { + if (isset($this->_connection)) { + $this->_connection->disconnect(); + } else { + $this->_command("QUIT\r\n"); + if (!fclose($this->_socket)) { + throw new Swift_Plugins_Pop_Pop3Exception( + sprintf('POP3 host [%s] connection could not be stopped', $this->_host) + ); + } + $this->_socket = null; + } + } + + /** + * Invoked just before a Transport is started. + * + * @param Swift_Events_TransportChangeEvent $evt + */ + public function beforeTransportStarted(Swift_Events_TransportChangeEvent $evt) + { + if (isset($this->_transport)) { + if ($this->_transport !== $evt->getTransport()) { + return; + } + } + + $this->connect(); + $this->disconnect(); + } + + /** + * Not used. + */ + public function transportStarted(Swift_Events_TransportChangeEvent $evt) + { + } + + /** + * Not used. + */ + public function beforeTransportStopped(Swift_Events_TransportChangeEvent $evt) + { + } + + /** + * Not used. + */ + public function transportStopped(Swift_Events_TransportChangeEvent $evt) + { + } + + private function _command($command) + { + if (!fwrite($this->_socket, $command)) { + throw new Swift_Plugins_Pop_Pop3Exception( + sprintf('Failed to write command [%s] to POP3 host', trim($command)) + ); + } + + if (false === $response = fgets($this->_socket)) { + throw new Swift_Plugins_Pop_Pop3Exception( + sprintf('Failed to read from POP3 host after command [%s]', trim($command)) + ); + } + + $this->_assertOk($response); + + return $response; + } + + private function _assertOk($response) + { + if (substr($response, 0, 3) != '+OK') { + throw new Swift_Plugins_Pop_Pop3Exception( + sprintf('POP3 command failed [%s]', trim($response)) + ); + } + } + + private function _getHostString() + { + $host = $this->_host; + switch (strtolower($this->_crypto)) { + case 'ssl': + $host = 'ssl://'.$host; + break; + + case 'tls': + $host = 'tls://'.$host; + break; + } + + return $host; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/RedirectingPlugin.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/RedirectingPlugin.php new file mode 100644 index 0000000000..c3a1f86854 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/RedirectingPlugin.php @@ -0,0 +1,213 @@ +_recipient = $recipient; + $this->_whitelist = $whitelist; + } + + /** + * Set the recipient of all messages. + * + * @param mixed $recipient + */ + public function setRecipient($recipient) + { + $this->_recipient = $recipient; + } + + /** + * Get the recipient of all messages. + * + * @return mixed + */ + public function getRecipient() + { + return $this->_recipient; + } + + /** + * Set a list of regular expressions to whitelist certain recipients. + * + * @param array $whitelist + */ + public function setWhitelist(array $whitelist) + { + $this->_whitelist = $whitelist; + } + + /** + * Get the whitelist. + * + * @return array + */ + public function getWhitelist() + { + return $this->_whitelist; + } + + /** + * Invoked immediately before the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function beforeSendPerformed(Swift_Events_SendEvent $evt) + { + $message = $evt->getMessage(); + $headers = $message->getHeaders(); + + // conditionally save current recipients + + if ($headers->has('to')) { + $headers->addMailboxHeader('X-Swift-To', $message->getTo()); + } + + if ($headers->has('cc')) { + $headers->addMailboxHeader('X-Swift-Cc', $message->getCc()); + } + + if ($headers->has('bcc')) { + $headers->addMailboxHeader('X-Swift-Bcc', $message->getBcc()); + } + + // Filter remaining headers against whitelist + $this->_filterHeaderSet($headers, 'To'); + $this->_filterHeaderSet($headers, 'Cc'); + $this->_filterHeaderSet($headers, 'Bcc'); + + // Add each hard coded recipient + $to = $message->getTo(); + if (null === $to) { + $to = array(); + } + + foreach ((array) $this->_recipient as $recipient) { + if (!array_key_exists($recipient, $to)) { + $message->addTo($recipient); + } + } + } + + /** + * Filter header set against a whitelist of regular expressions. + * + * @param Swift_Mime_HeaderSet $headerSet + * @param string $type + */ + private function _filterHeaderSet(Swift_Mime_HeaderSet $headerSet, $type) + { + foreach ($headerSet->getAll($type) as $headers) { + $headers->setNameAddresses($this->_filterNameAddresses($headers->getNameAddresses())); + } + } + + /** + * Filtered list of addresses => name pairs. + * + * @param array $recipients + * + * @return array + */ + private function _filterNameAddresses(array $recipients) + { + $filtered = array(); + + foreach ($recipients as $address => $name) { + if ($this->_isWhitelisted($address)) { + $filtered[$address] = $name; + } + } + + return $filtered; + } + + /** + * Matches address against whitelist of regular expressions. + * + * @param $recipient + * + * @return bool + */ + protected function _isWhitelisted($recipient) + { + if (in_array($recipient, (array) $this->_recipient)) { + return true; + } + + foreach ($this->_whitelist as $pattern) { + if (preg_match($pattern, $recipient)) { + return true; + } + } + + return false; + } + + /** + * Invoked immediately after the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function sendPerformed(Swift_Events_SendEvent $evt) + { + $this->_restoreMessage($evt->getMessage()); + } + + private function _restoreMessage(Swift_Mime_Message $message) + { + // restore original headers + $headers = $message->getHeaders(); + + if ($headers->has('X-Swift-To')) { + $message->setTo($headers->get('X-Swift-To')->getNameAddresses()); + $headers->removeAll('X-Swift-To'); + } else { + $message->setTo(null); + } + + if ($headers->has('X-Swift-Cc')) { + $message->setCc($headers->get('X-Swift-Cc')->getNameAddresses()); + $headers->removeAll('X-Swift-Cc'); + } + + if ($headers->has('X-Swift-Bcc')) { + $message->setBcc($headers->get('X-Swift-Bcc')->getNameAddresses()); + $headers->removeAll('X-Swift-Bcc'); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporter.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporter.php new file mode 100644 index 0000000000..0f21b7d605 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporter.php @@ -0,0 +1,32 @@ +_reporter = $reporter; + } + + /** + * Not used. + */ + public function beforeSendPerformed(Swift_Events_SendEvent $evt) + { + } + + /** + * Invoked immediately after the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function sendPerformed(Swift_Events_SendEvent $evt) + { + $message = $evt->getMessage(); + $failures = array_flip($evt->getFailedRecipients()); + foreach ((array) $message->getTo() as $address => $null) { + $this->_reporter->notify($message, $address, array_key_exists($address, $failures) ? Swift_Plugins_Reporter::RESULT_FAIL : Swift_Plugins_Reporter::RESULT_PASS); + } + foreach ((array) $message->getCc() as $address => $null) { + $this->_reporter->notify($message, $address, array_key_exists($address, $failures) ? Swift_Plugins_Reporter::RESULT_FAIL : Swift_Plugins_Reporter::RESULT_PASS); + } + foreach ((array) $message->getBcc() as $address => $null) { + $this->_reporter->notify($message, $address, array_key_exists($address, $failures) ? Swift_Plugins_Reporter::RESULT_FAIL : Swift_Plugins_Reporter::RESULT_PASS); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporters/HitReporter.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporters/HitReporter.php new file mode 100644 index 0000000000..cad9d168ef --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporters/HitReporter.php @@ -0,0 +1,59 @@ +_failures_cache[$address])) { + $this->_failures[] = $address; + $this->_failures_cache[$address] = true; + } + } + + /** + * Get an array of addresses for which delivery failed. + * + * @return array + */ + public function getFailedRecipients() + { + return $this->_failures; + } + + /** + * Clear the buffer (empty the list). + */ + public function clear() + { + $this->_failures = $this->_failures_cache = array(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporters/HtmlReporter.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporters/HtmlReporter.php new file mode 100644 index 0000000000..c62593557e --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporters/HtmlReporter.php @@ -0,0 +1,39 @@ +'.PHP_EOL; + echo 'PASS '.$address.PHP_EOL; + echo ''.PHP_EOL; + flush(); + } else { + echo '
'.PHP_EOL; + echo 'FAIL '.$address.PHP_EOL; + echo '
'.PHP_EOL; + flush(); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Sleeper.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Sleeper.php new file mode 100644 index 0000000000..595c0f603b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Sleeper.php @@ -0,0 +1,24 @@ +_rate = $rate; + $this->_mode = $mode; + $this->_sleeper = $sleeper; + $this->_timer = $timer; + } + + /** + * Invoked immediately before the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function beforeSendPerformed(Swift_Events_SendEvent $evt) + { + $time = $this->getTimestamp(); + if (!isset($this->_start)) { + $this->_start = $time; + } + $duration = $time - $this->_start; + + switch ($this->_mode) { + case self::BYTES_PER_MINUTE: + $sleep = $this->_throttleBytesPerMinute($duration); + break; + case self::MESSAGES_PER_SECOND: + $sleep = $this->_throttleMessagesPerSecond($duration); + break; + case self::MESSAGES_PER_MINUTE: + $sleep = $this->_throttleMessagesPerMinute($duration); + break; + default: + $sleep = 0; + break; + } + + if ($sleep > 0) { + $this->sleep($sleep); + } + } + + /** + * Invoked when a Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function sendPerformed(Swift_Events_SendEvent $evt) + { + parent::sendPerformed($evt); + ++$this->_messages; + } + + /** + * Sleep for $seconds. + * + * @param int $seconds + */ + public function sleep($seconds) + { + if (isset($this->_sleeper)) { + $this->_sleeper->sleep($seconds); + } else { + sleep($seconds); + } + } + + /** + * Get the current UNIX timestamp. + * + * @return int + */ + public function getTimestamp() + { + if (isset($this->_timer)) { + return $this->_timer->getTimestamp(); + } + + return time(); + } + + /** + * Get a number of seconds to sleep for. + * + * @param int $timePassed + * + * @return int + */ + private function _throttleBytesPerMinute($timePassed) + { + $expectedDuration = $this->getBytesOut() / ($this->_rate / 60); + + return (int) ceil($expectedDuration - $timePassed); + } + + /** + * Get a number of seconds to sleep for. + * + * @param int $timePassed + * + * @return int + */ + private function _throttleMessagesPerSecond($timePassed) + { + $expectedDuration = $this->_messages / ($this->_rate); + + return (int) ceil($expectedDuration - $timePassed); + } + + /** + * Get a number of seconds to sleep for. + * + * @param int $timePassed + * + * @return int + */ + private function _throttleMessagesPerMinute($timePassed) + { + $expectedDuration = $this->_messages / ($this->_rate / 60); + + return (int) ceil($expectedDuration - $timePassed); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Timer.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Timer.php new file mode 100644 index 0000000000..9c8deb38ac --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Timer.php @@ -0,0 +1,24 @@ +register('properties.charset')->asValue($charset); + + return $this; + } + + /** + * Set the directory where temporary files can be saved. + * + * @param string $dir + * + * @return Swift_Preferences + */ + public function setTempDir($dir) + { + Swift_DependencyContainer::getInstance() + ->register('tempdir')->asValue($dir); + + return $this; + } + + /** + * Set the type of cache to use (i.e. "disk" or "array"). + * + * @param string $type + * + * @return Swift_Preferences + */ + public function setCacheType($type) + { + Swift_DependencyContainer::getInstance() + ->register('cache')->asAliasOf(sprintf('cache.%s', $type)); + + return $this; + } + + /** + * Set the QuotedPrintable dot escaper preference. + * + * @param bool $dotEscape + * + * @return Swift_Preferences + */ + public function setQPDotEscape($dotEscape) + { + $dotEscape = !empty($dotEscape); + Swift_DependencyContainer::getInstance() + ->register('mime.qpcontentencoder') + ->asNewInstanceOf('Swift_Mime_ContentEncoder_QpContentEncoder') + ->withDependencies(array('mime.charstream', 'mime.bytecanonicalizer')) + ->addConstructorValue($dotEscape); + + return $this; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ReplacementFilterFactory.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ReplacementFilterFactory.php new file mode 100644 index 0000000000..2897474e7b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ReplacementFilterFactory.php @@ -0,0 +1,27 @@ +createDependenciesFor('transport.sendmail') + ); + + $this->setCommand($command); + } + + /** + * Create a new SendmailTransport instance. + * + * @param string $command + * + * @return Swift_SendmailTransport + */ + public static function newInstance($command = '/usr/sbin/sendmail -bs') + { + return new self($command); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SignedMessage.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SignedMessage.php new file mode 100644 index 0000000000..2e7a8726d2 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SignedMessage.php @@ -0,0 +1,23 @@ + + * + * @deprecated + */ +class Swift_SignedMessage extends Swift_Message +{ +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signer.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signer.php new file mode 100644 index 0000000000..2d8176d905 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signer.php @@ -0,0 +1,20 @@ + + */ +interface Swift_Signer +{ + public function reset(); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/BodySigner.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/BodySigner.php new file mode 100644 index 0000000000..9ffcef39c9 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/BodySigner.php @@ -0,0 +1,33 @@ + + */ +interface Swift_Signers_BodySigner extends Swift_Signer +{ + /** + * Change the Swift_Signed_Message to apply the singing. + * + * @param Swift_Message $message + * + * @return Swift_Signers_BodySigner + */ + public function signMessage(Swift_Message $message); + + /** + * Return the list of header a signer might tamper. + * + * @return array + */ + public function getAlteredHeaders(); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/DKIMSigner.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/DKIMSigner.php new file mode 100644 index 0000000000..6ddd4f9280 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/DKIMSigner.php @@ -0,0 +1,698 @@ + + */ +class Swift_Signers_DKIMSigner implements Swift_Signers_HeaderSigner +{ + /** + * PrivateKey. + * + * @var string + */ + protected $_privateKey; + + /** + * DomainName. + * + * @var string + */ + protected $_domainName; + + /** + * Selector. + * + * @var string + */ + protected $_selector; + + /** + * Hash algorithm used. + * + * @var string + */ + protected $_hashAlgorithm = 'rsa-sha1'; + + /** + * Body canon method. + * + * @var string + */ + protected $_bodyCanon = 'simple'; + + /** + * Header canon method. + * + * @var string + */ + protected $_headerCanon = 'simple'; + + /** + * Headers not being signed. + * + * @var array + */ + protected $_ignoredHeaders = array('return-path' => true); + + /** + * Signer identity. + * + * @var string + */ + protected $_signerIdentity; + + /** + * BodyLength. + * + * @var int + */ + protected $_bodyLen = 0; + + /** + * Maximum signedLen. + * + * @var int + */ + protected $_maxLen = PHP_INT_MAX; + + /** + * Embbed bodyLen in signature. + * + * @var bool + */ + protected $_showLen = false; + + /** + * When the signature has been applied (true means time()), false means not embedded. + * + * @var mixed + */ + protected $_signatureTimestamp = true; + + /** + * When will the signature expires false means not embedded, if sigTimestamp is auto + * Expiration is relative, otherwhise it's absolute. + * + * @var int + */ + protected $_signatureExpiration = false; + + /** + * Must we embed signed headers? + * + * @var bool + */ + protected $_debugHeaders = false; + + // work variables + /** + * Headers used to generate hash. + * + * @var array + */ + protected $_signedHeaders = array(); + + /** + * If debugHeaders is set store debugDatas here. + * + * @var string + */ + private $_debugHeadersData = ''; + + /** + * Stores the bodyHash. + * + * @var string + */ + private $_bodyHash = ''; + + /** + * Stores the signature header. + * + * @var Swift_Mime_Headers_ParameterizedHeader + */ + protected $_dkimHeader; + + private $_bodyHashHandler; + + private $_headerHash; + + private $_headerCanonData = ''; + + private $_bodyCanonEmptyCounter = 0; + + private $_bodyCanonIgnoreStart = 2; + + private $_bodyCanonSpace = false; + + private $_bodyCanonLastChar = null; + + private $_bodyCanonLine = ''; + + private $_bound = array(); + + /** + * Constructor. + * + * @param string $privateKey + * @param string $domainName + * @param string $selector + */ + public function __construct($privateKey, $domainName, $selector) + { + $this->_privateKey = $privateKey; + $this->_domainName = $domainName; + $this->_signerIdentity = '@'.$domainName; + $this->_selector = $selector; + } + + /** + * Instanciate DKIMSigner. + * + * @param string $privateKey + * @param string $domainName + * @param string $selector + * + * @return Swift_Signers_DKIMSigner + */ + public static function newInstance($privateKey, $domainName, $selector) + { + return new static($privateKey, $domainName, $selector); + } + + /** + * Reset the Signer. + * + * @see Swift_Signer::reset() + */ + public function reset() + { + $this->_headerHash = null; + $this->_signedHeaders = array(); + $this->_bodyHash = null; + $this->_bodyHashHandler = null; + $this->_bodyCanonIgnoreStart = 2; + $this->_bodyCanonEmptyCounter = 0; + $this->_bodyCanonLastChar = null; + $this->_bodyCanonSpace = false; + } + + /** + * Writes $bytes to the end of the stream. + * + * Writing may not happen immediately if the stream chooses to buffer. If + * you want to write these bytes with immediate effect, call {@link commit()} + * after calling write(). + * + * This method returns the sequence ID of the write (i.e. 1 for first, 2 for + * second, etc etc). + * + * @param string $bytes + * + * @throws Swift_IoException + * + * @return int + */ + public function write($bytes) + { + $this->_canonicalizeBody($bytes); + foreach ($this->_bound as $is) { + $is->write($bytes); + } + } + + /** + * For any bytes that are currently buffered inside the stream, force them + * off the buffer. + * + * @throws Swift_IoException + */ + public function commit() + { + // Nothing to do + return; + } + + /** + * Attach $is to this stream. + * The stream acts as an observer, receiving all data that is written. + * All {@link write()} and {@link flushBuffers()} operations will be mirrored. + * + * @param Swift_InputByteStream $is + */ + public function bind(Swift_InputByteStream $is) + { + // Don't have to mirror anything + $this->_bound[] = $is; + + return; + } + + /** + * Remove an already bound stream. + * If $is is not bound, no errors will be raised. + * If the stream currently has any buffered data it will be written to $is + * before unbinding occurs. + * + * @param Swift_InputByteStream $is + */ + public function unbind(Swift_InputByteStream $is) + { + // Don't have to mirror anything + foreach ($this->_bound as $k => $stream) { + if ($stream === $is) { + unset($this->_bound[$k]); + + return; + } + } + + return; + } + + /** + * Flush the contents of the stream (empty it) and set the internal pointer + * to the beginning. + * + * @throws Swift_IoException + */ + public function flushBuffers() + { + $this->reset(); + } + + /** + * Set hash_algorithm, must be one of rsa-sha256 | rsa-sha1 defaults to rsa-sha256. + * + * @param string $hash + * + * @return Swift_Signers_DKIMSigner + */ + public function setHashAlgorithm($hash) + { + // Unable to sign with rsa-sha256 + if ($hash == 'rsa-sha1') { + $this->_hashAlgorithm = 'rsa-sha1'; + } else { + $this->_hashAlgorithm = 'rsa-sha256'; + } + + return $this; + } + + /** + * Set the body canonicalization algorithm. + * + * @param string $canon + * + * @return Swift_Signers_DKIMSigner + */ + public function setBodyCanon($canon) + { + if ($canon == 'relaxed') { + $this->_bodyCanon = 'relaxed'; + } else { + $this->_bodyCanon = 'simple'; + } + + return $this; + } + + /** + * Set the header canonicalization algorithm. + * + * @param string $canon + * + * @return Swift_Signers_DKIMSigner + */ + public function setHeaderCanon($canon) + { + if ($canon == 'relaxed') { + $this->_headerCanon = 'relaxed'; + } else { + $this->_headerCanon = 'simple'; + } + + return $this; + } + + /** + * Set the signer identity. + * + * @param string $identity + * + * @return Swift_Signers_DKIMSigner + */ + public function setSignerIdentity($identity) + { + $this->_signerIdentity = $identity; + + return $this; + } + + /** + * Set the length of the body to sign. + * + * @param mixed $len (bool or int) + * + * @return Swift_Signers_DKIMSigner + */ + public function setBodySignedLen($len) + { + if ($len === true) { + $this->_showLen = true; + $this->_maxLen = PHP_INT_MAX; + } elseif ($len === false) { + $this->_showLen = false; + $this->_maxLen = PHP_INT_MAX; + } else { + $this->_showLen = true; + $this->_maxLen = (int) $len; + } + + return $this; + } + + /** + * Set the signature timestamp. + * + * @param int $time A timestamp + * + * @return Swift_Signers_DKIMSigner + */ + public function setSignatureTimestamp($time) + { + $this->_signatureTimestamp = $time; + + return $this; + } + + /** + * Set the signature expiration timestamp. + * + * @param int $time A timestamp + * + * @return Swift_Signers_DKIMSigner + */ + public function setSignatureExpiration($time) + { + $this->_signatureExpiration = $time; + + return $this; + } + + /** + * Enable / disable the DebugHeaders. + * + * @param bool $debug + * + * @return Swift_Signers_DKIMSigner + */ + public function setDebugHeaders($debug) + { + $this->_debugHeaders = (bool) $debug; + + return $this; + } + + /** + * Start Body. + */ + public function startBody() + { + // Init + switch ($this->_hashAlgorithm) { + case 'rsa-sha256': + $this->_bodyHashHandler = hash_init('sha256'); + break; + case 'rsa-sha1': + $this->_bodyHashHandler = hash_init('sha1'); + break; + } + $this->_bodyCanonLine = ''; + } + + /** + * End Body. + */ + public function endBody() + { + $this->_endOfBody(); + } + + /** + * Returns the list of Headers Tampered by this plugin. + * + * @return array + */ + public function getAlteredHeaders() + { + if ($this->_debugHeaders) { + return array('DKIM-Signature', 'X-DebugHash'); + } else { + return array('DKIM-Signature'); + } + } + + /** + * Adds an ignored Header. + * + * @param string $header_name + * + * @return Swift_Signers_DKIMSigner + */ + public function ignoreHeader($header_name) + { + $this->_ignoredHeaders[strtolower($header_name)] = true; + + return $this; + } + + /** + * Set the headers to sign. + * + * @param Swift_Mime_HeaderSet $headers + * + * @return Swift_Signers_DKIMSigner + */ + public function setHeaders(Swift_Mime_HeaderSet $headers) + { + $this->_headerCanonData = ''; + // Loop through Headers + $listHeaders = $headers->listAll(); + foreach ($listHeaders as $hName) { + // Check if we need to ignore Header + if (!isset($this->_ignoredHeaders[strtolower($hName)])) { + if ($headers->has($hName)) { + $tmp = $headers->getAll($hName); + foreach ($tmp as $header) { + if ($header->getFieldBody() != '') { + $this->_addHeader($header->toString()); + $this->_signedHeaders[] = $header->getFieldName(); + } + } + } + } + } + + return $this; + } + + /** + * Add the signature to the given Headers. + * + * @param Swift_Mime_HeaderSet $headers + * + * @return Swift_Signers_DKIMSigner + */ + public function addSignature(Swift_Mime_HeaderSet $headers) + { + // Prepare the DKIM-Signature + $params = array('v' => '1', 'a' => $this->_hashAlgorithm, 'bh' => base64_encode($this->_bodyHash), 'd' => $this->_domainName, 'h' => implode(': ', $this->_signedHeaders), 'i' => $this->_signerIdentity, 's' => $this->_selector); + if ($this->_bodyCanon != 'simple') { + $params['c'] = $this->_headerCanon.'/'.$this->_bodyCanon; + } elseif ($this->_headerCanon != 'simple') { + $params['c'] = $this->_headerCanon; + } + if ($this->_showLen) { + $params['l'] = $this->_bodyLen; + } + if ($this->_signatureTimestamp === true) { + $params['t'] = time(); + if ($this->_signatureExpiration !== false) { + $params['x'] = $params['t'] + $this->_signatureExpiration; + } + } else { + if ($this->_signatureTimestamp !== false) { + $params['t'] = $this->_signatureTimestamp; + } + if ($this->_signatureExpiration !== false) { + $params['x'] = $this->_signatureExpiration; + } + } + if ($this->_debugHeaders) { + $params['z'] = implode('|', $this->_debugHeadersData); + } + $string = ''; + foreach ($params as $k => $v) { + $string .= $k.'='.$v.'; '; + } + $string = trim($string); + $headers->addTextHeader('DKIM-Signature', $string); + // Add the last DKIM-Signature + $tmp = $headers->getAll('DKIM-Signature'); + $this->_dkimHeader = end($tmp); + $this->_addHeader(trim($this->_dkimHeader->toString())."\r\n b=", true); + $this->_endOfHeaders(); + if ($this->_debugHeaders) { + $headers->addTextHeader('X-DebugHash', base64_encode($this->_headerHash)); + } + $this->_dkimHeader->setValue($string.' b='.trim(chunk_split(base64_encode($this->_getEncryptedHash()), 73, ' '))); + + return $this; + } + + /* Private helpers */ + + protected function _addHeader($header, $is_sig = false) + { + switch ($this->_headerCanon) { + case 'relaxed': + // Prepare Header and cascade + $exploded = explode(':', $header, 2); + $name = strtolower(trim($exploded[0])); + $value = str_replace("\r\n", '', $exploded[1]); + $value = preg_replace("/[ \t][ \t]+/", ' ', $value); + $header = $name.':'.trim($value).($is_sig ? '' : "\r\n"); + case 'simple': + // Nothing to do + } + $this->_addToHeaderHash($header); + } + + /** + * @deprecated This method is currently useless in this class but it must be + * kept for BC reasons due to its "protected" scope. This method + * might be overriden by custom client code. + */ + protected function _endOfHeaders() + { + } + + protected function _canonicalizeBody($string) + { + $len = strlen($string); + $canon = ''; + $method = ($this->_bodyCanon == 'relaxed'); + for ($i = 0; $i < $len; ++$i) { + if ($this->_bodyCanonIgnoreStart > 0) { + --$this->_bodyCanonIgnoreStart; + continue; + } + switch ($string[$i]) { + case "\r": + $this->_bodyCanonLastChar = "\r"; + break; + case "\n": + if ($this->_bodyCanonLastChar == "\r") { + if ($method) { + $this->_bodyCanonSpace = false; + } + if ($this->_bodyCanonLine == '') { + ++$this->_bodyCanonEmptyCounter; + } else { + $this->_bodyCanonLine = ''; + $canon .= "\r\n"; + } + } else { + // Wooops Error + // todo handle it but should never happen + } + break; + case ' ': + case "\t": + if ($method) { + $this->_bodyCanonSpace = true; + break; + } + default: + if ($this->_bodyCanonEmptyCounter > 0) { + $canon .= str_repeat("\r\n", $this->_bodyCanonEmptyCounter); + $this->_bodyCanonEmptyCounter = 0; + } + if ($this->_bodyCanonSpace) { + $this->_bodyCanonLine .= ' '; + $canon .= ' '; + $this->_bodyCanonSpace = false; + } + $this->_bodyCanonLine .= $string[$i]; + $canon .= $string[$i]; + } + } + $this->_addToBodyHash($canon); + } + + protected function _endOfBody() + { + // Add trailing Line return if last line is non empty + if (strlen($this->_bodyCanonLine) > 0) { + $this->_addToBodyHash("\r\n"); + } + $this->_bodyHash = hash_final($this->_bodyHashHandler, true); + } + + private function _addToBodyHash($string) + { + $len = strlen($string); + if ($len > ($new_len = ($this->_maxLen - $this->_bodyLen))) { + $string = substr($string, 0, $new_len); + $len = $new_len; + } + hash_update($this->_bodyHashHandler, $string); + $this->_bodyLen += $len; + } + + private function _addToHeaderHash($header) + { + if ($this->_debugHeaders) { + $this->_debugHeadersData[] = trim($header); + } + $this->_headerCanonData .= $header; + } + + /** + * @throws Swift_SwiftException + * + * @return string + */ + private function _getEncryptedHash() + { + $signature = ''; + switch ($this->_hashAlgorithm) { + case 'rsa-sha1': + $algorithm = OPENSSL_ALGO_SHA1; + break; + case 'rsa-sha256': + $algorithm = OPENSSL_ALGO_SHA256; + break; + } + $pkeyId = openssl_get_privatekey($this->_privateKey); + if (!$pkeyId) { + throw new Swift_SwiftException('Unable to load DKIM Private Key ['.openssl_error_string().']'); + } + if (openssl_sign($this->_headerCanonData, $signature, $pkeyId, $algorithm)) { + return $signature; + } + throw new Swift_SwiftException('Unable to sign DKIM Hash ['.openssl_error_string().']'); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/DomainKeySigner.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/DomainKeySigner.php new file mode 100644 index 0000000000..786cee7b7a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/DomainKeySigner.php @@ -0,0 +1,525 @@ + + */ +class Swift_Signers_DomainKeySigner implements Swift_Signers_HeaderSigner +{ + /** + * PrivateKey. + * + * @var string + */ + protected $_privateKey; + + /** + * DomainName. + * + * @var string + */ + protected $_domainName; + + /** + * Selector. + * + * @var string + */ + protected $_selector; + + /** + * Hash algorithm used. + * + * @var string + */ + protected $_hashAlgorithm = 'rsa-sha1'; + + /** + * Canonisation method. + * + * @var string + */ + protected $_canon = 'simple'; + + /** + * Headers not being signed. + * + * @var array + */ + protected $_ignoredHeaders = array(); + + /** + * Signer identity. + * + * @var string + */ + protected $_signerIdentity; + + /** + * Must we embed signed headers? + * + * @var bool + */ + protected $_debugHeaders = false; + + // work variables + /** + * Headers used to generate hash. + * + * @var array + */ + private $_signedHeaders = array(); + + /** + * Stores the signature header. + * + * @var Swift_Mime_Headers_ParameterizedHeader + */ + protected $_domainKeyHeader; + + /** + * Hash Handler. + * + * @var resource|null + */ + private $_hashHandler; + + private $_hash; + + private $_canonData = ''; + + private $_bodyCanonEmptyCounter = 0; + + private $_bodyCanonIgnoreStart = 2; + + private $_bodyCanonSpace = false; + + private $_bodyCanonLastChar = null; + + private $_bodyCanonLine = ''; + + private $_bound = array(); + + /** + * Constructor. + * + * @param string $privateKey + * @param string $domainName + * @param string $selector + */ + public function __construct($privateKey, $domainName, $selector) + { + $this->_privateKey = $privateKey; + $this->_domainName = $domainName; + $this->_signerIdentity = '@'.$domainName; + $this->_selector = $selector; + } + + /** + * Instanciate DomainKeySigner. + * + * @param string $privateKey + * @param string $domainName + * @param string $selector + * + * @return Swift_Signers_DomainKeySigner + */ + public static function newInstance($privateKey, $domainName, $selector) + { + return new static($privateKey, $domainName, $selector); + } + + /** + * Resets internal states. + * + * @return Swift_Signers_DomainKeySigner + */ + public function reset() + { + $this->_hash = null; + $this->_hashHandler = null; + $this->_bodyCanonIgnoreStart = 2; + $this->_bodyCanonEmptyCounter = 0; + $this->_bodyCanonLastChar = null; + $this->_bodyCanonSpace = false; + + return $this; + } + + /** + * Writes $bytes to the end of the stream. + * + * Writing may not happen immediately if the stream chooses to buffer. If + * you want to write these bytes with immediate effect, call {@link commit()} + * after calling write(). + * + * This method returns the sequence ID of the write (i.e. 1 for first, 2 for + * second, etc etc). + * + * @param string $bytes + * + * @throws Swift_IoException + * + * @return int + * @return Swift_Signers_DomainKeySigner + */ + public function write($bytes) + { + $this->_canonicalizeBody($bytes); + foreach ($this->_bound as $is) { + $is->write($bytes); + } + + return $this; + } + + /** + * For any bytes that are currently buffered inside the stream, force them + * off the buffer. + * + * @throws Swift_IoException + * + * @return Swift_Signers_DomainKeySigner + */ + public function commit() + { + // Nothing to do + return $this; + } + + /** + * Attach $is to this stream. + * The stream acts as an observer, receiving all data that is written. + * All {@link write()} and {@link flushBuffers()} operations will be mirrored. + * + * @param Swift_InputByteStream $is + * + * @return Swift_Signers_DomainKeySigner + */ + public function bind(Swift_InputByteStream $is) + { + // Don't have to mirror anything + $this->_bound[] = $is; + + return $this; + } + + /** + * Remove an already bound stream. + * If $is is not bound, no errors will be raised. + * If the stream currently has any buffered data it will be written to $is + * before unbinding occurs. + * + * @param Swift_InputByteStream $is + * + * @return Swift_Signers_DomainKeySigner + */ + public function unbind(Swift_InputByteStream $is) + { + // Don't have to mirror anything + foreach ($this->_bound as $k => $stream) { + if ($stream === $is) { + unset($this->_bound[$k]); + + return; + } + } + + return $this; + } + + /** + * Flush the contents of the stream (empty it) and set the internal pointer + * to the beginning. + * + * @throws Swift_IoException + * + * @return Swift_Signers_DomainKeySigner + */ + public function flushBuffers() + { + $this->reset(); + + return $this; + } + + /** + * Set hash_algorithm, must be one of rsa-sha256 | rsa-sha1 defaults to rsa-sha256. + * + * @param string $hash + * + * @return Swift_Signers_DomainKeySigner + */ + public function setHashAlgorithm($hash) + { + $this->_hashAlgorithm = 'rsa-sha1'; + + return $this; + } + + /** + * Set the canonicalization algorithm. + * + * @param string $canon simple | nofws defaults to simple + * + * @return Swift_Signers_DomainKeySigner + */ + public function setCanon($canon) + { + if ($canon == 'nofws') { + $this->_canon = 'nofws'; + } else { + $this->_canon = 'simple'; + } + + return $this; + } + + /** + * Set the signer identity. + * + * @param string $identity + * + * @return Swift_Signers_DomainKeySigner + */ + public function setSignerIdentity($identity) + { + $this->_signerIdentity = $identity; + + return $this; + } + + /** + * Enable / disable the DebugHeaders. + * + * @param bool $debug + * + * @return Swift_Signers_DomainKeySigner + */ + public function setDebugHeaders($debug) + { + $this->_debugHeaders = (bool) $debug; + + return $this; + } + + /** + * Start Body. + */ + public function startBody() + { + } + + /** + * End Body. + */ + public function endBody() + { + $this->_endOfBody(); + } + + /** + * Returns the list of Headers Tampered by this plugin. + * + * @return array + */ + public function getAlteredHeaders() + { + if ($this->_debugHeaders) { + return array('DomainKey-Signature', 'X-DebugHash'); + } + + return array('DomainKey-Signature'); + } + + /** + * Adds an ignored Header. + * + * @param string $header_name + * + * @return Swift_Signers_DomainKeySigner + */ + public function ignoreHeader($header_name) + { + $this->_ignoredHeaders[strtolower($header_name)] = true; + + return $this; + } + + /** + * Set the headers to sign. + * + * @param Swift_Mime_HeaderSet $headers + * + * @return Swift_Signers_DomainKeySigner + */ + public function setHeaders(Swift_Mime_HeaderSet $headers) + { + $this->_startHash(); + $this->_canonData = ''; + // Loop through Headers + $listHeaders = $headers->listAll(); + foreach ($listHeaders as $hName) { + // Check if we need to ignore Header + if (!isset($this->_ignoredHeaders[strtolower($hName)])) { + if ($headers->has($hName)) { + $tmp = $headers->getAll($hName); + foreach ($tmp as $header) { + if ($header->getFieldBody() != '') { + $this->_addHeader($header->toString()); + $this->_signedHeaders[] = $header->getFieldName(); + } + } + } + } + } + $this->_endOfHeaders(); + + return $this; + } + + /** + * Add the signature to the given Headers. + * + * @param Swift_Mime_HeaderSet $headers + * + * @return Swift_Signers_DomainKeySigner + */ + public function addSignature(Swift_Mime_HeaderSet $headers) + { + // Prepare the DomainKey-Signature Header + $params = array('a' => $this->_hashAlgorithm, 'b' => chunk_split(base64_encode($this->_getEncryptedHash()), 73, ' '), 'c' => $this->_canon, 'd' => $this->_domainName, 'h' => implode(': ', $this->_signedHeaders), 'q' => 'dns', 's' => $this->_selector); + $string = ''; + foreach ($params as $k => $v) { + $string .= $k.'='.$v.'; '; + } + $string = trim($string); + $headers->addTextHeader('DomainKey-Signature', $string); + + return $this; + } + + /* Private helpers */ + + protected function _addHeader($header) + { + switch ($this->_canon) { + case 'nofws': + // Prepare Header and cascade + $exploded = explode(':', $header, 2); + $name = strtolower(trim($exploded[0])); + $value = str_replace("\r\n", '', $exploded[1]); + $value = preg_replace("/[ \t][ \t]+/", ' ', $value); + $header = $name.':'.trim($value)."\r\n"; + case 'simple': + // Nothing to do + } + $this->_addToHash($header); + } + + protected function _endOfHeaders() + { + $this->_bodyCanonEmptyCounter = 1; + } + + protected function _canonicalizeBody($string) + { + $len = strlen($string); + $canon = ''; + $nofws = ($this->_canon == 'nofws'); + for ($i = 0; $i < $len; ++$i) { + if ($this->_bodyCanonIgnoreStart > 0) { + --$this->_bodyCanonIgnoreStart; + continue; + } + switch ($string[$i]) { + case "\r": + $this->_bodyCanonLastChar = "\r"; + break; + case "\n": + if ($this->_bodyCanonLastChar == "\r") { + if ($nofws) { + $this->_bodyCanonSpace = false; + } + if ($this->_bodyCanonLine == '') { + ++$this->_bodyCanonEmptyCounter; + } else { + $this->_bodyCanonLine = ''; + $canon .= "\r\n"; + } + } else { + // Wooops Error + throw new Swift_SwiftException('Invalid new line sequence in mail found \n without preceding \r'); + } + break; + case ' ': + case "\t": + case "\x09": //HTAB + if ($nofws) { + $this->_bodyCanonSpace = true; + break; + } + default: + if ($this->_bodyCanonEmptyCounter > 0) { + $canon .= str_repeat("\r\n", $this->_bodyCanonEmptyCounter); + $this->_bodyCanonEmptyCounter = 0; + } + $this->_bodyCanonLine .= $string[$i]; + $canon .= $string[$i]; + } + } + $this->_addToHash($canon); + } + + protected function _endOfBody() + { + if (strlen($this->_bodyCanonLine) > 0) { + $this->_addToHash("\r\n"); + } + $this->_hash = hash_final($this->_hashHandler, true); + } + + private function _addToHash($string) + { + $this->_canonData .= $string; + hash_update($this->_hashHandler, $string); + } + + private function _startHash() + { + // Init + switch ($this->_hashAlgorithm) { + case 'rsa-sha1': + $this->_hashHandler = hash_init('sha1'); + break; + } + $this->_bodyCanonLine = ''; + } + + /** + * @throws Swift_SwiftException + * + * @return string + */ + private function _getEncryptedHash() + { + $signature = ''; + $pkeyId = openssl_get_privatekey($this->_privateKey); + if (!$pkeyId) { + throw new Swift_SwiftException('Unable to load DomainKey Private Key ['.openssl_error_string().']'); + } + if (openssl_sign($this->_canonData, $signature, $pkeyId, OPENSSL_ALGO_SHA1)) { + return $signature; + } + throw new Swift_SwiftException('Unable to sign DomainKey Hash ['.openssl_error_string().']'); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/HeaderSigner.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/HeaderSigner.php new file mode 100644 index 0000000000..c75cb08a51 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/HeaderSigner.php @@ -0,0 +1,65 @@ + + */ +interface Swift_Signers_HeaderSigner extends Swift_Signer, Swift_InputByteStream +{ + /** + * Exclude an header from the signed headers. + * + * @param string $header_name + * + * @return Swift_Signers_HeaderSigner + */ + public function ignoreHeader($header_name); + + /** + * Prepare the Signer to get a new Body. + * + * @return Swift_Signers_HeaderSigner + */ + public function startBody(); + + /** + * Give the signal that the body has finished streaming. + * + * @return Swift_Signers_HeaderSigner + */ + public function endBody(); + + /** + * Give the headers already given. + * + * @param Swift_Mime_SimpleHeaderSet $headers + * + * @return Swift_Signers_HeaderSigner + */ + public function setHeaders(Swift_Mime_HeaderSet $headers); + + /** + * Add the header(s) to the headerSet. + * + * @param Swift_Mime_HeaderSet $headers + * + * @return Swift_Signers_HeaderSigner + */ + public function addSignature(Swift_Mime_HeaderSet $headers); + + /** + * Return the list of header a signer might tamper. + * + * @return array + */ + public function getAlteredHeaders(); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/OpenDKIMSigner.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/OpenDKIMSigner.php new file mode 100644 index 0000000000..3a35ad55d0 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/OpenDKIMSigner.php @@ -0,0 +1,190 @@ + + */ +class Swift_Signers_OpenDKIMSigner extends Swift_Signers_DKIMSigner +{ + private $_peclLoaded = false; + + private $_dkimHandler = null; + + private $dropFirstLF = true; + + const CANON_RELAXED = 1; + const CANON_SIMPLE = 2; + const SIG_RSA_SHA1 = 3; + const SIG_RSA_SHA256 = 4; + + public function __construct($privateKey, $domainName, $selector) + { + if (!extension_loaded('opendkim')) { + throw new Swift_SwiftException('php-opendkim extension not found'); + } + + $this->_peclLoaded = true; + + parent::__construct($privateKey, $domainName, $selector); + } + + public static function newInstance($privateKey, $domainName, $selector) + { + return new static($privateKey, $domainName, $selector); + } + + public function addSignature(Swift_Mime_HeaderSet $headers) + { + $header = new Swift_Mime_Headers_OpenDKIMHeader('DKIM-Signature'); + $headerVal = $this->_dkimHandler->getSignatureHeader(); + if (!$headerVal) { + throw new Swift_SwiftException('OpenDKIM Error: '.$this->_dkimHandler->getError()); + } + $header->setValue($headerVal); + $headers->set($header); + + return $this; + } + + public function setHeaders(Swift_Mime_HeaderSet $headers) + { + $bodyLen = $this->_bodyLen; + if (is_bool($bodyLen)) { + $bodyLen = -1; + } + $hash = $this->_hashAlgorithm == 'rsa-sha1' ? OpenDKIMSign::ALG_RSASHA1 : OpenDKIMSign::ALG_RSASHA256; + $bodyCanon = $this->_bodyCanon == 'simple' ? OpenDKIMSign::CANON_SIMPLE : OpenDKIMSign::CANON_RELAXED; + $headerCanon = $this->_headerCanon == 'simple' ? OpenDKIMSign::CANON_SIMPLE : OpenDKIMSign::CANON_RELAXED; + $this->_dkimHandler = new OpenDKIMSign($this->_privateKey, $this->_selector, $this->_domainName, $headerCanon, $bodyCanon, $hash, $bodyLen); + // Hardcode signature Margin for now + $this->_dkimHandler->setMargin(78); + + if (!is_numeric($this->_signatureTimestamp)) { + OpenDKIM::setOption(OpenDKIM::OPTS_FIXEDTIME, time()); + } else { + if (!OpenDKIM::setOption(OpenDKIM::OPTS_FIXEDTIME, $this->_signatureTimestamp)) { + throw new Swift_SwiftException('Unable to force signature timestamp ['.openssl_error_string().']'); + } + } + if (isset($this->_signerIdentity)) { + $this->_dkimHandler->setSigner($this->_signerIdentity); + } + $listHeaders = $headers->listAll(); + foreach ($listHeaders as $hName) { + // Check if we need to ignore Header + if (!isset($this->_ignoredHeaders[strtolower($hName)])) { + $tmp = $headers->getAll($hName); + if ($headers->has($hName)) { + foreach ($tmp as $header) { + if ($header->getFieldBody() != '') { + $htosign = $header->toString(); + $this->_dkimHandler->header($htosign); + $this->_signedHeaders[] = $header->getFieldName(); + } + } + } + } + } + + return $this; + } + + public function startBody() + { + if (!$this->_peclLoaded) { + return parent::startBody(); + } + $this->dropFirstLF = true; + $this->_dkimHandler->eoh(); + + return $this; + } + + public function endBody() + { + if (!$this->_peclLoaded) { + return parent::endBody(); + } + $this->_dkimHandler->eom(); + + return $this; + } + + public function reset() + { + $this->_dkimHandler = null; + parent::reset(); + + return $this; + } + + /** + * Set the signature timestamp. + * + * @param int $time + * + * @return Swift_Signers_DKIMSigner + */ + public function setSignatureTimestamp($time) + { + $this->_signatureTimestamp = $time; + + return $this; + } + + /** + * Set the signature expiration timestamp. + * + * @param int $time + * + * @return Swift_Signers_DKIMSigner + */ + public function setSignatureExpiration($time) + { + $this->_signatureExpiration = $time; + + return $this; + } + + /** + * Enable / disable the DebugHeaders. + * + * @param bool $debug + * + * @return Swift_Signers_DKIMSigner + */ + public function setDebugHeaders($debug) + { + $this->_debugHeaders = (bool) $debug; + + return $this; + } + + // Protected + + protected function _canonicalizeBody($string) + { + if (!$this->_peclLoaded) { + return parent::_canonicalizeBody($string); + } + if (false && $this->dropFirstLF === true) { + if ($string[0] == "\r" && $string[1] == "\n") { + $string = substr($string, 2); + } + } + $this->dropFirstLF = false; + if (strlen($string)) { + $this->_dkimHandler->body($string); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/SMimeSigner.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/SMimeSigner.php new file mode 100644 index 0000000000..b267099a86 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/SMimeSigner.php @@ -0,0 +1,436 @@ + + */ +class Swift_Signers_SMimeSigner implements Swift_Signers_BodySigner +{ + protected $signCertificate; + protected $signPrivateKey; + protected $encryptCert; + protected $signThenEncrypt = true; + protected $signLevel; + protected $encryptLevel; + protected $signOptions; + protected $encryptOptions; + protected $encryptCipher; + protected $extraCerts = null; + + /** + * @var Swift_StreamFilters_StringReplacementFilterFactory + */ + protected $replacementFactory; + + /** + * @var Swift_Mime_HeaderFactory + */ + protected $headerFactory; + + /** + * Constructor. + * + * @param string|null $signCertificate + * @param string|null $signPrivateKey + * @param string|null $encryptCertificate + */ + public function __construct($signCertificate = null, $signPrivateKey = null, $encryptCertificate = null) + { + if (null !== $signPrivateKey) { + $this->setSignCertificate($signCertificate, $signPrivateKey); + } + + if (null !== $encryptCertificate) { + $this->setEncryptCertificate($encryptCertificate); + } + + $this->replacementFactory = Swift_DependencyContainer::getInstance() + ->lookup('transport.replacementfactory'); + + $this->signOptions = PKCS7_DETACHED; + + // Supported since php5.4 + if (defined('OPENSSL_CIPHER_AES_128_CBC')) { + $this->encryptCipher = OPENSSL_CIPHER_AES_128_CBC; + } else { + $this->encryptCipher = OPENSSL_CIPHER_RC2_128; + } + } + + /** + * Returns an new Swift_Signers_SMimeSigner instance. + * + * @param string $certificate + * @param string $privateKey + * + * @return Swift_Signers_SMimeSigner + */ + public static function newInstance($certificate = null, $privateKey = null) + { + return new self($certificate, $privateKey); + } + + /** + * Set the certificate location to use for signing. + * + * @link http://www.php.net/manual/en/openssl.pkcs7.flags.php + * + * @param string $certificate + * @param string|array $privateKey If the key needs an passphrase use array('file-location', 'passphrase') instead + * @param int $signOptions Bitwise operator options for openssl_pkcs7_sign() + * @param string $extraCerts A file containing intermediate certificates needed by the signing certificate + * + * @return Swift_Signers_SMimeSigner + */ + public function setSignCertificate($certificate, $privateKey = null, $signOptions = PKCS7_DETACHED, $extraCerts = null) + { + $this->signCertificate = 'file://'.str_replace('\\', '/', realpath($certificate)); + + if (null !== $privateKey) { + if (is_array($privateKey)) { + $this->signPrivateKey = $privateKey; + $this->signPrivateKey[0] = 'file://'.str_replace('\\', '/', realpath($privateKey[0])); + } else { + $this->signPrivateKey = 'file://'.str_replace('\\', '/', realpath($privateKey)); + } + } + + $this->signOptions = $signOptions; + if (null !== $extraCerts) { + $this->extraCerts = str_replace('\\', '/', realpath($extraCerts)); + } + + return $this; + } + + /** + * Set the certificate location to use for encryption. + * + * @link http://www.php.net/manual/en/openssl.pkcs7.flags.php + * @link http://nl3.php.net/manual/en/openssl.ciphers.php + * + * @param string|array $recipientCerts Either an single X.509 certificate, or an assoc array of X.509 certificates. + * @param int $cipher + * + * @return Swift_Signers_SMimeSigner + */ + public function setEncryptCertificate($recipientCerts, $cipher = null) + { + if (is_array($recipientCerts)) { + $this->encryptCert = array(); + + foreach ($recipientCerts as $cert) { + $this->encryptCert[] = 'file://'.str_replace('\\', '/', realpath($cert)); + } + } else { + $this->encryptCert = 'file://'.str_replace('\\', '/', realpath($recipientCerts)); + } + + if (null !== $cipher) { + $this->encryptCipher = $cipher; + } + + return $this; + } + + /** + * @return string + */ + public function getSignCertificate() + { + return $this->signCertificate; + } + + /** + * @return string + */ + public function getSignPrivateKey() + { + return $this->signPrivateKey; + } + + /** + * Set perform signing before encryption. + * + * The default is to first sign the message and then encrypt. + * But some older mail clients, namely Microsoft Outlook 2000 will work when the message first encrypted. + * As this goes against the official specs, its recommended to only use 'encryption -> signing' when specifically targeting these 'broken' clients. + * + * @param bool $signThenEncrypt + * + * @return Swift_Signers_SMimeSigner + */ + public function setSignThenEncrypt($signThenEncrypt = true) + { + $this->signThenEncrypt = $signThenEncrypt; + + return $this; + } + + /** + * @return bool + */ + public function isSignThenEncrypt() + { + return $this->signThenEncrypt; + } + + /** + * Resets internal states. + * + * @return Swift_Signers_SMimeSigner + */ + public function reset() + { + return $this; + } + + /** + * Change the Swift_Message to apply the signing. + * + * @param Swift_Message $message + * + * @return Swift_Signers_SMimeSigner + */ + public function signMessage(Swift_Message $message) + { + if (null === $this->signCertificate && null === $this->encryptCert) { + return $this; + } + + // Store the message using ByteStream to a file{1} + // Remove all Children + // Sign file{1}, parse the new MIME headers and set them on the primary MimeEntity + // Set the singed-body as the new body (without boundary) + + $messageStream = new Swift_ByteStream_TemporaryFileByteStream(); + $this->toSMimeByteStream($messageStream, $message); + $message->setEncoder(Swift_DependencyContainer::getInstance()->lookup('mime.rawcontentencoder')); + + $message->setChildren(array()); + $this->streamToMime($messageStream, $message); + } + + /** + * Return the list of header a signer might tamper. + * + * @return array + */ + public function getAlteredHeaders() + { + return array('Content-Type', 'Content-Transfer-Encoding', 'Content-Disposition'); + } + + /** + * @param Swift_InputByteStream $inputStream + * @param Swift_Message $mimeEntity + */ + protected function toSMimeByteStream(Swift_InputByteStream $inputStream, Swift_Message $message) + { + $mimeEntity = $this->createMessage($message); + $messageStream = new Swift_ByteStream_TemporaryFileByteStream(); + + $mimeEntity->toByteStream($messageStream); + $messageStream->commit(); + + if (null !== $this->signCertificate && null !== $this->encryptCert) { + $temporaryStream = new Swift_ByteStream_TemporaryFileByteStream(); + + if ($this->signThenEncrypt) { + $this->messageStreamToSignedByteStream($messageStream, $temporaryStream); + $this->messageStreamToEncryptedByteStream($temporaryStream, $inputStream); + } else { + $this->messageStreamToEncryptedByteStream($messageStream, $temporaryStream); + $this->messageStreamToSignedByteStream($temporaryStream, $inputStream); + } + } elseif ($this->signCertificate !== null) { + $this->messageStreamToSignedByteStream($messageStream, $inputStream); + } else { + $this->messageStreamToEncryptedByteStream($messageStream, $inputStream); + } + } + + /** + * @param Swift_Message $message + * + * @return Swift_Message + */ + protected function createMessage(Swift_Message $message) + { + $mimeEntity = new Swift_Message('', $message->getBody(), $message->getContentType(), $message->getCharset()); + $mimeEntity->setChildren($message->getChildren()); + + $messageHeaders = $mimeEntity->getHeaders(); + $messageHeaders->remove('Message-ID'); + $messageHeaders->remove('Date'); + $messageHeaders->remove('Subject'); + $messageHeaders->remove('MIME-Version'); + $messageHeaders->remove('To'); + $messageHeaders->remove('From'); + + return $mimeEntity; + } + + /** + * @param Swift_FileStream $outputStream + * @param Swift_InputByteStream $inputStream + * + * @throws Swift_IoException + */ + protected function messageStreamToSignedByteStream(Swift_FileStream $outputStream, Swift_InputByteStream $inputStream) + { + $signedMessageStream = new Swift_ByteStream_TemporaryFileByteStream(); + + $args = array($outputStream->getPath(), $signedMessageStream->getPath(), $this->signCertificate, $this->signPrivateKey, array(), $this->signOptions); + if (null !== $this->extraCerts) { + $args[] = $this->extraCerts; + } + + if (!call_user_func_array('openssl_pkcs7_sign', $args)) { + throw new Swift_IoException(sprintf('Failed to sign S/Mime message. Error: "%s".', openssl_error_string())); + } + + $this->copyFromOpenSSLOutput($signedMessageStream, $inputStream); + } + + /** + * @param Swift_FileStream $outputStream + * @param Swift_InputByteStream $is + * + * @throws Swift_IoException + */ + protected function messageStreamToEncryptedByteStream(Swift_FileStream $outputStream, Swift_InputByteStream $is) + { + $encryptedMessageStream = new Swift_ByteStream_TemporaryFileByteStream(); + + if (!openssl_pkcs7_encrypt($outputStream->getPath(), $encryptedMessageStream->getPath(), $this->encryptCert, array(), 0, $this->encryptCipher)) { + throw new Swift_IoException(sprintf('Failed to encrypt S/Mime message. Error: "%s".', openssl_error_string())); + } + + $this->copyFromOpenSSLOutput($encryptedMessageStream, $is); + } + + /** + * @param Swift_OutputByteStream $fromStream + * @param Swift_InputByteStream $toStream + */ + protected function copyFromOpenSSLOutput(Swift_OutputByteStream $fromStream, Swift_InputByteStream $toStream) + { + $bufferLength = 4096; + $filteredStream = new Swift_ByteStream_TemporaryFileByteStream(); + $filteredStream->addFilter($this->replacementFactory->createFilter("\r\n", "\n"), 'CRLF to LF'); + $filteredStream->addFilter($this->replacementFactory->createFilter("\n", "\r\n"), 'LF to CRLF'); + + while (false !== ($buffer = $fromStream->read($bufferLength))) { + $filteredStream->write($buffer); + } + + $filteredStream->flushBuffers(); + + while (false !== ($buffer = $filteredStream->read($bufferLength))) { + $toStream->write($buffer); + } + + $toStream->commit(); + } + + /** + * Merges an OutputByteStream to Swift_Message. + * + * @param Swift_OutputByteStream $fromStream + * @param Swift_Message $message + */ + protected function streamToMime(Swift_OutputByteStream $fromStream, Swift_Message $message) + { + $bufferLength = 78; + $headerData = ''; + + $fromStream->setReadPointer(0); + + while (($buffer = $fromStream->read($bufferLength)) !== false) { + $headerData .= $buffer; + + if (false !== strpos($buffer, "\r\n\r\n")) { + break; + } + } + + $headersPosEnd = strpos($headerData, "\r\n\r\n"); + $headerData = trim($headerData); + $headerData = substr($headerData, 0, $headersPosEnd); + $headerLines = explode("\r\n", $headerData); + unset($headerData); + + $headers = array(); + $currentHeaderName = ''; + + foreach ($headerLines as $headerLine) { + // Line separated + if (ctype_space($headerLines[0]) || false === strpos($headerLine, ':')) { + $headers[$currentHeaderName] .= ' '.trim($headerLine); + continue; + } + + $header = explode(':', $headerLine, 2); + $currentHeaderName = strtolower($header[0]); + $headers[$currentHeaderName] = trim($header[1]); + } + + $messageStream = new Swift_ByteStream_TemporaryFileByteStream(); + $messageStream->addFilter($this->replacementFactory->createFilter("\r\n", "\n"), 'CRLF to LF'); + $messageStream->addFilter($this->replacementFactory->createFilter("\n", "\r\n"), 'LF to CRLF'); + + $messageHeaders = $message->getHeaders(); + + // No need to check for 'application/pkcs7-mime', as this is always base64 + if ('multipart/signed;' === substr($headers['content-type'], 0, 17)) { + if (!preg_match('/boundary=("[^"]+"|(?:[^\s]+|$))/is', $headers['content-type'], $contentTypeData)) { + throw new Swift_SwiftException('Failed to find Boundary parameter'); + } + + $boundary = trim($contentTypeData['1'], '"'); + + // Skip the header and CRLF CRLF + $fromStream->setReadPointer($headersPosEnd + 4); + + while (false !== ($buffer = $fromStream->read($bufferLength))) { + $messageStream->write($buffer); + } + + $messageStream->commit(); + + $messageHeaders->remove('Content-Transfer-Encoding'); + $message->setContentType($headers['content-type']); + $message->setBoundary($boundary); + $message->setBody($messageStream); + } else { + $fromStream->setReadPointer($headersPosEnd + 4); + + if (null === $this->headerFactory) { + $this->headerFactory = Swift_DependencyContainer::getInstance()->lookup('mime.headerfactory'); + } + + $message->setContentType($headers['content-type']); + $messageHeaders->set($this->headerFactory->createTextHeader('Content-Transfer-Encoding', $headers['content-transfer-encoding'])); + $messageHeaders->set($this->headerFactory->createTextHeader('Content-Disposition', $headers['content-disposition'])); + + while (false !== ($buffer = $fromStream->read($bufferLength))) { + $messageStream->write($buffer); + } + + $messageStream->commit(); + $message->setBody($messageStream); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SmtpTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SmtpTransport.php new file mode 100644 index 0000000000..6251611401 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SmtpTransport.php @@ -0,0 +1,58 @@ +createDependenciesFor('transport.smtp') + ); + + $this->setHost($host); + $this->setPort($port); + $this->setEncryption($security); + } + + /** + * Create a new SmtpTransport instance. + * + * @param string $host + * @param int $port + * @param string $security + * + * @return Swift_SmtpTransport + */ + public static function newInstance($host = 'localhost', $port = 25, $security = null) + { + return new self($host, $port, $security); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Spool.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Spool.php new file mode 100644 index 0000000000..c16ab4b38e --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Spool.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Interface for spools. + * + * @author Fabien Potencier + */ +interface Swift_Spool +{ + /** + * Starts this Spool mechanism. + */ + public function start(); + + /** + * Stops this Spool mechanism. + */ + public function stop(); + + /** + * Tests if this Spool mechanism has started. + * + * @return bool + */ + public function isStarted(); + + /** + * Queues a message. + * + * @param Swift_Mime_Message $message The message to store + * + * @return bool Whether the operation has succeeded + */ + public function queueMessage(Swift_Mime_Message $message); + + /** + * Sends messages using the given transport instance. + * + * @param Swift_Transport $transport A transport instance + * @param string[] $failedRecipients An array of failures by-reference + * + * @return int The number of sent emails + */ + public function flushQueue(Swift_Transport $transport, &$failedRecipients = null); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SpoolTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SpoolTransport.php new file mode 100644 index 0000000000..cf9bf78fb8 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SpoolTransport.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Stores Messages in a queue. + * + * @author Fabien Potencier + */ +class Swift_SpoolTransport extends Swift_Transport_SpoolTransport +{ + /** + * Create a new SpoolTransport. + * + * @param Swift_Spool $spool + */ + public function __construct(Swift_Spool $spool) + { + $arguments = Swift_DependencyContainer::getInstance() + ->createDependenciesFor('transport.spool'); + + $arguments[] = $spool; + + call_user_func_array( + array($this, 'Swift_Transport_SpoolTransport::__construct'), + $arguments + ); + } + + /** + * Create a new SpoolTransport instance. + * + * @param Swift_Spool $spool + * + * @return Swift_SpoolTransport + */ + public static function newInstance(Swift_Spool $spool) + { + return new self($spool); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilter.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilter.php new file mode 100644 index 0000000000..362be2e8d4 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilter.php @@ -0,0 +1,35 @@ +_search = $search; + $this->_index = array(); + $this->_tree = array(); + $this->_replace = array(); + $this->_repSize = array(); + + $tree = null; + $i = null; + $last_size = $size = 0; + foreach ($search as $i => $search_element) { + if ($tree !== null) { + $tree[-1] = min(count($replace) - 1, $i - 1); + $tree[-2] = $last_size; + } + $tree = &$this->_tree; + if (is_array($search_element)) { + foreach ($search_element as $k => $char) { + $this->_index[$char] = true; + if (!isset($tree[$char])) { + $tree[$char] = array(); + } + $tree = &$tree[$char]; + } + $last_size = $k + 1; + $size = max($size, $last_size); + } else { + $last_size = 1; + if (!isset($tree[$search_element])) { + $tree[$search_element] = array(); + } + $tree = &$tree[$search_element]; + $size = max($last_size, $size); + $this->_index[$search_element] = true; + } + } + if ($i !== null) { + $tree[-1] = min(count($replace) - 1, $i); + $tree[-2] = $last_size; + $this->_treeMaxLen = $size; + } + foreach ($replace as $rep) { + if (!is_array($rep)) { + $rep = array($rep); + } + $this->_replace[] = $rep; + } + for ($i = count($this->_replace) - 1; $i >= 0; --$i) { + $this->_replace[$i] = $rep = $this->filter($this->_replace[$i], $i); + $this->_repSize[$i] = count($rep); + } + } + + /** + * Returns true if based on the buffer passed more bytes should be buffered. + * + * @param array $buffer + * + * @return bool + */ + public function shouldBuffer($buffer) + { + $endOfBuffer = end($buffer); + + return isset($this->_index[$endOfBuffer]); + } + + /** + * Perform the actual replacements on $buffer and return the result. + * + * @param array $buffer + * @param int $_minReplaces + * + * @return array + */ + public function filter($buffer, $_minReplaces = -1) + { + if ($this->_treeMaxLen == 0) { + return $buffer; + } + + $newBuffer = array(); + $buf_size = count($buffer); + $last_size = 0; + for ($i = 0; $i < $buf_size; ++$i) { + $search_pos = $this->_tree; + $last_found = PHP_INT_MAX; + // We try to find if the next byte is part of a search pattern + for ($j = 0; $j <= $this->_treeMaxLen; ++$j) { + // We have a new byte for a search pattern + if (isset($buffer [$p = $i + $j]) && isset($search_pos[$buffer[$p]])) { + $search_pos = $search_pos[$buffer[$p]]; + // We have a complete pattern, save, in case we don't find a better match later + if (isset($search_pos[-1]) && $search_pos[-1] < $last_found + && $search_pos[-1] > $_minReplaces) { + $last_found = $search_pos[-1]; + $last_size = $search_pos[-2]; + } + } + // We got a complete pattern + elseif ($last_found !== PHP_INT_MAX) { + // Adding replacement datas to output buffer + $rep_size = $this->_repSize[$last_found]; + for ($j = 0; $j < $rep_size; ++$j) { + $newBuffer[] = $this->_replace[$last_found][$j]; + } + // We Move cursor forward + $i += $last_size - 1; + // Edge Case, last position in buffer + if ($i >= $buf_size) { + $newBuffer[] = $buffer[$i]; + } + + // We start the next loop + continue 2; + } else { + // this byte is not in a pattern and we haven't found another pattern + break; + } + } + // Normal byte, move it to output buffer + $newBuffer[] = $buffer[$i]; + } + + return $newBuffer; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilter.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilter.php new file mode 100644 index 0000000000..d0db8b9619 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilter.php @@ -0,0 +1,66 @@ +_search = $search; + $this->_replace = $replace; + } + + /** + * Returns true if based on the buffer passed more bytes should be buffered. + * + * @param string $buffer + * + * @return bool + */ + public function shouldBuffer($buffer) + { + $endOfBuffer = substr($buffer, -1); + foreach ((array) $this->_search as $needle) { + if (false !== strpos($needle, $endOfBuffer)) { + return true; + } + } + + return false; + } + + /** + * Perform the actual replacements on $buffer and return the result. + * + * @param string $buffer + * + * @return string + */ + public function filter($buffer) + { + return str_replace($this->_search, $this->_replace, $buffer); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilterFactory.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilterFactory.php new file mode 100644 index 0000000000..e98240b5bc --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilterFactory.php @@ -0,0 +1,45 @@ +_filters[$search][$replace])) { + if (!isset($this->_filters[$search])) { + $this->_filters[$search] = array(); + } + + if (!isset($this->_filters[$search][$replace])) { + $this->_filters[$search][$replace] = array(); + } + + $this->_filters[$search][$replace] = new Swift_StreamFilters_StringReplacementFilter($search, $replace); + } + + return $this->_filters[$search][$replace]; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SwiftException.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SwiftException.php new file mode 100644 index 0000000000..db3d31093e --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SwiftException.php @@ -0,0 +1,29 @@ +_eventDispatcher = $dispatcher; + $this->_buffer = $buf; + $this->_lookupHostname(); + } + + /** + * Set the name of the local domain which Swift will identify itself as. + * + * This should be a fully-qualified domain name and should be truly the domain + * you're using. + * + * If your server doesn't have a domain name, use the IP in square + * brackets (i.e. [127.0.0.1]). + * + * @param string $domain + * + * @return Swift_Transport_AbstractSmtpTransport + */ + public function setLocalDomain($domain) + { + $this->_domain = $domain; + + return $this; + } + + /** + * Get the name of the domain Swift will identify as. + * + * @return string + */ + public function getLocalDomain() + { + return $this->_domain; + } + + /** + * Sets the source IP. + * + * @param string $source + */ + public function setSourceIp($source) + { + $this->_sourceIp = $source; + } + + /** + * Returns the IP used to connect to the destination. + * + * @return string + */ + public function getSourceIp() + { + return $this->_sourceIp; + } + + /** + * Start the SMTP connection. + */ + public function start() + { + if (!$this->_started) { + if ($evt = $this->_eventDispatcher->createTransportChangeEvent($this)) { + $this->_eventDispatcher->dispatchEvent($evt, 'beforeTransportStarted'); + if ($evt->bubbleCancelled()) { + return; + } + } + + try { + $this->_buffer->initialize($this->_getBufferParams()); + } catch (Swift_TransportException $e) { + $this->_throwException($e); + } + $this->_readGreeting(); + $this->_doHeloCommand(); + + if ($evt) { + $this->_eventDispatcher->dispatchEvent($evt, 'transportStarted'); + } + + $this->_started = true; + } + } + + /** + * Test if an SMTP connection has been established. + * + * @return bool + */ + public function isStarted() + { + return $this->_started; + } + + /** + * Send the given Message. + * + * Recipient/sender data will be retrieved from the Message API. + * The return value is the number of recipients who were accepted for delivery. + * + * @param Swift_Mime_Message $message + * @param string[] $failedRecipients An array of failures by-reference + * + * @return int + */ + public function send(Swift_Mime_Message $message, &$failedRecipients = null) + { + $sent = 0; + $failedRecipients = (array) $failedRecipients; + + if ($evt = $this->_eventDispatcher->createSendEvent($this, $message)) { + $this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed'); + if ($evt->bubbleCancelled()) { + return 0; + } + } + + if (!$reversePath = $this->_getReversePath($message)) { + $this->_throwException(new Swift_TransportException( + 'Cannot send message without a sender address' + ) + ); + } + + $to = (array) $message->getTo(); + $cc = (array) $message->getCc(); + $tos = array_merge($to, $cc); + $bcc = (array) $message->getBcc(); + + $message->setBcc(array()); + + try { + $sent += $this->_sendTo($message, $reversePath, $tos, $failedRecipients); + $sent += $this->_sendBcc($message, $reversePath, $bcc, $failedRecipients); + } catch (Exception $e) { + $message->setBcc($bcc); + throw $e; + } + + $message->setBcc($bcc); + + if ($evt) { + if ($sent == count($to) + count($cc) + count($bcc)) { + $evt->setResult(Swift_Events_SendEvent::RESULT_SUCCESS); + } elseif ($sent > 0) { + $evt->setResult(Swift_Events_SendEvent::RESULT_TENTATIVE); + } else { + $evt->setResult(Swift_Events_SendEvent::RESULT_FAILED); + } + $evt->setFailedRecipients($failedRecipients); + $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed'); + } + + $message->generateId(); //Make sure a new Message ID is used + + return $sent; + } + + /** + * Stop the SMTP connection. + */ + public function stop() + { + if ($this->_started) { + if ($evt = $this->_eventDispatcher->createTransportChangeEvent($this)) { + $this->_eventDispatcher->dispatchEvent($evt, 'beforeTransportStopped'); + if ($evt->bubbleCancelled()) { + return; + } + } + + try { + $this->executeCommand("QUIT\r\n", array(221)); + } catch (Swift_TransportException $e) { + } + + try { + $this->_buffer->terminate(); + + if ($evt) { + $this->_eventDispatcher->dispatchEvent($evt, 'transportStopped'); + } + } catch (Swift_TransportException $e) { + $this->_throwException($e); + } + } + $this->_started = false; + } + + /** + * Register a plugin. + * + * @param Swift_Events_EventListener $plugin + */ + public function registerPlugin(Swift_Events_EventListener $plugin) + { + $this->_eventDispatcher->bindEventListener($plugin); + } + + /** + * Reset the current mail transaction. + */ + public function reset() + { + $this->executeCommand("RSET\r\n", array(250)); + } + + /** + * Get the IoBuffer where read/writes are occurring. + * + * @return Swift_Transport_IoBuffer + */ + public function getBuffer() + { + return $this->_buffer; + } + + /** + * Run a command against the buffer, expecting the given response codes. + * + * If no response codes are given, the response will not be validated. + * If codes are given, an exception will be thrown on an invalid response. + * + * @param string $command + * @param int[] $codes + * @param string[] $failures An array of failures by-reference + * + * @return string + */ + public function executeCommand($command, $codes = array(), &$failures = null) + { + $failures = (array) $failures; + $seq = $this->_buffer->write($command); + $response = $this->_getFullResponse($seq); + if ($evt = $this->_eventDispatcher->createCommandEvent($this, $command, $codes)) { + $this->_eventDispatcher->dispatchEvent($evt, 'commandSent'); + } + $this->_assertResponseCode($response, $codes); + + return $response; + } + + /** Read the opening SMTP greeting */ + protected function _readGreeting() + { + $this->_assertResponseCode($this->_getFullResponse(0), array(220)); + } + + /** Send the HELO welcome */ + protected function _doHeloCommand() + { + $this->executeCommand( + sprintf("HELO %s\r\n", $this->_domain), array(250) + ); + } + + /** Send the MAIL FROM command */ + protected function _doMailFromCommand($address) + { + $this->executeCommand( + sprintf("MAIL FROM:<%s>\r\n", $address), array(250) + ); + } + + /** Send the RCPT TO command */ + protected function _doRcptToCommand($address) + { + $this->executeCommand( + sprintf("RCPT TO:<%s>\r\n", $address), array(250, 251, 252) + ); + } + + /** Send the DATA command */ + protected function _doDataCommand() + { + $this->executeCommand("DATA\r\n", array(354)); + } + + /** Stream the contents of the message over the buffer */ + protected function _streamMessage(Swift_Mime_Message $message) + { + $this->_buffer->setWriteTranslations(array("\r\n." => "\r\n..")); + try { + $message->toByteStream($this->_buffer); + $this->_buffer->flushBuffers(); + } catch (Swift_TransportException $e) { + $this->_throwException($e); + } + $this->_buffer->setWriteTranslations(array()); + $this->executeCommand("\r\n.\r\n", array(250)); + } + + /** Determine the best-use reverse path for this message */ + protected function _getReversePath(Swift_Mime_Message $message) + { + $return = $message->getReturnPath(); + $sender = $message->getSender(); + $from = $message->getFrom(); + $path = null; + if (!empty($return)) { + $path = $return; + } elseif (!empty($sender)) { + // Don't use array_keys + reset($sender); // Reset Pointer to first pos + $path = key($sender); // Get key + } elseif (!empty($from)) { + reset($from); // Reset Pointer to first pos + $path = key($from); // Get key + } + + return $path; + } + + /** Throw a TransportException, first sending it to any listeners */ + protected function _throwException(Swift_TransportException $e) + { + if ($evt = $this->_eventDispatcher->createTransportExceptionEvent($this, $e)) { + $this->_eventDispatcher->dispatchEvent($evt, 'exceptionThrown'); + if (!$evt->bubbleCancelled()) { + throw $e; + } + } else { + throw $e; + } + } + + /** Throws an Exception if a response code is incorrect */ + protected function _assertResponseCode($response, $wanted) + { + list($code) = sscanf($response, '%3d'); + $valid = (empty($wanted) || in_array($code, $wanted)); + + if ($evt = $this->_eventDispatcher->createResponseEvent($this, $response, + $valid)) { + $this->_eventDispatcher->dispatchEvent($evt, 'responseReceived'); + } + + if (!$valid) { + $this->_throwException( + new Swift_TransportException( + 'Expected response code '.implode('/', $wanted).' but got code '. + '"'.$code.'", with message "'.$response.'"', + $code) + ); + } + } + + /** Get an entire multi-line response using its sequence number */ + protected function _getFullResponse($seq) + { + $response = ''; + try { + do { + $line = $this->_buffer->readLine($seq); + $response .= $line; + } while (null !== $line && false !== $line && ' ' != $line{3}); + } catch (Swift_TransportException $e) { + $this->_throwException($e); + } catch (Swift_IoException $e) { + $this->_throwException( + new Swift_TransportException( + $e->getMessage()) + ); + } + + return $response; + } + + /** Send an email to the given recipients from the given reverse path */ + private function _doMailTransaction($message, $reversePath, array $recipients, array &$failedRecipients) + { + $sent = 0; + $this->_doMailFromCommand($reversePath); + foreach ($recipients as $forwardPath) { + try { + $this->_doRcptToCommand($forwardPath); + ++$sent; + } catch (Swift_TransportException $e) { + $failedRecipients[] = $forwardPath; + } + } + + if ($sent != 0) { + $this->_doDataCommand(); + $this->_streamMessage($message); + } else { + $this->reset(); + } + + return $sent; + } + + /** Send a message to the given To: recipients */ + private function _sendTo(Swift_Mime_Message $message, $reversePath, array $to, array &$failedRecipients) + { + if (empty($to)) { + return 0; + } + + return $this->_doMailTransaction($message, $reversePath, array_keys($to), + $failedRecipients); + } + + /** Send a message to all Bcc: recipients */ + private function _sendBcc(Swift_Mime_Message $message, $reversePath, array $bcc, array &$failedRecipients) + { + $sent = 0; + foreach ($bcc as $forwardPath => $name) { + $message->setBcc(array($forwardPath => $name)); + $sent += $this->_doMailTransaction( + $message, $reversePath, array($forwardPath), $failedRecipients + ); + } + + return $sent; + } + + /** Try to determine the hostname of the server this is run on */ + private function _lookupHostname() + { + if (!empty($_SERVER['SERVER_NAME']) && $this->_isFqdn($_SERVER['SERVER_NAME'])) { + $this->_domain = $_SERVER['SERVER_NAME']; + } elseif (!empty($_SERVER['SERVER_ADDR'])) { + // Set the address literal tag (See RFC 5321, section: 4.1.3) + if (false === strpos($_SERVER['SERVER_ADDR'], ':')) { + $prefix = ''; // IPv4 addresses are not tagged. + } else { + $prefix = 'IPv6:'; // Adding prefix in case of IPv6. + } + + $this->_domain = sprintf('[%s%s]', $prefix, $_SERVER['SERVER_ADDR']); + } + } + + /** Determine is the $hostname is a fully-qualified name */ + private function _isFqdn($hostname) + { + // We could do a really thorough check, but there's really no point + if (false !== $dotPos = strpos($hostname, '.')) { + return ($dotPos > 0) && ($dotPos != strlen($hostname) - 1); + } + + return false; + } + + /** + * Destructor. + */ + public function __destruct() + { + $this->stop(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/CramMd5Authenticator.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/CramMd5Authenticator.php new file mode 100644 index 0000000000..53f721d03c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/CramMd5Authenticator.php @@ -0,0 +1,81 @@ +executeCommand("AUTH CRAM-MD5\r\n", array(334)); + $challenge = base64_decode(substr($challenge, 4)); + $message = base64_encode( + $username.' '.$this->_getResponse($password, $challenge) + ); + $agent->executeCommand(sprintf("%s\r\n", $message), array(235)); + + return true; + } catch (Swift_TransportException $e) { + $agent->executeCommand("RSET\r\n", array(250)); + + return false; + } + } + + /** + * Generate a CRAM-MD5 response from a server challenge. + * + * @param string $secret + * @param string $challenge + * + * @return string + */ + private function _getResponse($secret, $challenge) + { + if (strlen($secret) > 64) { + $secret = pack('H32', md5($secret)); + } + + if (strlen($secret) < 64) { + $secret = str_pad($secret, 64, chr(0)); + } + + $k_ipad = substr($secret, 0, 64) ^ str_repeat(chr(0x36), 64); + $k_opad = substr($secret, 0, 64) ^ str_repeat(chr(0x5C), 64); + + $inner = pack('H32', md5($k_ipad.$challenge)); + $digest = md5($k_opad.$inner); + + return $digest; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/LoginAuthenticator.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/LoginAuthenticator.php new file mode 100644 index 0000000000..6ab6e3337e --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/LoginAuthenticator.php @@ -0,0 +1,51 @@ +executeCommand("AUTH LOGIN\r\n", array(334)); + $agent->executeCommand(sprintf("%s\r\n", base64_encode($username)), array(334)); + $agent->executeCommand(sprintf("%s\r\n", base64_encode($password)), array(235)); + + return true; + } catch (Swift_TransportException $e) { + $agent->executeCommand("RSET\r\n", array(250)); + + return false; + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/NTLMAuthenticator.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/NTLMAuthenticator.php new file mode 100644 index 0000000000..eb04acf0ce --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/NTLMAuthenticator.php @@ -0,0 +1,720 @@ + + */ +class Swift_Transport_Esmtp_Auth_NTLMAuthenticator implements Swift_Transport_Esmtp_Authenticator +{ + const NTLMSIG = "NTLMSSP\x00"; + const DESCONST = 'KGS!@#$%'; + + /** + * Get the name of the AUTH mechanism this Authenticator handles. + * + * @return string + */ + public function getAuthKeyword() + { + return 'NTLM'; + } + + /** + * Try to authenticate the user with $username and $password. + * + * @param Swift_Transport_SmtpAgent $agent + * @param string $username + * @param string $password + * + * @return bool + */ + public function authenticate(Swift_Transport_SmtpAgent $agent, $username, $password) + { + if (!function_exists('openssl_random_pseudo_bytes') || !function_exists('openssl_encrypt')) { + throw new LogicException('The OpenSSL extension must be enabled to use the NTLM authenticator.'); + } + + if (!function_exists('bcmul')) { + throw new LogicException('The BCMath functions must be enabled to use the NTLM authenticator.'); + } + + try { + // execute AUTH command and filter out the code at the beginning + // AUTH NTLM xxxx + $response = base64_decode(substr(trim($this->sendMessage1($agent)), 4)); + + // extra parameters for our unit cases + $timestamp = func_num_args() > 3 ? func_get_arg(3) : $this->getCorrectTimestamp(bcmul(microtime(true), '1000')); + $client = func_num_args() > 4 ? func_get_arg(4) : $this->getRandomBytes(8); + + // Message 3 response + $this->sendMessage3($response, $username, $password, $timestamp, $client, $agent); + + return true; + } catch (Swift_TransportException $e) { + $agent->executeCommand("RSET\r\n", array(250)); + + return false; + } + } + + protected function si2bin($si, $bits = 32) + { + $bin = null; + if ($si >= -pow(2, $bits - 1) && ($si <= pow(2, $bits - 1))) { + // positive or zero + if ($si >= 0) { + $bin = base_convert($si, 10, 2); + // pad to $bits bit + $bin_length = strlen($bin); + if ($bin_length < $bits) { + $bin = str_repeat('0', $bits - $bin_length).$bin; + } + } else { + // negative + $si = -$si - pow(2, $bits); + $bin = base_convert($si, 10, 2); + $bin_length = strlen($bin); + if ($bin_length > $bits) { + $bin = str_repeat('1', $bits - $bin_length).$bin; + } + } + } + + return $bin; + } + + /** + * Send our auth message and returns the response. + * + * @param Swift_Transport_SmtpAgent $agent + * + * @return string SMTP Response + */ + protected function sendMessage1(Swift_Transport_SmtpAgent $agent) + { + $message = $this->createMessage1(); + + return $agent->executeCommand(sprintf("AUTH %s %s\r\n", $this->getAuthKeyword(), base64_encode($message)), array(334)); + } + + /** + * Fetch all details of our response (message 2). + * + * @param string $response + * + * @return array our response parsed + */ + protected function parseMessage2($response) + { + $responseHex = bin2hex($response); + $length = floor(hexdec(substr($responseHex, 28, 4)) / 256) * 2; + $offset = floor(hexdec(substr($responseHex, 32, 4)) / 256) * 2; + $challenge = $this->hex2bin(substr($responseHex, 48, 16)); + $context = $this->hex2bin(substr($responseHex, 64, 16)); + $targetInfoH = $this->hex2bin(substr($responseHex, 80, 16)); + $targetName = $this->hex2bin(substr($responseHex, $offset, $length)); + $offset = floor(hexdec(substr($responseHex, 88, 4)) / 256) * 2; + $targetInfoBlock = substr($responseHex, $offset); + list($domainName, $serverName, $DNSDomainName, $DNSServerName, $terminatorByte) = $this->readSubBlock($targetInfoBlock); + + return array( + $challenge, + $context, + $targetInfoH, + $targetName, + $domainName, + $serverName, + $DNSDomainName, + $DNSServerName, + $this->hex2bin($targetInfoBlock), + $terminatorByte, + ); + } + + /** + * Read the blob information in from message2. + * + * @param $block + * + * @return array + */ + protected function readSubBlock($block) + { + // remove terminatorByte cause it's always the same + $block = substr($block, 0, -8); + + $length = strlen($block); + $offset = 0; + $data = array(); + while ($offset < $length) { + $blockLength = hexdec(substr(substr($block, $offset, 8), -4)) / 256; + $offset += 8; + $data[] = $this->hex2bin(substr($block, $offset, $blockLength * 2)); + $offset += $blockLength * 2; + } + + if (count($data) == 3) { + $data[] = $data[2]; + $data[2] = ''; + } + + $data[] = $this->createByte('00'); + + return $data; + } + + /** + * Send our final message with all our data. + * + * @param string $response Message 1 response (message 2) + * @param string $username + * @param string $password + * @param string $timestamp + * @param string $client + * @param Swift_Transport_SmtpAgent $agent + * @param bool $v2 Use version2 of the protocol + * + * @return string + */ + protected function sendMessage3($response, $username, $password, $timestamp, $client, Swift_Transport_SmtpAgent $agent, $v2 = true) + { + list($domain, $username) = $this->getDomainAndUsername($username); + //$challenge, $context, $targetInfoH, $targetName, $domainName, $workstation, $DNSDomainName, $DNSServerName, $blob, $ter + list($challenge, , , , , $workstation, , , $blob) = $this->parseMessage2($response); + + if (!$v2) { + // LMv1 + $lmResponse = $this->createLMPassword($password, $challenge); + // NTLMv1 + $ntlmResponse = $this->createNTLMPassword($password, $challenge); + } else { + // LMv2 + $lmResponse = $this->createLMv2Password($password, $username, $domain, $challenge, $client); + // NTLMv2 + $ntlmResponse = $this->createNTLMv2Hash($password, $username, $domain, $challenge, $blob, $timestamp, $client); + } + + $message = $this->createMessage3($domain, $username, $workstation, $lmResponse, $ntlmResponse); + + return $agent->executeCommand(sprintf("%s\r\n", base64_encode($message)), array(235)); + } + + /** + * Create our message 1. + * + * @return string + */ + protected function createMessage1() + { + return self::NTLMSIG + .$this->createByte('01') // Message 1 +.$this->createByte('0702'); // Flags + } + + /** + * Create our message 3. + * + * @param string $domain + * @param string $username + * @param string $workstation + * @param string $lmResponse + * @param string $ntlmResponse + * + * @return string + */ + protected function createMessage3($domain, $username, $workstation, $lmResponse, $ntlmResponse) + { + // Create security buffers + $domainSec = $this->createSecurityBuffer($domain, 64); + $domainInfo = $this->readSecurityBuffer(bin2hex($domainSec)); + $userSec = $this->createSecurityBuffer($username, ($domainInfo[0] + $domainInfo[1]) / 2); + $userInfo = $this->readSecurityBuffer(bin2hex($userSec)); + $workSec = $this->createSecurityBuffer($workstation, ($userInfo[0] + $userInfo[1]) / 2); + $workInfo = $this->readSecurityBuffer(bin2hex($workSec)); + $lmSec = $this->createSecurityBuffer($lmResponse, ($workInfo[0] + $workInfo[1]) / 2, true); + $lmInfo = $this->readSecurityBuffer(bin2hex($lmSec)); + $ntlmSec = $this->createSecurityBuffer($ntlmResponse, ($lmInfo[0] + $lmInfo[1]) / 2, true); + + return self::NTLMSIG + .$this->createByte('03') // TYPE 3 message +.$lmSec // LM response header +.$ntlmSec // NTLM response header +.$domainSec // Domain header +.$userSec // User header +.$workSec // Workstation header +.$this->createByte('000000009a', 8) // session key header (empty) +.$this->createByte('01020000') // FLAGS +.$this->convertTo16bit($domain) // domain name +.$this->convertTo16bit($username) // username +.$this->convertTo16bit($workstation) // workstation +.$lmResponse + .$ntlmResponse; + } + + /** + * @param string $timestamp Epoch timestamp in microseconds + * @param string $client Random bytes + * @param string $targetInfo + * + * @return string + */ + protected function createBlob($timestamp, $client, $targetInfo) + { + return $this->createByte('0101') + .$this->createByte('00') + .$timestamp + .$client + .$this->createByte('00') + .$targetInfo + .$this->createByte('00'); + } + + /** + * Get domain and username from our username. + * + * @example DOMAIN\username + * + * @param string $name + * + * @return array + */ + protected function getDomainAndUsername($name) + { + if (strpos($name, '\\') !== false) { + return explode('\\', $name); + } + + list($user, $domain) = explode('@', $name); + + return array($domain, $user); + } + + /** + * Create LMv1 response. + * + * @param string $password + * @param string $challenge + * + * @return string + */ + protected function createLMPassword($password, $challenge) + { + // FIRST PART + $password = $this->createByte(strtoupper($password), 14, false); + list($key1, $key2) = str_split($password, 7); + + $desKey1 = $this->createDesKey($key1); + $desKey2 = $this->createDesKey($key2); + + $constantDecrypt = $this->createByte($this->desEncrypt(self::DESCONST, $desKey1).$this->desEncrypt(self::DESCONST, $desKey2), 21, false); + + // SECOND PART + list($key1, $key2, $key3) = str_split($constantDecrypt, 7); + + $desKey1 = $this->createDesKey($key1); + $desKey2 = $this->createDesKey($key2); + $desKey3 = $this->createDesKey($key3); + + return $this->desEncrypt($challenge, $desKey1).$this->desEncrypt($challenge, $desKey2).$this->desEncrypt($challenge, $desKey3); + } + + /** + * Create NTLMv1 response. + * + * @param string $password + * @param string $challenge + * + * @return string + */ + protected function createNTLMPassword($password, $challenge) + { + // FIRST PART + $ntlmHash = $this->createByte($this->md4Encrypt($password), 21, false); + list($key1, $key2, $key3) = str_split($ntlmHash, 7); + + $desKey1 = $this->createDesKey($key1); + $desKey2 = $this->createDesKey($key2); + $desKey3 = $this->createDesKey($key3); + + return $this->desEncrypt($challenge, $desKey1).$this->desEncrypt($challenge, $desKey2).$this->desEncrypt($challenge, $desKey3); + } + + /** + * Convert a normal timestamp to a tenth of a microtime epoch time. + * + * @param string $time + * + * @return string + */ + protected function getCorrectTimestamp($time) + { + // Get our timestamp (tricky!) + bcscale(0); + + $time = number_format($time, 0, '.', ''); // save microtime to string + $time = bcadd($time, '11644473600000'); // add epoch time + $time = bcmul($time, 10000); // tenths of a microsecond. + + $binary = $this->si2bin($time, 64); // create 64 bit binary string + $timestamp = ''; + for ($i = 0; $i < 8; ++$i) { + $timestamp .= chr(bindec(substr($binary, -(($i + 1) * 8), 8))); + } + + return $timestamp; + } + + /** + * Create LMv2 response. + * + * @param string $password + * @param string $username + * @param string $domain + * @param string $challenge NTLM Challenge + * @param string $client Random string + * + * @return string + */ + protected function createLMv2Password($password, $username, $domain, $challenge, $client) + { + $lmPass = '00'; // by default 00 + // if $password > 15 than we can't use this method + if (strlen($password) <= 15) { + $ntlmHash = $this->md4Encrypt($password); + $ntml2Hash = $this->md5Encrypt($ntlmHash, $this->convertTo16bit(strtoupper($username).$domain)); + + $lmPass = bin2hex($this->md5Encrypt($ntml2Hash, $challenge.$client).$client); + } + + return $this->createByte($lmPass, 24); + } + + /** + * Create NTLMv2 response. + * + * @param string $password + * @param string $username + * @param string $domain + * @param string $challenge Hex values + * @param string $targetInfo Hex values + * @param string $timestamp + * @param string $client Random bytes + * + * @return string + * + * @see http://davenport.sourceforge.net/ntlm.html#theNtlmResponse + */ + protected function createNTLMv2Hash($password, $username, $domain, $challenge, $targetInfo, $timestamp, $client) + { + $ntlmHash = $this->md4Encrypt($password); + $ntml2Hash = $this->md5Encrypt($ntlmHash, $this->convertTo16bit(strtoupper($username).$domain)); + + // create blob + $blob = $this->createBlob($timestamp, $client, $targetInfo); + + $ntlmv2Response = $this->md5Encrypt($ntml2Hash, $challenge.$blob); + + return $ntlmv2Response.$blob; + } + + protected function createDesKey($key) + { + $material = array(bin2hex($key[0])); + $len = strlen($key); + for ($i = 1; $i < $len; ++$i) { + list($high, $low) = str_split(bin2hex($key[$i])); + $v = $this->castToByte(ord($key[$i - 1]) << (7 + 1 - $i) | $this->uRShift(hexdec(dechex(hexdec($high) & 0xf).dechex(hexdec($low) & 0xf)), $i)); + $material[] = str_pad(substr(dechex($v), -2), 2, '0', STR_PAD_LEFT); // cast to byte + } + $material[] = str_pad(substr(dechex($this->castToByte(ord($key[6]) << 1)), -2), 2, '0'); + + // odd parity + foreach ($material as $k => $v) { + $b = $this->castToByte(hexdec($v)); + $needsParity = (($this->uRShift($b, 7) ^ $this->uRShift($b, 6) ^ $this->uRShift($b, 5) + ^ $this->uRShift($b, 4) ^ $this->uRShift($b, 3) ^ $this->uRShift($b, 2) + ^ $this->uRShift($b, 1)) & 0x01) == 0; + + list($high, $low) = str_split($v); + if ($needsParity) { + $material[$k] = dechex(hexdec($high) | 0x0).dechex(hexdec($low) | 0x1); + } else { + $material[$k] = dechex(hexdec($high) & 0xf).dechex(hexdec($low) & 0xe); + } + } + + return $this->hex2bin(implode('', $material)); + } + + /** HELPER FUNCTIONS */ + /** + * Create our security buffer depending on length and offset. + * + * @param string $value Value we want to put in + * @param int $offset start of value + * @param bool $is16 Do we 16bit string or not? + * + * @return string + */ + protected function createSecurityBuffer($value, $offset, $is16 = false) + { + $length = strlen(bin2hex($value)); + $length = $is16 ? $length / 2 : $length; + $length = $this->createByte(str_pad(dechex($length), 2, '0', STR_PAD_LEFT), 2); + + return $length.$length.$this->createByte(dechex($offset), 4); + } + + /** + * Read our security buffer to fetch length and offset of our value. + * + * @param string $value Securitybuffer in hex + * + * @return array array with length and offset + */ + protected function readSecurityBuffer($value) + { + $length = floor(hexdec(substr($value, 0, 4)) / 256) * 2; + $offset = floor(hexdec(substr($value, 8, 4)) / 256) * 2; + + return array($length, $offset); + } + + /** + * Cast to byte java equivalent to (byte). + * + * @param int $v + * + * @return int + */ + protected function castToByte($v) + { + return (($v + 128) % 256) - 128; + } + + /** + * Java unsigned right bitwise + * $a >>> $b. + * + * @param int $a + * @param int $b + * + * @return int + */ + protected function uRShift($a, $b) + { + if ($b == 0) { + return $a; + } + + return ($a >> $b) & ~(1 << (8 * PHP_INT_SIZE - 1) >> ($b - 1)); + } + + /** + * Right padding with 0 to certain length. + * + * @param string $input + * @param int $bytes Length of bytes + * @param bool $isHex Did we provided hex value + * + * @return string + */ + protected function createByte($input, $bytes = 4, $isHex = true) + { + if ($isHex) { + $byte = $this->hex2bin(str_pad($input, $bytes * 2, '00')); + } else { + $byte = str_pad($input, $bytes, "\x00"); + } + + return $byte; + } + + /** + * Create random bytes. + * + * @param $length + * + * @return string + */ + protected function getRandomBytes($length) + { + $bytes = openssl_random_pseudo_bytes($length, $strong); + + if (false !== $bytes && true === $strong) { + return $bytes; + } + + throw new RuntimeException('OpenSSL did not produce a secure random number.'); + } + + /** ENCRYPTION ALGORITHMS */ + /** + * DES Encryption. + * + * @param string $value An 8-byte string + * @param string $key + * + * @return string + */ + protected function desEncrypt($value, $key) + { + // 1 == OPENSSL_RAW_DATA - but constant is only available as of PHP 5.4. + return substr(openssl_encrypt($value, 'DES-ECB', $key, 1), 0, 8); + } + + /** + * MD5 Encryption. + * + * @param string $key Encryption key + * @param string $msg Message to encrypt + * + * @return string + */ + protected function md5Encrypt($key, $msg) + { + $blocksize = 64; + if (strlen($key) > $blocksize) { + $key = pack('H*', md5($key)); + } + + $key = str_pad($key, $blocksize, "\0"); + $ipadk = $key ^ str_repeat("\x36", $blocksize); + $opadk = $key ^ str_repeat("\x5c", $blocksize); + + return pack('H*', md5($opadk.pack('H*', md5($ipadk.$msg)))); + } + + /** + * MD4 Encryption. + * + * @param string $input + * + * @return string + * + * @see http://php.net/manual/en/ref.hash.php + */ + protected function md4Encrypt($input) + { + $input = $this->convertTo16bit($input); + + return function_exists('hash') ? $this->hex2bin(hash('md4', $input)) : mhash(MHASH_MD4, $input); + } + + /** + * Convert UTF-8 to UTF-16. + * + * @param string $input + * + * @return string + */ + protected function convertTo16bit($input) + { + return iconv('UTF-8', 'UTF-16LE', $input); + } + + /** + * Hex2bin replacement for < PHP 5.4. + * + * @param string $hex + * + * @return string Binary + */ + protected function hex2bin($hex) + { + if (function_exists('hex2bin')) { + return hex2bin($hex); + } else { + return pack('H*', $hex); + } + } + + /** + * @param string $message + */ + protected function debug($message) + { + $message = bin2hex($message); + $messageId = substr($message, 16, 8); + echo substr($message, 0, 16)." NTLMSSP Signature
\n"; + echo $messageId." Type Indicator
\n"; + + if ($messageId == '02000000') { + $map = array( + 'Challenge', + 'Context', + 'Target Information Security Buffer', + 'Target Name Data', + 'NetBIOS Domain Name', + 'NetBIOS Server Name', + 'DNS Domain Name', + 'DNS Server Name', + 'BLOB', + 'Target Information Terminator', + ); + + $data = $this->parseMessage2($this->hex2bin($message)); + + foreach ($map as $key => $value) { + echo bin2hex($data[$key]).' - '.$data[$key].' ||| '.$value."
\n"; + } + } elseif ($messageId == '03000000') { + $i = 0; + $data[$i++] = substr($message, 24, 16); + list($lmLength, $lmOffset) = $this->readSecurityBuffer($data[$i - 1]); + + $data[$i++] = substr($message, 40, 16); + list($ntmlLength, $ntmlOffset) = $this->readSecurityBuffer($data[$i - 1]); + + $data[$i++] = substr($message, 56, 16); + list($targetLength, $targetOffset) = $this->readSecurityBuffer($data[$i - 1]); + + $data[$i++] = substr($message, 72, 16); + list($userLength, $userOffset) = $this->readSecurityBuffer($data[$i - 1]); + + $data[$i++] = substr($message, 88, 16); + list($workLength, $workOffset) = $this->readSecurityBuffer($data[$i - 1]); + + $data[$i++] = substr($message, 104, 16); + $data[$i++] = substr($message, 120, 8); + $data[$i++] = substr($message, $targetOffset, $targetLength); + $data[$i++] = substr($message, $userOffset, $userLength); + $data[$i++] = substr($message, $workOffset, $workLength); + $data[$i++] = substr($message, $lmOffset, $lmLength); + $data[$i] = substr($message, $ntmlOffset, $ntmlLength); + + $map = array( + 'LM Response Security Buffer', + 'NTLM Response Security Buffer', + 'Target Name Security Buffer', + 'User Name Security Buffer', + 'Workstation Name Security Buffer', + 'Session Key Security Buffer', + 'Flags', + 'Target Name Data', + 'User Name Data', + 'Workstation Name Data', + 'LM Response Data', + 'NTLM Response Data', + ); + + foreach ($map as $key => $value) { + echo $data[$key].' - '.$this->hex2bin($data[$key]).' ||| '.$value."
\n"; + } + } + + echo '

'; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/PlainAuthenticator.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/PlainAuthenticator.php new file mode 100644 index 0000000000..43219f9344 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/PlainAuthenticator.php @@ -0,0 +1,50 @@ +executeCommand(sprintf("AUTH PLAIN %s\r\n", $message), array(235)); + + return true; + } catch (Swift_TransportException $e) { + $agent->executeCommand("RSET\r\n", array(250)); + + return false; + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/XOAuth2Authenticator.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/XOAuth2Authenticator.php new file mode 100644 index 0000000000..ca35e7b83a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/XOAuth2Authenticator.php @@ -0,0 +1,70 @@ + + * $transport = Swift_SmtpTransport::newInstance('smtp.gmail.com', 587, 'tls') + * ->setAuthMode('XOAUTH2') + * ->setUsername('YOUR_EMAIL_ADDRESS') + * ->setPassword('YOUR_ACCESS_TOKEN'); + * + * + * @author xu.li + * + * @see https://developers.google.com/google-apps/gmail/xoauth2_protocol + */ +class Swift_Transport_Esmtp_Auth_XOAuth2Authenticator implements Swift_Transport_Esmtp_Authenticator +{ + /** + * Get the name of the AUTH mechanism this Authenticator handles. + * + * @return string + */ + public function getAuthKeyword() + { + return 'XOAUTH2'; + } + + /** + * Try to authenticate the user with $email and $token. + * + * @param Swift_Transport_SmtpAgent $agent + * @param string $email + * @param string $token + * + * @return bool + */ + public function authenticate(Swift_Transport_SmtpAgent $agent, $email, $token) + { + try { + $param = $this->constructXOAuth2Params($email, $token); + $agent->executeCommand('AUTH XOAUTH2 '.$param."\r\n", array(235)); + + return true; + } catch (Swift_TransportException $e) { + $agent->executeCommand("RSET\r\n", array(250)); + + return false; + } + } + + /** + * Construct the auth parameter. + * + * @see https://developers.google.com/google-apps/gmail/xoauth2_protocol#the_sasl_xoauth2_mechanism + */ + protected function constructXOAuth2Params($email, $token) + { + return base64_encode("user=$email\1auth=Bearer $token\1\1"); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/AuthHandler.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/AuthHandler.php new file mode 100644 index 0000000000..cb36133c94 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/AuthHandler.php @@ -0,0 +1,263 @@ +setAuthenticators($authenticators); + } + + /** + * Set the Authenticators which can process a login request. + * + * @param Swift_Transport_Esmtp_Authenticator[] $authenticators + */ + public function setAuthenticators(array $authenticators) + { + $this->_authenticators = $authenticators; + } + + /** + * Get the Authenticators which can process a login request. + * + * @return Swift_Transport_Esmtp_Authenticator[] + */ + public function getAuthenticators() + { + return $this->_authenticators; + } + + /** + * Set the username to authenticate with. + * + * @param string $username + */ + public function setUsername($username) + { + $this->_username = $username; + } + + /** + * Get the username to authenticate with. + * + * @return string + */ + public function getUsername() + { + return $this->_username; + } + + /** + * Set the password to authenticate with. + * + * @param string $password + */ + public function setPassword($password) + { + $this->_password = $password; + } + + /** + * Get the password to authenticate with. + * + * @return string + */ + public function getPassword() + { + return $this->_password; + } + + /** + * Set the auth mode to use to authenticate. + * + * @param string $mode + */ + public function setAuthMode($mode) + { + $this->_auth_mode = $mode; + } + + /** + * Get the auth mode to use to authenticate. + * + * @return string + */ + public function getAuthMode() + { + return $this->_auth_mode; + } + + /** + * Get the name of the ESMTP extension this handles. + * + * @return bool + */ + public function getHandledKeyword() + { + return 'AUTH'; + } + + /** + * Set the parameters which the EHLO greeting indicated. + * + * @param string[] $parameters + */ + public function setKeywordParams(array $parameters) + { + $this->_esmtpParams = $parameters; + } + + /** + * Runs immediately after a EHLO has been issued. + * + * @param Swift_Transport_SmtpAgent $agent to read/write + */ + public function afterEhlo(Swift_Transport_SmtpAgent $agent) + { + if ($this->_username) { + $count = 0; + foreach ($this->_getAuthenticatorsForAgent() as $authenticator) { + if (in_array(strtolower($authenticator->getAuthKeyword()), + array_map('strtolower', $this->_esmtpParams))) { + ++$count; + if ($authenticator->authenticate($agent, $this->_username, $this->_password)) { + return; + } + } + } + throw new Swift_TransportException( + 'Failed to authenticate on SMTP server with username "'. + $this->_username.'" using '.$count.' possible authenticators' + ); + } + } + + /** + * Not used. + */ + public function getMailParams() + { + return array(); + } + + /** + * Not used. + */ + public function getRcptParams() + { + return array(); + } + + /** + * Not used. + */ + public function onCommand(Swift_Transport_SmtpAgent $agent, $command, $codes = array(), &$failedRecipients = null, &$stop = false) + { + } + + /** + * Returns +1, -1 or 0 according to the rules for usort(). + * + * This method is called to ensure extensions can be execute in an appropriate order. + * + * @param string $esmtpKeyword to compare with + * + * @return int + */ + public function getPriorityOver($esmtpKeyword) + { + return 0; + } + + /** + * Returns an array of method names which are exposed to the Esmtp class. + * + * @return string[] + */ + public function exposeMixinMethods() + { + return array('setUsername', 'getUsername', 'setPassword', 'getPassword', 'setAuthMode', 'getAuthMode'); + } + + /** + * Not used. + */ + public function resetState() + { + } + + /** + * Returns the authenticator list for the given agent. + * + * @param Swift_Transport_SmtpAgent $agent + * + * @return array + */ + protected function _getAuthenticatorsForAgent() + { + if (!$mode = strtolower($this->_auth_mode)) { + return $this->_authenticators; + } + + foreach ($this->_authenticators as $authenticator) { + if (strtolower($authenticator->getAuthKeyword()) == $mode) { + return array($authenticator); + } + } + + throw new Swift_TransportException('Auth mode '.$mode.' is invalid'); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Authenticator.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Authenticator.php new file mode 100644 index 0000000000..12a9abf819 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Authenticator.php @@ -0,0 +1,35 @@ +. + * + * @return string[] + */ + public function getMailParams(); + + /** + * Get params which are appended to RCPT TO:<>. + * + * @return string[] + */ + public function getRcptParams(); + + /** + * Runs when a command is due to be sent. + * + * @param Swift_Transport_SmtpAgent $agent to read/write + * @param string $command to send + * @param int[] $codes expected in response + * @param string[] $failedRecipients to collect failures + * @param bool $stop to be set true by-reference if the command is now sent + */ + public function onCommand(Swift_Transport_SmtpAgent $agent, $command, $codes = array(), &$failedRecipients = null, &$stop = false); + + /** + * Returns +1, -1 or 0 according to the rules for usort(). + * + * This method is called to ensure extensions can be execute in an appropriate order. + * + * @param string $esmtpKeyword to compare with + * + * @return int + */ + public function getPriorityOver($esmtpKeyword); + + /** + * Returns an array of method names which are exposed to the Esmtp class. + * + * @return string[] + */ + public function exposeMixinMethods(); + + /** + * Tells this handler to clear any buffers and reset its state. + */ + public function resetState(); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/EsmtpTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/EsmtpTransport.php new file mode 100644 index 0000000000..4106df417f --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/EsmtpTransport.php @@ -0,0 +1,413 @@ + 'tcp', + 'host' => 'localhost', + 'port' => 25, + 'timeout' => 30, + 'blocking' => 1, + 'tls' => false, + 'type' => Swift_Transport_IoBuffer::TYPE_SOCKET, + 'stream_context_options' => array(), + ); + + /** + * Creates a new EsmtpTransport using the given I/O buffer. + * + * @param Swift_Transport_IoBuffer $buf + * @param Swift_Transport_EsmtpHandler[] $extensionHandlers + * @param Swift_Events_EventDispatcher $dispatcher + */ + public function __construct(Swift_Transport_IoBuffer $buf, array $extensionHandlers, Swift_Events_EventDispatcher $dispatcher) + { + parent::__construct($buf, $dispatcher); + $this->setExtensionHandlers($extensionHandlers); + } + + /** + * Set the host to connect to. + * + * @param string $host + * + * @return Swift_Transport_EsmtpTransport + */ + public function setHost($host) + { + $this->_params['host'] = $host; + + return $this; + } + + /** + * Get the host to connect to. + * + * @return string + */ + public function getHost() + { + return $this->_params['host']; + } + + /** + * Set the port to connect to. + * + * @param int $port + * + * @return Swift_Transport_EsmtpTransport + */ + public function setPort($port) + { + $this->_params['port'] = (int) $port; + + return $this; + } + + /** + * Get the port to connect to. + * + * @return int + */ + public function getPort() + { + return $this->_params['port']; + } + + /** + * Set the connection timeout. + * + * @param int $timeout seconds + * + * @return Swift_Transport_EsmtpTransport + */ + public function setTimeout($timeout) + { + $this->_params['timeout'] = (int) $timeout; + $this->_buffer->setParam('timeout', (int) $timeout); + + return $this; + } + + /** + * Get the connection timeout. + * + * @return int + */ + public function getTimeout() + { + return $this->_params['timeout']; + } + + /** + * Set the encryption type (tls or ssl). + * + * @param string $encryption + * + * @return Swift_Transport_EsmtpTransport + */ + public function setEncryption($encryption) + { + $encryption = strtolower($encryption); + if ('tls' == $encryption) { + $this->_params['protocol'] = 'tcp'; + $this->_params['tls'] = true; + } else { + $this->_params['protocol'] = $encryption; + $this->_params['tls'] = false; + } + + return $this; + } + + /** + * Get the encryption type. + * + * @return string + */ + public function getEncryption() + { + return $this->_params['tls'] ? 'tls' : $this->_params['protocol']; + } + + /** + * Sets the stream context options. + * + * @param array $options + * + * @return Swift_Transport_EsmtpTransport + */ + public function setStreamOptions($options) + { + $this->_params['stream_context_options'] = $options; + + return $this; + } + + /** + * Returns the stream context options. + * + * @return array + */ + public function getStreamOptions() + { + return $this->_params['stream_context_options']; + } + + /** + * Sets the source IP. + * + * @param string $source + * + * @return Swift_Transport_EsmtpTransport + */ + public function setSourceIp($source) + { + $this->_params['sourceIp'] = $source; + + return $this; + } + + /** + * Returns the IP used to connect to the destination. + * + * @return string + */ + public function getSourceIp() + { + return isset($this->_params['sourceIp']) ? $this->_params['sourceIp'] : null; + } + + /** + * Set ESMTP extension handlers. + * + * @param Swift_Transport_EsmtpHandler[] $handlers + * + * @return Swift_Transport_EsmtpTransport + */ + public function setExtensionHandlers(array $handlers) + { + $assoc = array(); + foreach ($handlers as $handler) { + $assoc[$handler->getHandledKeyword()] = $handler; + } + + @uasort($assoc, array($this, '_sortHandlers')); + $this->_handlers = $assoc; + $this->_setHandlerParams(); + + return $this; + } + + /** + * Get ESMTP extension handlers. + * + * @return Swift_Transport_EsmtpHandler[] + */ + public function getExtensionHandlers() + { + return array_values($this->_handlers); + } + + /** + * Run a command against the buffer, expecting the given response codes. + * + * If no response codes are given, the response will not be validated. + * If codes are given, an exception will be thrown on an invalid response. + * + * @param string $command + * @param int[] $codes + * @param string[] $failures An array of failures by-reference + * + * @return string + */ + public function executeCommand($command, $codes = array(), &$failures = null) + { + $failures = (array) $failures; + $stopSignal = false; + $response = null; + foreach ($this->_getActiveHandlers() as $handler) { + $response = $handler->onCommand( + $this, $command, $codes, $failures, $stopSignal + ); + if ($stopSignal) { + return $response; + } + } + + return parent::executeCommand($command, $codes, $failures); + } + + // -- Mixin invocation code + + /** Mixin handling method for ESMTP handlers */ + public function __call($method, $args) + { + foreach ($this->_handlers as $handler) { + if (in_array(strtolower($method), + array_map('strtolower', (array) $handler->exposeMixinMethods()) + )) { + $return = call_user_func_array(array($handler, $method), $args); + // Allow fluid method calls + if (is_null($return) && substr($method, 0, 3) == 'set') { + return $this; + } else { + return $return; + } + } + } + trigger_error('Call to undefined method '.$method, E_USER_ERROR); + } + + /** Get the params to initialize the buffer */ + protected function _getBufferParams() + { + return $this->_params; + } + + /** Overridden to perform EHLO instead */ + protected function _doHeloCommand() + { + try { + $response = $this->executeCommand( + sprintf("EHLO %s\r\n", $this->_domain), array(250) + ); + } catch (Swift_TransportException $e) { + return parent::_doHeloCommand(); + } + + if ($this->_params['tls']) { + try { + $this->executeCommand("STARTTLS\r\n", array(220)); + + if (!$this->_buffer->startTLS()) { + throw new Swift_TransportException('Unable to connect with TLS encryption'); + } + + try { + $response = $this->executeCommand( + sprintf("EHLO %s\r\n", $this->_domain), array(250) + ); + } catch (Swift_TransportException $e) { + return parent::_doHeloCommand(); + } + } catch (Swift_TransportException $e) { + $this->_throwException($e); + } + } + + $this->_capabilities = $this->_getCapabilities($response); + $this->_setHandlerParams(); + foreach ($this->_getActiveHandlers() as $handler) { + $handler->afterEhlo($this); + } + } + + /** Overridden to add Extension support */ + protected function _doMailFromCommand($address) + { + $handlers = $this->_getActiveHandlers(); + $params = array(); + foreach ($handlers as $handler) { + $params = array_merge($params, (array) $handler->getMailParams()); + } + $paramStr = !empty($params) ? ' '.implode(' ', $params) : ''; + $this->executeCommand( + sprintf("MAIL FROM:<%s>%s\r\n", $address, $paramStr), array(250) + ); + } + + /** Overridden to add Extension support */ + protected function _doRcptToCommand($address) + { + $handlers = $this->_getActiveHandlers(); + $params = array(); + foreach ($handlers as $handler) { + $params = array_merge($params, (array) $handler->getRcptParams()); + } + $paramStr = !empty($params) ? ' '.implode(' ', $params) : ''; + $this->executeCommand( + sprintf("RCPT TO:<%s>%s\r\n", $address, $paramStr), array(250, 251, 252) + ); + } + + /** Determine ESMTP capabilities by function group */ + private function _getCapabilities($ehloResponse) + { + $capabilities = array(); + $ehloResponse = trim($ehloResponse); + $lines = explode("\r\n", $ehloResponse); + array_shift($lines); + foreach ($lines as $line) { + if (preg_match('/^[0-9]{3}[ -]([A-Z0-9-]+)((?:[ =].*)?)$/Di', $line, $matches)) { + $keyword = strtoupper($matches[1]); + $paramStr = strtoupper(ltrim($matches[2], ' =')); + $params = !empty($paramStr) ? explode(' ', $paramStr) : array(); + $capabilities[$keyword] = $params; + } + } + + return $capabilities; + } + + /** Set parameters which are used by each extension handler */ + private function _setHandlerParams() + { + foreach ($this->_handlers as $keyword => $handler) { + if (array_key_exists($keyword, $this->_capabilities)) { + $handler->setKeywordParams($this->_capabilities[$keyword]); + } + } + } + + /** Get ESMTP handlers which are currently ok to use */ + private function _getActiveHandlers() + { + $handlers = array(); + foreach ($this->_handlers as $keyword => $handler) { + if (array_key_exists($keyword, $this->_capabilities)) { + $handlers[] = $handler; + } + } + + return $handlers; + } + + /** Custom sort for extension handler ordering */ + private function _sortHandlers($a, $b) + { + return $a->getPriorityOver($b->getHandledKeyword()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/FailoverTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/FailoverTransport.php new file mode 100644 index 0000000000..311a0f2add --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/FailoverTransport.php @@ -0,0 +1,88 @@ +_transports); + $sent = 0; + $this->_lastUsedTransport = null; + + for ($i = 0; $i < $maxTransports + && $transport = $this->_getNextTransport(); ++$i) { + try { + if (!$transport->isStarted()) { + $transport->start(); + } + + if ($sent = $transport->send($message, $failedRecipients)) { + $this->_lastUsedTransport = $transport; + + return $sent; + } + } catch (Swift_TransportException $e) { + $this->_killCurrentTransport(); + } + } + + if (count($this->_transports) == 0) { + throw new Swift_TransportException( + 'All Transports in FailoverTransport failed, or no Transports available' + ); + } + + return $sent; + } + + protected function _getNextTransport() + { + if (!isset($this->_currentTransport)) { + $this->_currentTransport = parent::_getNextTransport(); + } + + return $this->_currentTransport; + } + + protected function _killCurrentTransport() + { + $this->_currentTransport = null; + parent::_killCurrentTransport(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/IoBuffer.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/IoBuffer.php new file mode 100644 index 0000000000..af97adf1f8 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/IoBuffer.php @@ -0,0 +1,67 @@ +_transports = $transports; + $this->_deadTransports = array(); + } + + /** + * Get $transports to delegate to. + * + * @return Swift_Transport[] + */ + public function getTransports() + { + return array_merge($this->_transports, $this->_deadTransports); + } + + /** + * Get the Transport used in the last successful send operation. + * + * @return Swift_Transport + */ + public function getLastUsedTransport() + { + return $this->_lastUsedTransport; + } + + /** + * Test if this Transport mechanism has started. + * + * @return bool + */ + public function isStarted() + { + return count($this->_transports) > 0; + } + + /** + * Start this Transport mechanism. + */ + public function start() + { + $this->_transports = array_merge($this->_transports, $this->_deadTransports); + } + + /** + * Stop this Transport mechanism. + */ + public function stop() + { + foreach ($this->_transports as $transport) { + $transport->stop(); + } + } + + /** + * Send the given Message. + * + * Recipient/sender data will be retrieved from the Message API. + * The return value is the number of recipients who were accepted for delivery. + * + * @param Swift_Mime_Message $message + * @param string[] $failedRecipients An array of failures by-reference + * + * @return int + */ + public function send(Swift_Mime_Message $message, &$failedRecipients = null) + { + $maxTransports = count($this->_transports); + $sent = 0; + $this->_lastUsedTransport = null; + + for ($i = 0; $i < $maxTransports + && $transport = $this->_getNextTransport(); ++$i) { + try { + if (!$transport->isStarted()) { + $transport->start(); + } + if ($sent = $transport->send($message, $failedRecipients)) { + $this->_lastUsedTransport = $transport; + break; + } + } catch (Swift_TransportException $e) { + $this->_killCurrentTransport(); + } + } + + if (count($this->_transports) == 0) { + throw new Swift_TransportException( + 'All Transports in LoadBalancedTransport failed, or no Transports available' + ); + } + + return $sent; + } + + /** + * Register a plugin. + * + * @param Swift_Events_EventListener $plugin + */ + public function registerPlugin(Swift_Events_EventListener $plugin) + { + foreach ($this->_transports as $transport) { + $transport->registerPlugin($plugin); + } + } + + /** + * Rotates the transport list around and returns the first instance. + * + * @return Swift_Transport + */ + protected function _getNextTransport() + { + if ($next = array_shift($this->_transports)) { + $this->_transports[] = $next; + } + + return $next; + } + + /** + * Tag the currently used (top of stack) transport as dead/useless. + */ + protected function _killCurrentTransport() + { + if ($transport = array_pop($this->_transports)) { + try { + $transport->stop(); + } catch (Exception $e) { + } + $this->_deadTransports[] = $transport; + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/MailInvoker.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/MailInvoker.php new file mode 100644 index 0000000000..77489cedc6 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/MailInvoker.php @@ -0,0 +1,32 @@ +_invoker = $invoker; + $this->_eventDispatcher = $eventDispatcher; + } + + /** + * Not used. + */ + public function isStarted() + { + return false; + } + + /** + * Not used. + */ + public function start() + { + } + + /** + * Not used. + */ + public function stop() + { + } + + /** + * Set the additional parameters used on the mail() function. + * + * This string is formatted for sprintf() where %s is the sender address. + * + * @param string $params + * + * @return Swift_Transport_MailTransport + */ + public function setExtraParams($params) + { + $this->_extraParams = $params; + + return $this; + } + + /** + * Get the additional parameters used on the mail() function. + * + * This string is formatted for sprintf() where %s is the sender address. + * + * @return string + */ + public function getExtraParams() + { + return $this->_extraParams; + } + + /** + * Send the given Message. + * + * Recipient/sender data will be retrieved from the Message API. + * The return value is the number of recipients who were accepted for delivery. + * + * @param Swift_Mime_Message $message + * @param string[] $failedRecipients An array of failures by-reference + * + * @return int + */ + public function send(Swift_Mime_Message $message, &$failedRecipients = null) + { + $failedRecipients = (array) $failedRecipients; + + if ($evt = $this->_eventDispatcher->createSendEvent($this, $message)) { + $this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed'); + if ($evt->bubbleCancelled()) { + return 0; + } + } + + $count = ( + count((array) $message->getTo()) + + count((array) $message->getCc()) + + count((array) $message->getBcc()) + ); + + $toHeader = $message->getHeaders()->get('To'); + $subjectHeader = $message->getHeaders()->get('Subject'); + + if (0 === $count) { + $this->_throwException(new Swift_TransportException('Cannot send message without a recipient')); + } + $to = $toHeader ? $toHeader->getFieldBody() : ''; + $subject = $subjectHeader ? $subjectHeader->getFieldBody() : ''; + + $reversePath = $this->_getReversePath($message); + + // Remove headers that would otherwise be duplicated + $message->getHeaders()->remove('To'); + $message->getHeaders()->remove('Subject'); + + $messageStr = $message->toString(); + + if ($toHeader) { + $message->getHeaders()->set($toHeader); + } + $message->getHeaders()->set($subjectHeader); + + // Separate headers from body + if (false !== $endHeaders = strpos($messageStr, "\r\n\r\n")) { + $headers = substr($messageStr, 0, $endHeaders)."\r\n"; //Keep last EOL + $body = substr($messageStr, $endHeaders + 4); + } else { + $headers = $messageStr."\r\n"; + $body = ''; + } + + unset($messageStr); + + if ("\r\n" != PHP_EOL) { + // Non-windows (not using SMTP) + $headers = str_replace("\r\n", PHP_EOL, $headers); + $subject = str_replace("\r\n", PHP_EOL, $subject); + $body = str_replace("\r\n", PHP_EOL, $body); + } else { + // Windows, using SMTP + $headers = str_replace("\r\n.", "\r\n..", $headers); + $subject = str_replace("\r\n.", "\r\n..", $subject); + $body = str_replace("\r\n.", "\r\n..", $body); + } + + if ($this->_invoker->mail($to, $subject, $body, $headers, $this->_formatExtraParams($this->_extraParams, $reversePath))) { + if ($evt) { + $evt->setResult(Swift_Events_SendEvent::RESULT_SUCCESS); + $evt->setFailedRecipients($failedRecipients); + $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed'); + } + } else { + $failedRecipients = array_merge( + $failedRecipients, + array_keys((array) $message->getTo()), + array_keys((array) $message->getCc()), + array_keys((array) $message->getBcc()) + ); + + if ($evt) { + $evt->setResult(Swift_Events_SendEvent::RESULT_FAILED); + $evt->setFailedRecipients($failedRecipients); + $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed'); + } + + $message->generateId(); + + $count = 0; + } + + return $count; + } + + /** + * Register a plugin. + * + * @param Swift_Events_EventListener $plugin + */ + public function registerPlugin(Swift_Events_EventListener $plugin) + { + $this->_eventDispatcher->bindEventListener($plugin); + } + + /** Throw a TransportException, first sending it to any listeners */ + protected function _throwException(Swift_TransportException $e) + { + if ($evt = $this->_eventDispatcher->createTransportExceptionEvent($this, $e)) { + $this->_eventDispatcher->dispatchEvent($evt, 'exceptionThrown'); + if (!$evt->bubbleCancelled()) { + throw $e; + } + } else { + throw $e; + } + } + + /** Determine the best-use reverse path for this message */ + private function _getReversePath(Swift_Mime_Message $message) + { + $return = $message->getReturnPath(); + $sender = $message->getSender(); + $from = $message->getFrom(); + $path = null; + if (!empty($return)) { + $path = $return; + } elseif (!empty($sender)) { + $keys = array_keys($sender); + $path = array_shift($keys); + } elseif (!empty($from)) { + $keys = array_keys($from); + $path = array_shift($keys); + } + + return $path; + } + + /** + * Fix CVE-2016-10074 by disallowing potentially unsafe shell characters. + * + * Note that escapeshellarg and escapeshellcmd are inadequate for our purposes, especially on Windows. + * + * @param string $string The string to be validated + * + * @return bool + */ + private function _isShellSafe($string) + { + // Future-proof + if (escapeshellcmd($string) !== $string || !in_array(escapeshellarg($string), array("'$string'", "\"$string\""))) { + return false; + } + + $length = strlen($string); + for ($i = 0; $i < $length; ++$i) { + $c = $string[$i]; + // All other characters have a special meaning in at least one common shell, including = and +. + // Full stop (.) has a special meaning in cmd.exe, but its impact should be negligible here. + // Note that this does permit non-Latin alphanumeric characters based on the current locale. + if (!ctype_alnum($c) && strpos('@_-.', $c) === false) { + return false; + } + } + + return true; + } + + /** + * Return php mail extra params to use for invoker->mail. + * + * @param $extraParams + * @param $reversePath + * + * @return string|null + */ + private function _formatExtraParams($extraParams, $reversePath) + { + if (false !== strpos($extraParams, '-f%s')) { + if (empty($reversePath) || false === $this->_isShellSafe($reversePath)) { + $extraParams = str_replace('-f%s', '', $extraParams); + } else { + $extraParams = sprintf($extraParams, $reversePath); + } + } + + return !empty($extraParams) ? $extraParams : null; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/NullTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/NullTransport.php new file mode 100644 index 0000000000..ad20e0e535 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/NullTransport.php @@ -0,0 +1,93 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Pretends messages have been sent, but just ignores them. + * + * @author Fabien Potencier + */ +class Swift_Transport_NullTransport implements Swift_Transport +{ + /** The event dispatcher from the plugin API */ + private $_eventDispatcher; + + /** + * Constructor. + */ + public function __construct(Swift_Events_EventDispatcher $eventDispatcher) + { + $this->_eventDispatcher = $eventDispatcher; + } + + /** + * Tests if this Transport mechanism has started. + * + * @return bool + */ + public function isStarted() + { + return true; + } + + /** + * Starts this Transport mechanism. + */ + public function start() + { + } + + /** + * Stops this Transport mechanism. + */ + public function stop() + { + } + + /** + * Sends the given message. + * + * @param Swift_Mime_Message $message + * @param string[] $failedRecipients An array of failures by-reference + * + * @return int The number of sent emails + */ + public function send(Swift_Mime_Message $message, &$failedRecipients = null) + { + if ($evt = $this->_eventDispatcher->createSendEvent($this, $message)) { + $this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed'); + if ($evt->bubbleCancelled()) { + return 0; + } + } + + if ($evt) { + $evt->setResult(Swift_Events_SendEvent::RESULT_SUCCESS); + $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed'); + } + + $count = ( + count((array) $message->getTo()) + + count((array) $message->getCc()) + + count((array) $message->getBcc()) + ); + + return $count; + } + + /** + * Register a plugin. + * + * @param Swift_Events_EventListener $plugin + */ + public function registerPlugin(Swift_Events_EventListener $plugin) + { + $this->_eventDispatcher->bindEventListener($plugin); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SendmailTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SendmailTransport.php new file mode 100644 index 0000000000..34ac4ce3af --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SendmailTransport.php @@ -0,0 +1,160 @@ + 30, + 'blocking' => 1, + 'command' => '/usr/sbin/sendmail -bs', + 'type' => Swift_Transport_IoBuffer::TYPE_PROCESS, + ); + + /** + * Create a new SendmailTransport with $buf for I/O. + * + * @param Swift_Transport_IoBuffer $buf + * @param Swift_Events_EventDispatcher $dispatcher + */ + public function __construct(Swift_Transport_IoBuffer $buf, Swift_Events_EventDispatcher $dispatcher) + { + parent::__construct($buf, $dispatcher); + } + + /** + * Start the standalone SMTP session if running in -bs mode. + */ + public function start() + { + if (false !== strpos($this->getCommand(), ' -bs')) { + parent::start(); + } + } + + /** + * Set the command to invoke. + * + * If using -t mode you are strongly advised to include -oi or -i in the flags. + * For example: /usr/sbin/sendmail -oi -t + * Swift will append a -f flag if one is not present. + * + * The recommended mode is "-bs" since it is interactive and failure notifications + * are hence possible. + * + * @param string $command + * + * @return Swift_Transport_SendmailTransport + */ + public function setCommand($command) + { + $this->_params['command'] = $command; + + return $this; + } + + /** + * Get the sendmail command which will be invoked. + * + * @return string + */ + public function getCommand() + { + return $this->_params['command']; + } + + /** + * Send the given Message. + * + * Recipient/sender data will be retrieved from the Message API. + * + * The return value is the number of recipients who were accepted for delivery. + * NOTE: If using 'sendmail -t' you will not be aware of any failures until + * they bounce (i.e. send() will always return 100% success). + * + * @param Swift_Mime_Message $message + * @param string[] $failedRecipients An array of failures by-reference + * + * @return int + */ + public function send(Swift_Mime_Message $message, &$failedRecipients = null) + { + $failedRecipients = (array) $failedRecipients; + $command = $this->getCommand(); + $buffer = $this->getBuffer(); + $count = 0; + + if (false !== strpos($command, ' -t')) { + if ($evt = $this->_eventDispatcher->createSendEvent($this, $message)) { + $this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed'); + if ($evt->bubbleCancelled()) { + return 0; + } + } + + if (false === strpos($command, ' -f')) { + $command .= ' -f'.escapeshellarg($this->_getReversePath($message)); + } + + $buffer->initialize(array_merge($this->_params, array('command' => $command))); + + if (false === strpos($command, ' -i') && false === strpos($command, ' -oi')) { + $buffer->setWriteTranslations(array("\r\n" => "\n", "\n." => "\n..")); + } else { + $buffer->setWriteTranslations(array("\r\n" => "\n")); + } + + $count = count((array) $message->getTo()) + + count((array) $message->getCc()) + + count((array) $message->getBcc()) + ; + $message->toByteStream($buffer); + $buffer->flushBuffers(); + $buffer->setWriteTranslations(array()); + $buffer->terminate(); + + if ($evt) { + $evt->setResult(Swift_Events_SendEvent::RESULT_SUCCESS); + $evt->setFailedRecipients($failedRecipients); + $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed'); + } + + $message->generateId(); + } elseif (false !== strpos($command, ' -bs')) { + $count = parent::send($message, $failedRecipients); + } else { + $this->_throwException(new Swift_TransportException( + 'Unsupported sendmail command flags ['.$command.']. '. + 'Must be one of "-bs" or "-t" but can include additional flags.' + )); + } + + return $count; + } + + /** Get the params to initialize the buffer */ + protected function _getBufferParams() + { + return $this->_params; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SimpleMailInvoker.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SimpleMailInvoker.php new file mode 100644 index 0000000000..4cab66bd6b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SimpleMailInvoker.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Stores Messages in a queue. + * + * @author Fabien Potencier + */ +class Swift_Transport_SpoolTransport implements Swift_Transport +{ + /** The spool instance */ + private $_spool; + + /** The event dispatcher from the plugin API */ + private $_eventDispatcher; + + /** + * Constructor. + */ + public function __construct(Swift_Events_EventDispatcher $eventDispatcher, Swift_Spool $spool = null) + { + $this->_eventDispatcher = $eventDispatcher; + $this->_spool = $spool; + } + + /** + * Sets the spool object. + * + * @param Swift_Spool $spool + * + * @return Swift_Transport_SpoolTransport + */ + public function setSpool(Swift_Spool $spool) + { + $this->_spool = $spool; + + return $this; + } + + /** + * Get the spool object. + * + * @return Swift_Spool + */ + public function getSpool() + { + return $this->_spool; + } + + /** + * Tests if this Transport mechanism has started. + * + * @return bool + */ + public function isStarted() + { + return true; + } + + /** + * Starts this Transport mechanism. + */ + public function start() + { + } + + /** + * Stops this Transport mechanism. + */ + public function stop() + { + } + + /** + * Sends the given message. + * + * @param Swift_Mime_Message $message + * @param string[] $failedRecipients An array of failures by-reference + * + * @return int The number of sent e-mail's + */ + public function send(Swift_Mime_Message $message, &$failedRecipients = null) + { + if ($evt = $this->_eventDispatcher->createSendEvent($this, $message)) { + $this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed'); + if ($evt->bubbleCancelled()) { + return 0; + } + } + + $success = $this->_spool->queueMessage($message); + + if ($evt) { + $evt->setResult($success ? Swift_Events_SendEvent::RESULT_SPOOLED : Swift_Events_SendEvent::RESULT_FAILED); + $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed'); + } + + return 1; + } + + /** + * Register a plugin. + * + * @param Swift_Events_EventListener $plugin + */ + public function registerPlugin(Swift_Events_EventListener $plugin) + { + $this->_eventDispatcher->bindEventListener($plugin); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/StreamBuffer.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/StreamBuffer.php new file mode 100644 index 0000000000..5134ea48a4 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/StreamBuffer.php @@ -0,0 +1,325 @@ +_replacementFactory = $replacementFactory; + } + + /** + * Perform any initialization needed, using the given $params. + * + * Parameters will vary depending upon the type of IoBuffer used. + * + * @param array $params + */ + public function initialize(array $params) + { + $this->_params = $params; + switch ($params['type']) { + case self::TYPE_PROCESS: + $this->_establishProcessConnection(); + break; + case self::TYPE_SOCKET: + default: + $this->_establishSocketConnection(); + break; + } + } + + /** + * Set an individual param on the buffer (e.g. switching to SSL). + * + * @param string $param + * @param mixed $value + */ + public function setParam($param, $value) + { + if (isset($this->_stream)) { + switch ($param) { + case 'timeout': + if ($this->_stream) { + stream_set_timeout($this->_stream, $value); + } + break; + + case 'blocking': + if ($this->_stream) { + stream_set_blocking($this->_stream, 1); + } + + } + } + $this->_params[$param] = $value; + } + + public function startTLS() + { + return stream_socket_enable_crypto($this->_stream, true, STREAM_CRYPTO_METHOD_TLS_CLIENT); + } + + /** + * Perform any shutdown logic needed. + */ + public function terminate() + { + if (isset($this->_stream)) { + switch ($this->_params['type']) { + case self::TYPE_PROCESS: + fclose($this->_in); + fclose($this->_out); + proc_close($this->_stream); + break; + case self::TYPE_SOCKET: + default: + fclose($this->_stream); + break; + } + } + $this->_stream = null; + $this->_out = null; + $this->_in = null; + } + + /** + * Set an array of string replacements which should be made on data written + * to the buffer. + * + * This could replace LF with CRLF for example. + * + * @param string[] $replacements + */ + public function setWriteTranslations(array $replacements) + { + foreach ($this->_translations as $search => $replace) { + if (!isset($replacements[$search])) { + $this->removeFilter($search); + unset($this->_translations[$search]); + } + } + + foreach ($replacements as $search => $replace) { + if (!isset($this->_translations[$search])) { + $this->addFilter( + $this->_replacementFactory->createFilter($search, $replace), $search + ); + $this->_translations[$search] = true; + } + } + } + + /** + * Get a line of output (including any CRLF). + * + * The $sequence number comes from any writes and may or may not be used + * depending upon the implementation. + * + * @param int $sequence of last write to scan from + * + * @throws Swift_IoException + * + * @return string + */ + public function readLine($sequence) + { + if (isset($this->_out) && !feof($this->_out)) { + $line = fgets($this->_out); + if (strlen($line) == 0) { + $metas = stream_get_meta_data($this->_out); + if ($metas['timed_out']) { + throw new Swift_IoException( + 'Connection to '. + $this->_getReadConnectionDescription(). + ' Timed Out' + ); + } + } + + return $line; + } + } + + /** + * Reads $length bytes from the stream into a string and moves the pointer + * through the stream by $length. + * + * If less bytes exist than are requested the remaining bytes are given instead. + * If no bytes are remaining at all, boolean false is returned. + * + * @param int $length + * + * @throws Swift_IoException + * + * @return string|bool + */ + public function read($length) + { + if (isset($this->_out) && !feof($this->_out)) { + $ret = fread($this->_out, $length); + if (strlen($ret) == 0) { + $metas = stream_get_meta_data($this->_out); + if ($metas['timed_out']) { + throw new Swift_IoException( + 'Connection to '. + $this->_getReadConnectionDescription(). + ' Timed Out' + ); + } + } + + return $ret; + } + } + + /** Not implemented */ + public function setReadPointer($byteOffset) + { + } + + /** Flush the stream contents */ + protected function _flush() + { + if (isset($this->_in)) { + fflush($this->_in); + } + } + + /** Write this bytes to the stream */ + protected function _commit($bytes) + { + if (isset($this->_in)) { + $bytesToWrite = strlen($bytes); + $totalBytesWritten = 0; + + while ($totalBytesWritten < $bytesToWrite) { + $bytesWritten = fwrite($this->_in, substr($bytes, $totalBytesWritten)); + if (false === $bytesWritten || 0 === $bytesWritten) { + break; + } + + $totalBytesWritten += $bytesWritten; + } + + if ($totalBytesWritten > 0) { + return ++$this->_sequence; + } + } + } + + /** + * Establishes a connection to a remote server. + */ + private function _establishSocketConnection() + { + $host = $this->_params['host']; + if (!empty($this->_params['protocol'])) { + $host = $this->_params['protocol'].'://'.$host; + } + $timeout = 15; + if (!empty($this->_params['timeout'])) { + $timeout = $this->_params['timeout']; + } + $options = array(); + if (!empty($this->_params['sourceIp'])) { + $options['socket']['bindto'] = $this->_params['sourceIp'].':0'; + } + if (isset($this->_params['stream_context_options'])) { + $options = array_merge($options, $this->_params['stream_context_options']); + } + $streamContext = stream_context_create($options); + $this->_stream = @stream_socket_client($host.':'.$this->_params['port'], $errno, $errstr, $timeout, STREAM_CLIENT_CONNECT, $streamContext); + if (false === $this->_stream) { + throw new Swift_TransportException( + 'Connection could not be established with host '.$this->_params['host']. + ' ['.$errstr.' #'.$errno.']' + ); + } + if (!empty($this->_params['blocking'])) { + stream_set_blocking($this->_stream, 1); + } else { + stream_set_blocking($this->_stream, 0); + } + stream_set_timeout($this->_stream, $timeout); + $this->_in = &$this->_stream; + $this->_out = &$this->_stream; + } + + /** + * Opens a process for input/output. + */ + private function _establishProcessConnection() + { + $command = $this->_params['command']; + $descriptorSpec = array( + 0 => array('pipe', 'r'), + 1 => array('pipe', 'w'), + 2 => array('pipe', 'w'), + ); + $this->_stream = proc_open($command, $descriptorSpec, $pipes); + stream_set_blocking($pipes[2], 0); + if ($err = stream_get_contents($pipes[2])) { + throw new Swift_TransportException( + 'Process could not be started ['.$err.']' + ); + } + $this->_in = &$pipes[0]; + $this->_out = &$pipes[1]; + } + + private function _getReadConnectionDescription() + { + switch ($this->_params['type']) { + case self::TYPE_PROCESS: + return 'Process '.$this->_params['command']; + break; + + case self::TYPE_SOCKET: + default: + $host = $this->_params['host']; + if (!empty($this->_params['protocol'])) { + $host = $this->_params['protocol'].'://'.$host; + } + $host .= ':'.$this->_params['port']; + + return $host; + break; + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/TransportException.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/TransportException.php new file mode 100644 index 0000000000..4ae2412e62 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/TransportException.php @@ -0,0 +1,29 @@ + + */ +class Swift_Validate +{ + /** + * Grammar Object. + * + * @var Swift_Mime_Grammar + */ + private static $grammar = null; + + /** + * Checks if an e-mail address matches the current grammars. + * + * @param string $email + * + * @return bool + */ + public static function email($email) + { + if (self::$grammar === null) { + self::$grammar = Swift_DependencyContainer::getInstance() + ->lookup('mime.grammar'); + } + + return (bool) preg_match( + '/^'.self::$grammar->getDefinition('addr-spec').'$/D', + $email + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/dependency_maps/cache_deps.php b/vendor/swiftmailer/swiftmailer/lib/dependency_maps/cache_deps.php new file mode 100644 index 0000000000..6023448e85 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/dependency_maps/cache_deps.php @@ -0,0 +1,23 @@ +register('cache') + ->asAliasOf('cache.array') + + ->register('tempdir') + ->asValue('/tmp') + + ->register('cache.null') + ->asSharedInstanceOf('Swift_KeyCache_NullKeyCache') + + ->register('cache.array') + ->asSharedInstanceOf('Swift_KeyCache_ArrayKeyCache') + ->withDependencies(array('cache.inputstream')) + + ->register('cache.disk') + ->asSharedInstanceOf('Swift_KeyCache_DiskKeyCache') + ->withDependencies(array('cache.inputstream', 'tempdir')) + + ->register('cache.inputstream') + ->asNewInstanceOf('Swift_KeyCache_SimpleKeyCacheInputStream') +; diff --git a/vendor/swiftmailer/swiftmailer/lib/dependency_maps/message_deps.php b/vendor/swiftmailer/swiftmailer/lib/dependency_maps/message_deps.php new file mode 100644 index 0000000000..64d69d2159 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/dependency_maps/message_deps.php @@ -0,0 +1,9 @@ +register('message.message') + ->asNewInstanceOf('Swift_Message') + + ->register('message.mimepart') + ->asNewInstanceOf('Swift_MimePart') +; diff --git a/vendor/swiftmailer/swiftmailer/lib/dependency_maps/mime_deps.php b/vendor/swiftmailer/swiftmailer/lib/dependency_maps/mime_deps.php new file mode 100644 index 0000000000..56169c9284 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/dependency_maps/mime_deps.php @@ -0,0 +1,123 @@ +register('properties.charset') + ->asValue('utf-8') + + ->register('mime.grammar') + ->asSharedInstanceOf('Swift_Mime_Grammar') + + ->register('mime.message') + ->asNewInstanceOf('Swift_Mime_SimpleMessage') + ->withDependencies(array( + 'mime.headerset', + 'mime.qpcontentencoder', + 'cache', + 'mime.grammar', + 'properties.charset', + )) + + ->register('mime.part') + ->asNewInstanceOf('Swift_Mime_MimePart') + ->withDependencies(array( + 'mime.headerset', + 'mime.qpcontentencoder', + 'cache', + 'mime.grammar', + 'properties.charset', + )) + + ->register('mime.attachment') + ->asNewInstanceOf('Swift_Mime_Attachment') + ->withDependencies(array( + 'mime.headerset', + 'mime.base64contentencoder', + 'cache', + 'mime.grammar', + )) + ->addConstructorValue($swift_mime_types) + + ->register('mime.embeddedfile') + ->asNewInstanceOf('Swift_Mime_EmbeddedFile') + ->withDependencies(array( + 'mime.headerset', + 'mime.base64contentencoder', + 'cache', + 'mime.grammar', + )) + ->addConstructorValue($swift_mime_types) + + ->register('mime.headerfactory') + ->asNewInstanceOf('Swift_Mime_SimpleHeaderFactory') + ->withDependencies(array( + 'mime.qpheaderencoder', + 'mime.rfc2231encoder', + 'mime.grammar', + 'properties.charset', + )) + + ->register('mime.headerset') + ->asNewInstanceOf('Swift_Mime_SimpleHeaderSet') + ->withDependencies(array('mime.headerfactory', 'properties.charset')) + + ->register('mime.qpheaderencoder') + ->asNewInstanceOf('Swift_Mime_HeaderEncoder_QpHeaderEncoder') + ->withDependencies(array('mime.charstream')) + + ->register('mime.base64headerencoder') + ->asNewInstanceOf('Swift_Mime_HeaderEncoder_Base64HeaderEncoder') + ->withDependencies(array('mime.charstream')) + + ->register('mime.charstream') + ->asNewInstanceOf('Swift_CharacterStream_NgCharacterStream') + ->withDependencies(array('mime.characterreaderfactory', 'properties.charset')) + + ->register('mime.bytecanonicalizer') + ->asSharedInstanceOf('Swift_StreamFilters_ByteArrayReplacementFilter') + ->addConstructorValue(array(array(0x0D, 0x0A), array(0x0D), array(0x0A))) + ->addConstructorValue(array(array(0x0A), array(0x0A), array(0x0D, 0x0A))) + + ->register('mime.characterreaderfactory') + ->asSharedInstanceOf('Swift_CharacterReaderFactory_SimpleCharacterReaderFactory') + + ->register('mime.safeqpcontentencoder') + ->asNewInstanceOf('Swift_Mime_ContentEncoder_QpContentEncoder') + ->withDependencies(array('mime.charstream', 'mime.bytecanonicalizer')) + + ->register('mime.rawcontentencoder') + ->asNewInstanceOf('Swift_Mime_ContentEncoder_RawContentEncoder') + + ->register('mime.nativeqpcontentencoder') + ->withDependencies(array('properties.charset')) + ->asNewInstanceOf('Swift_Mime_ContentEncoder_NativeQpContentEncoder') + + ->register('mime.qpcontentencoderproxy') + ->asNewInstanceOf('Swift_Mime_ContentEncoder_QpContentEncoderProxy') + ->withDependencies(array('mime.safeqpcontentencoder', 'mime.nativeqpcontentencoder', 'properties.charset')) + + ->register('mime.7bitcontentencoder') + ->asNewInstanceOf('Swift_Mime_ContentEncoder_PlainContentEncoder') + ->addConstructorValue('7bit') + ->addConstructorValue(true) + + ->register('mime.8bitcontentencoder') + ->asNewInstanceOf('Swift_Mime_ContentEncoder_PlainContentEncoder') + ->addConstructorValue('8bit') + ->addConstructorValue(true) + + ->register('mime.base64contentencoder') + ->asSharedInstanceOf('Swift_Mime_ContentEncoder_Base64ContentEncoder') + + ->register('mime.rfc2231encoder') + ->asNewInstanceOf('Swift_Encoder_Rfc2231Encoder') + ->withDependencies(array('mime.charstream')) + + // As of PHP 5.4.7, the quoted_printable_encode() function behaves correctly. + // see https://github.com/php/php-src/commit/18bb426587d62f93c54c40bf8535eb8416603629 + ->register('mime.qpcontentencoder') + ->asAliasOf(version_compare(phpversion(), '5.4.7', '>=') ? 'mime.qpcontentencoderproxy' : 'mime.safeqpcontentencoder') +; + +unset($swift_mime_types); diff --git a/vendor/swiftmailer/swiftmailer/lib/dependency_maps/transport_deps.php b/vendor/swiftmailer/swiftmailer/lib/dependency_maps/transport_deps.php new file mode 100644 index 0000000000..77e432cfac --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/dependency_maps/transport_deps.php @@ -0,0 +1,76 @@ +register('transport.smtp') + ->asNewInstanceOf('Swift_Transport_EsmtpTransport') + ->withDependencies(array( + 'transport.buffer', + array('transport.authhandler'), + 'transport.eventdispatcher', + )) + + ->register('transport.sendmail') + ->asNewInstanceOf('Swift_Transport_SendmailTransport') + ->withDependencies(array( + 'transport.buffer', + 'transport.eventdispatcher', + )) + + ->register('transport.mail') + ->asNewInstanceOf('Swift_Transport_MailTransport') + ->withDependencies(array('transport.mailinvoker', 'transport.eventdispatcher')) + + ->register('transport.loadbalanced') + ->asNewInstanceOf('Swift_Transport_LoadBalancedTransport') + + ->register('transport.failover') + ->asNewInstanceOf('Swift_Transport_FailoverTransport') + + ->register('transport.spool') + ->asNewInstanceOf('Swift_Transport_SpoolTransport') + ->withDependencies(array('transport.eventdispatcher')) + + ->register('transport.null') + ->asNewInstanceOf('Swift_Transport_NullTransport') + ->withDependencies(array('transport.eventdispatcher')) + + ->register('transport.mailinvoker') + ->asSharedInstanceOf('Swift_Transport_SimpleMailInvoker') + + ->register('transport.buffer') + ->asNewInstanceOf('Swift_Transport_StreamBuffer') + ->withDependencies(array('transport.replacementfactory')) + + ->register('transport.authhandler') + ->asNewInstanceOf('Swift_Transport_Esmtp_AuthHandler') + ->withDependencies(array( + array( + 'transport.crammd5auth', + 'transport.loginauth', + 'transport.plainauth', + 'transport.ntlmauth', + 'transport.xoauth2auth', + ), + )) + + ->register('transport.crammd5auth') + ->asNewInstanceOf('Swift_Transport_Esmtp_Auth_CramMd5Authenticator') + + ->register('transport.loginauth') + ->asNewInstanceOf('Swift_Transport_Esmtp_Auth_LoginAuthenticator') + + ->register('transport.plainauth') + ->asNewInstanceOf('Swift_Transport_Esmtp_Auth_PlainAuthenticator') + + ->register('transport.xoauth2auth') + ->asNewInstanceOf('Swift_Transport_Esmtp_Auth_XOAuth2Authenticator') + + ->register('transport.ntlmauth') + ->asNewInstanceOf('Swift_Transport_Esmtp_Auth_NTLMAuthenticator') + + ->register('transport.eventdispatcher') + ->asNewInstanceOf('Swift_Events_SimpleEventDispatcher') + + ->register('transport.replacementfactory') + ->asSharedInstanceOf('Swift_StreamFilters_StringReplacementFilterFactory') +; diff --git a/vendor/swiftmailer/swiftmailer/lib/mime_types.php b/vendor/swiftmailer/swiftmailer/lib/mime_types.php new file mode 100644 index 0000000000..2d7b98dc1a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/mime_types.php @@ -0,0 +1,1007 @@ + 'text/vnd.in3d.3dml', + '3ds' => 'image/x-3ds', + '3g2' => 'video/3gpp2', + '3gp' => 'video/3gpp', + '7z' => 'application/x-7z-compressed', + 'aab' => 'application/x-authorware-bin', + 'aac' => 'audio/x-aac', + 'aam' => 'application/x-authorware-map', + 'aas' => 'application/x-authorware-seg', + 'abw' => 'application/x-abiword', + 'ac' => 'application/pkix-attr-cert', + 'acc' => 'application/vnd.americandynamics.acc', + 'ace' => 'application/x-ace-compressed', + 'acu' => 'application/vnd.acucobol', + 'acutc' => 'application/vnd.acucorp', + 'adp' => 'audio/adpcm', + 'aep' => 'application/vnd.audiograph', + 'afm' => 'application/x-font-type1', + 'afp' => 'application/vnd.ibm.modcap', + 'ahead' => 'application/vnd.ahead.space', + 'ai' => 'application/postscript', + 'aif' => 'audio/x-aiff', + 'aifc' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', + 'air' => 'application/vnd.adobe.air-application-installer-package+zip', + 'ait' => 'application/vnd.dvb.ait', + 'ami' => 'application/vnd.amiga.ami', + 'apk' => 'application/vnd.android.package-archive', + 'appcache' => 'text/cache-manifest', + 'apr' => 'application/vnd.lotus-approach', + 'aps' => 'application/postscript', + 'arc' => 'application/x-freearc', + 'asc' => 'application/pgp-signature', + 'asf' => 'video/x-ms-asf', + 'asm' => 'text/x-asm', + 'aso' => 'application/vnd.accpac.simply.aso', + 'asx' => 'video/x-ms-asf', + 'atc' => 'application/vnd.acucorp', + 'atom' => 'application/atom+xml', + 'atomcat' => 'application/atomcat+xml', + 'atomsvc' => 'application/atomsvc+xml', + 'atx' => 'application/vnd.antix.game-component', + 'au' => 'audio/basic', + 'avi' => 'video/x-msvideo', + 'aw' => 'application/applixware', + 'azf' => 'application/vnd.airzip.filesecure.azf', + 'azs' => 'application/vnd.airzip.filesecure.azs', + 'azw' => 'application/vnd.amazon.ebook', + 'bat' => 'application/x-msdownload', + 'bcpio' => 'application/x-bcpio', + 'bdf' => 'application/x-font-bdf', + 'bdm' => 'application/vnd.syncml.dm+wbxml', + 'bed' => 'application/vnd.realvnc.bed', + 'bh2' => 'application/vnd.fujitsu.oasysprs', + 'bin' => 'application/octet-stream', + 'blb' => 'application/x-blorb', + 'blorb' => 'application/x-blorb', + 'bmi' => 'application/vnd.bmi', + 'bmp' => 'image/bmp', + 'book' => 'application/vnd.framemaker', + 'box' => 'application/vnd.previewsystems.box', + 'boz' => 'application/x-bzip2', + 'bpk' => 'application/octet-stream', + 'btif' => 'image/prs.btif', + 'bz' => 'application/x-bzip', + 'bz2' => 'application/x-bzip2', + 'c' => 'text/x-c', + 'c11amc' => 'application/vnd.cluetrust.cartomobile-config', + 'c11amz' => 'application/vnd.cluetrust.cartomobile-config-pkg', + 'c4d' => 'application/vnd.clonk.c4group', + 'c4f' => 'application/vnd.clonk.c4group', + 'c4g' => 'application/vnd.clonk.c4group', + 'c4p' => 'application/vnd.clonk.c4group', + 'c4u' => 'application/vnd.clonk.c4group', + 'cab' => 'application/vnd.ms-cab-compressed', + 'caf' => 'audio/x-caf', + 'cap' => 'application/vnd.tcpdump.pcap', + 'car' => 'application/vnd.curl.car', + 'cat' => 'application/vnd.ms-pki.seccat', + 'cb7' => 'application/x-cbr', + 'cba' => 'application/x-cbr', + 'cbr' => 'application/x-cbr', + 'cbt' => 'application/x-cbr', + 'cbz' => 'application/x-cbr', + 'cc' => 'text/x-c', + 'cct' => 'application/x-director', + 'ccxml' => 'application/ccxml+xml', + 'cdbcmsg' => 'application/vnd.contact.cmsg', + 'cdf' => 'application/x-netcdf', + 'cdkey' => 'application/vnd.mediastation.cdkey', + 'cdmia' => 'application/cdmi-capability', + 'cdmic' => 'application/cdmi-container', + 'cdmid' => 'application/cdmi-domain', + 'cdmio' => 'application/cdmi-object', + 'cdmiq' => 'application/cdmi-queue', + 'cdx' => 'chemical/x-cdx', + 'cdxml' => 'application/vnd.chemdraw+xml', + 'cdy' => 'application/vnd.cinderella', + 'cer' => 'application/pkix-cert', + 'cfs' => 'application/x-cfs-compressed', + 'cgm' => 'image/cgm', + 'chat' => 'application/x-chat', + 'chm' => 'application/vnd.ms-htmlhelp', + 'chrt' => 'application/vnd.kde.kchart', + 'cif' => 'chemical/x-cif', + 'cii' => 'application/vnd.anser-web-certificate-issue-initiation', + 'cil' => 'application/vnd.ms-artgalry', + 'cla' => 'application/vnd.claymore', + 'class' => 'application/java-vm', + 'clkk' => 'application/vnd.crick.clicker.keyboard', + 'clkp' => 'application/vnd.crick.clicker.palette', + 'clkt' => 'application/vnd.crick.clicker.template', + 'clkw' => 'application/vnd.crick.clicker.wordbank', + 'clkx' => 'application/vnd.crick.clicker', + 'clp' => 'application/x-msclip', + 'cmc' => 'application/vnd.cosmocaller', + 'cmdf' => 'chemical/x-cmdf', + 'cml' => 'chemical/x-cml', + 'cmp' => 'application/vnd.yellowriver-custom-menu', + 'cmx' => 'image/x-cmx', + 'cod' => 'application/vnd.rim.cod', + 'com' => 'application/x-msdownload', + 'conf' => 'text/plain', + 'cpio' => 'application/x-cpio', + 'cpp' => 'text/x-c', + 'cpt' => 'application/mac-compactpro', + 'crd' => 'application/x-mscardfile', + 'crl' => 'application/pkix-crl', + 'crt' => 'application/x-x509-ca-cert', + 'csh' => 'application/x-csh', + 'csml' => 'chemical/x-csml', + 'csp' => 'application/vnd.commonspace', + 'css' => 'text/css', + 'cst' => 'application/x-director', + 'csv' => 'text/csv', + 'cu' => 'application/cu-seeme', + 'curl' => 'text/vnd.curl', + 'cww' => 'application/prs.cww', + 'cxt' => 'application/x-director', + 'cxx' => 'text/x-c', + 'dae' => 'model/vnd.collada+xml', + 'daf' => 'application/vnd.mobius.daf', + 'dart' => 'application/vnd.dart', + 'dataless' => 'application/vnd.fdsn.seed', + 'davmount' => 'application/davmount+xml', + 'dbk' => 'application/docbook+xml', + 'dcr' => 'application/x-director', + 'dcurl' => 'text/vnd.curl.dcurl', + 'dd2' => 'application/vnd.oma.dd2+xml', + 'ddd' => 'application/vnd.fujixerox.ddd', + 'deb' => 'application/x-debian-package', + 'def' => 'text/plain', + 'deploy' => 'application/octet-stream', + 'der' => 'application/x-x509-ca-cert', + 'dfac' => 'application/vnd.dreamfactory', + 'dgc' => 'application/x-dgc-compressed', + 'dic' => 'text/x-c', + 'dir' => 'application/x-director', + 'dis' => 'application/vnd.mobius.dis', + 'dist' => 'application/octet-stream', + 'distz' => 'application/octet-stream', + 'djv' => 'image/vnd.djvu', + 'djvu' => 'image/vnd.djvu', + 'dll' => 'application/x-msdownload', + 'dmg' => 'application/x-apple-diskimage', + 'dmp' => 'application/vnd.tcpdump.pcap', + 'dms' => 'application/octet-stream', + 'dna' => 'application/vnd.dna', + 'doc' => 'application/msword', + 'docm' => 'application/vnd.ms-word.document.macroenabled.12', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'dot' => 'application/msword', + 'dotm' => 'application/vnd.ms-word.template.macroenabled.12', + 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', + 'dp' => 'application/vnd.osgi.dp', + 'dpg' => 'application/vnd.dpgraph', + 'dra' => 'audio/vnd.dra', + 'dsc' => 'text/prs.lines.tag', + 'dssc' => 'application/dssc+der', + 'dtb' => 'application/x-dtbook+xml', + 'dtd' => 'application/xml-dtd', + 'dts' => 'audio/vnd.dts', + 'dtshd' => 'audio/vnd.dts.hd', + 'dump' => 'application/octet-stream', + 'dvb' => 'video/vnd.dvb.file', + 'dvi' => 'application/x-dvi', + 'dwf' => 'model/vnd.dwf', + 'dwg' => 'image/vnd.dwg', + 'dxf' => 'image/vnd.dxf', + 'dxp' => 'application/vnd.spotfire.dxp', + 'dxr' => 'application/x-director', + 'ecelp4800' => 'audio/vnd.nuera.ecelp4800', + 'ecelp7470' => 'audio/vnd.nuera.ecelp7470', + 'ecelp9600' => 'audio/vnd.nuera.ecelp9600', + 'ecma' => 'application/ecmascript', + 'edm' => 'application/vnd.novadigm.edm', + 'edx' => 'application/vnd.novadigm.edx', + 'efif' => 'application/vnd.picsel', + 'ei6' => 'application/vnd.pg.osasli', + 'elc' => 'application/octet-stream', + 'emf' => 'application/x-msmetafile', + 'eml' => 'message/rfc822', + 'emma' => 'application/emma+xml', + 'emz' => 'application/x-msmetafile', + 'eol' => 'audio/vnd.digital-winds', + 'eot' => 'application/vnd.ms-fontobject', + 'eps' => 'application/postscript', + 'epub' => 'application/epub+zip', + 'es3' => 'application/vnd.eszigno3+xml', + 'esa' => 'application/vnd.osgi.subsystem', + 'esf' => 'application/vnd.epson.esf', + 'et3' => 'application/vnd.eszigno3+xml', + 'etx' => 'text/x-setext', + 'eva' => 'application/x-eva', + 'evy' => 'application/x-envoy', + 'exe' => 'application/x-msdownload', + 'exi' => 'application/exi', + 'ext' => 'application/vnd.novadigm.ext', + 'ez' => 'application/andrew-inset', + 'ez2' => 'application/vnd.ezpix-album', + 'ez3' => 'application/vnd.ezpix-package', + 'f' => 'text/x-fortran', + 'f4v' => 'video/x-f4v', + 'f77' => 'text/x-fortran', + 'f90' => 'text/x-fortran', + 'fbs' => 'image/vnd.fastbidsheet', + 'fcdt' => 'application/vnd.adobe.formscentral.fcdt', + 'fcs' => 'application/vnd.isac.fcs', + 'fdf' => 'application/vnd.fdf', + 'fe_launch' => 'application/vnd.denovo.fcselayout-link', + 'fg5' => 'application/vnd.fujitsu.oasysgp', + 'fgd' => 'application/x-director', + 'fh' => 'image/x-freehand', + 'fh4' => 'image/x-freehand', + 'fh5' => 'image/x-freehand', + 'fh7' => 'image/x-freehand', + 'fhc' => 'image/x-freehand', + 'fig' => 'application/x-xfig', + 'flac' => 'audio/x-flac', + 'fli' => 'video/x-fli', + 'flo' => 'application/vnd.micrografx.flo', + 'flv' => 'video/x-flv', + 'flw' => 'application/vnd.kde.kivio', + 'flx' => 'text/vnd.fmi.flexstor', + 'fly' => 'text/vnd.fly', + 'fm' => 'application/vnd.framemaker', + 'fnc' => 'application/vnd.frogans.fnc', + 'for' => 'text/x-fortran', + 'fpx' => 'image/vnd.fpx', + 'frame' => 'application/vnd.framemaker', + 'fsc' => 'application/vnd.fsc.weblaunch', + 'fst' => 'image/vnd.fst', + 'ftc' => 'application/vnd.fluxtime.clip', + 'fti' => 'application/vnd.anser-web-funds-transfer-initiation', + 'fvt' => 'video/vnd.fvt', + 'fxp' => 'application/vnd.adobe.fxp', + 'fxpl' => 'application/vnd.adobe.fxp', + 'fzs' => 'application/vnd.fuzzysheet', + 'g2w' => 'application/vnd.geoplan', + 'g3' => 'image/g3fax', + 'g3w' => 'application/vnd.geospace', + 'gac' => 'application/vnd.groove-account', + 'gam' => 'application/x-tads', + 'gbr' => 'application/rpki-ghostbusters', + 'gca' => 'application/x-gca-compressed', + 'gdl' => 'model/vnd.gdl', + 'geo' => 'application/vnd.dynageo', + 'gex' => 'application/vnd.geometry-explorer', + 'ggb' => 'application/vnd.geogebra.file', + 'ggt' => 'application/vnd.geogebra.tool', + 'ghf' => 'application/vnd.groove-help', + 'gif' => 'image/gif', + 'gim' => 'application/vnd.groove-identity-message', + 'gml' => 'application/gml+xml', + 'gmx' => 'application/vnd.gmx', + 'gnumeric' => 'application/x-gnumeric', + 'gph' => 'application/vnd.flographit', + 'gpx' => 'application/gpx+xml', + 'gqf' => 'application/vnd.grafeq', + 'gqs' => 'application/vnd.grafeq', + 'gram' => 'application/srgs', + 'gramps' => 'application/x-gramps-xml', + 'gre' => 'application/vnd.geometry-explorer', + 'grv' => 'application/vnd.groove-injector', + 'grxml' => 'application/srgs+xml', + 'gsf' => 'application/x-font-ghostscript', + 'gtar' => 'application/x-gtar', + 'gtm' => 'application/vnd.groove-tool-message', + 'gtw' => 'model/vnd.gtw', + 'gv' => 'text/vnd.graphviz', + 'gxf' => 'application/gxf', + 'gxt' => 'application/vnd.geonext', + 'gz' => 'application/x-gzip', + 'h' => 'text/x-c', + 'h261' => 'video/h261', + 'h263' => 'video/h263', + 'h264' => 'video/h264', + 'hal' => 'application/vnd.hal+xml', + 'hbci' => 'application/vnd.hbci', + 'hdf' => 'application/x-hdf', + 'hh' => 'text/x-c', + 'hlp' => 'application/winhlp', + 'hpgl' => 'application/vnd.hp-hpgl', + 'hpid' => 'application/vnd.hp-hpid', + 'hps' => 'application/vnd.hp-hps', + 'hqx' => 'application/mac-binhex40', + 'htke' => 'application/vnd.kenameaapp', + 'htm' => 'text/html', + 'html' => 'text/html', + 'hvd' => 'application/vnd.yamaha.hv-dic', + 'hvp' => 'application/vnd.yamaha.hv-voice', + 'hvs' => 'application/vnd.yamaha.hv-script', + 'i2g' => 'application/vnd.intergeo', + 'icc' => 'application/vnd.iccprofile', + 'ice' => 'x-conference/x-cooltalk', + 'icm' => 'application/vnd.iccprofile', + 'ico' => 'image/x-icon', + 'ics' => 'text/calendar', + 'ief' => 'image/ief', + 'ifb' => 'text/calendar', + 'ifm' => 'application/vnd.shana.informed.formdata', + 'iges' => 'model/iges', + 'igl' => 'application/vnd.igloader', + 'igm' => 'application/vnd.insors.igm', + 'igs' => 'model/iges', + 'igx' => 'application/vnd.micrografx.igx', + 'iif' => 'application/vnd.shana.informed.interchange', + 'imp' => 'application/vnd.accpac.simply.imp', + 'ims' => 'application/vnd.ms-ims', + 'in' => 'text/plain', + 'ink' => 'application/inkml+xml', + 'inkml' => 'application/inkml+xml', + 'install' => 'application/x-install-instructions', + 'iota' => 'application/vnd.astraea-software.iota', + 'ipfix' => 'application/ipfix', + 'ipk' => 'application/vnd.shana.informed.package', + 'irm' => 'application/vnd.ibm.rights-management', + 'irp' => 'application/vnd.irepository.package+xml', + 'iso' => 'application/x-iso9660-image', + 'itp' => 'application/vnd.shana.informed.formtemplate', + 'ivp' => 'application/vnd.immervision-ivp', + 'ivu' => 'application/vnd.immervision-ivu', + 'jad' => 'text/vnd.sun.j2me.app-descriptor', + 'jam' => 'application/vnd.jam', + 'jar' => 'application/java-archive', + 'java' => 'text/x-java-source', + 'jisp' => 'application/vnd.jisp', + 'jlt' => 'application/vnd.hp-jlyt', + 'jnlp' => 'application/x-java-jnlp-file', + 'joda' => 'application/vnd.joost.joda-archive', + 'jpe' => 'image/jpeg', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'jpgm' => 'video/jpm', + 'jpgv' => 'video/jpeg', + 'jpm' => 'video/jpm', + 'js' => 'application/javascript', + 'json' => 'application/json', + 'jsonml' => 'application/jsonml+json', + 'kar' => 'audio/midi', + 'karbon' => 'application/vnd.kde.karbon', + 'kfo' => 'application/vnd.kde.kformula', + 'kia' => 'application/vnd.kidspiration', + 'kml' => 'application/vnd.google-earth.kml+xml', + 'kmz' => 'application/vnd.google-earth.kmz', + 'kne' => 'application/vnd.kinar', + 'knp' => 'application/vnd.kinar', + 'kon' => 'application/vnd.kde.kontour', + 'kpr' => 'application/vnd.kde.kpresenter', + 'kpt' => 'application/vnd.kde.kpresenter', + 'kpxx' => 'application/vnd.ds-keypoint', + 'ksp' => 'application/vnd.kde.kspread', + 'ktr' => 'application/vnd.kahootz', + 'ktx' => 'image/ktx', + 'ktz' => 'application/vnd.kahootz', + 'kwd' => 'application/vnd.kde.kword', + 'kwt' => 'application/vnd.kde.kword', + 'lasxml' => 'application/vnd.las.las+xml', + 'latex' => 'application/x-latex', + 'lbd' => 'application/vnd.llamagraphics.life-balance.desktop', + 'lbe' => 'application/vnd.llamagraphics.life-balance.exchange+xml', + 'les' => 'application/vnd.hhe.lesson-player', + 'lha' => 'application/x-lzh-compressed', + 'link66' => 'application/vnd.route66.link66+xml', + 'list' => 'text/plain', + 'list3820' => 'application/vnd.ibm.modcap', + 'listafp' => 'application/vnd.ibm.modcap', + 'lnk' => 'application/x-ms-shortcut', + 'log' => 'text/plain', + 'lostxml' => 'application/lost+xml', + 'lrf' => 'application/octet-stream', + 'lrm' => 'application/vnd.ms-lrm', + 'ltf' => 'application/vnd.frogans.ltf', + 'lvp' => 'audio/vnd.lucent.voice', + 'lwp' => 'application/vnd.lotus-wordpro', + 'lzh' => 'application/x-lzh-compressed', + 'm13' => 'application/x-msmediaview', + 'm14' => 'application/x-msmediaview', + 'm1v' => 'video/mpeg', + 'm21' => 'application/mp21', + 'm2a' => 'audio/mpeg', + 'm2v' => 'video/mpeg', + 'm3a' => 'audio/mpeg', + 'm3u' => 'audio/x-mpegurl', + 'm3u8' => 'application/vnd.apple.mpegurl', + 'm4a' => 'audio/mp4', + 'm4u' => 'video/vnd.mpegurl', + 'm4v' => 'video/x-m4v', + 'ma' => 'application/mathematica', + 'mads' => 'application/mads+xml', + 'mag' => 'application/vnd.ecowin.chart', + 'maker' => 'application/vnd.framemaker', + 'man' => 'text/troff', + 'mar' => 'application/octet-stream', + 'mathml' => 'application/mathml+xml', + 'mb' => 'application/mathematica', + 'mbk' => 'application/vnd.mobius.mbk', + 'mbox' => 'application/mbox', + 'mc1' => 'application/vnd.medcalcdata', + 'mcd' => 'application/vnd.mcd', + 'mcurl' => 'text/vnd.curl.mcurl', + 'mdb' => 'application/x-msaccess', + 'mdi' => 'image/vnd.ms-modi', + 'me' => 'text/troff', + 'mesh' => 'model/mesh', + 'meta4' => 'application/metalink4+xml', + 'metalink' => 'application/metalink+xml', + 'mets' => 'application/mets+xml', + 'mfm' => 'application/vnd.mfmp', + 'mft' => 'application/rpki-manifest', + 'mgp' => 'application/vnd.osgeo.mapguide.package', + 'mgz' => 'application/vnd.proteus.magazine', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mie' => 'application/x-mie', + 'mif' => 'application/vnd.mif', + 'mime' => 'message/rfc822', + 'mj2' => 'video/mj2', + 'mjp2' => 'video/mj2', + 'mk3d' => 'video/x-matroska', + 'mka' => 'audio/x-matroska', + 'mks' => 'video/x-matroska', + 'mkv' => 'video/x-matroska', + 'mlp' => 'application/vnd.dolby.mlp', + 'mmd' => 'application/vnd.chipnuts.karaoke-mmd', + 'mmf' => 'application/vnd.smaf', + 'mmr' => 'image/vnd.fujixerox.edmics-mmr', + 'mng' => 'video/x-mng', + 'mny' => 'application/x-msmoney', + 'mobi' => 'application/x-mobipocket-ebook', + 'mods' => 'application/mods+xml', + 'mov' => 'video/quicktime', + 'movie' => 'video/x-sgi-movie', + 'mp2' => 'audio/mpeg', + 'mp21' => 'application/mp21', + 'mp2a' => 'audio/mpeg', + 'mp3' => 'audio/mpeg', + 'mp4' => 'video/mp4', + 'mp4a' => 'audio/mp4', + 'mp4s' => 'application/mp4', + 'mp4v' => 'video/mp4', + 'mpc' => 'application/vnd.mophun.certificate', + 'mpe' => 'video/mpeg', + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mpg4' => 'video/mp4', + 'mpga' => 'audio/mpeg', + 'mpkg' => 'application/vnd.apple.installer+xml', + 'mpm' => 'application/vnd.blueice.multipass', + 'mpn' => 'application/vnd.mophun.application', + 'mpp' => 'application/vnd.ms-project', + 'mpt' => 'application/vnd.ms-project', + 'mpy' => 'application/vnd.ibm.minipay', + 'mqy' => 'application/vnd.mobius.mqy', + 'mrc' => 'application/marc', + 'mrcx' => 'application/marcxml+xml', + 'ms' => 'text/troff', + 'mscml' => 'application/mediaservercontrol+xml', + 'mseed' => 'application/vnd.fdsn.mseed', + 'mseq' => 'application/vnd.mseq', + 'msf' => 'application/vnd.epson.msf', + 'msh' => 'model/mesh', + 'msi' => 'application/x-msdownload', + 'msl' => 'application/vnd.mobius.msl', + 'msty' => 'application/vnd.muvee.style', + 'mts' => 'model/vnd.mts', + 'mus' => 'application/vnd.musician', + 'musicxml' => 'application/vnd.recordare.musicxml+xml', + 'mvb' => 'application/x-msmediaview', + 'mwf' => 'application/vnd.mfer', + 'mxf' => 'application/mxf', + 'mxl' => 'application/vnd.recordare.musicxml', + 'mxml' => 'application/xv+xml', + 'mxs' => 'application/vnd.triscape.mxs', + 'mxu' => 'video/vnd.mpegurl', + 'n-gage' => 'application/vnd.nokia.n-gage.symbian.install', + 'n3' => 'text/n3', + 'nb' => 'application/mathematica', + 'nbp' => 'application/vnd.wolfram.player', + 'nc' => 'application/x-netcdf', + 'ncx' => 'application/x-dtbncx+xml', + 'nfo' => 'text/x-nfo', + 'ngdat' => 'application/vnd.nokia.n-gage.data', + 'nitf' => 'application/vnd.nitf', + 'nlu' => 'application/vnd.neurolanguage.nlu', + 'nml' => 'application/vnd.enliven', + 'nnd' => 'application/vnd.noblenet-directory', + 'nns' => 'application/vnd.noblenet-sealer', + 'nnw' => 'application/vnd.noblenet-web', + 'npx' => 'image/vnd.net-fpx', + 'nsc' => 'application/x-conference', + 'nsf' => 'application/vnd.lotus-notes', + 'ntf' => 'application/vnd.nitf', + 'nzb' => 'application/x-nzb', + 'oa2' => 'application/vnd.fujitsu.oasys2', + 'oa3' => 'application/vnd.fujitsu.oasys3', + 'oas' => 'application/vnd.fujitsu.oasys', + 'obd' => 'application/x-msbinder', + 'obj' => 'application/x-tgif', + 'oda' => 'application/oda', + 'odb' => 'application/vnd.oasis.opendocument.database', + 'odc' => 'application/vnd.oasis.opendocument.chart', + 'odf' => 'application/vnd.oasis.opendocument.formula', + 'odft' => 'application/vnd.oasis.opendocument.formula-template', + 'odg' => 'application/vnd.oasis.opendocument.graphics', + 'odi' => 'application/vnd.oasis.opendocument.image', + 'odm' => 'application/vnd.oasis.opendocument.text-master', + 'odp' => 'application/vnd.oasis.opendocument.presentation', + 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', + 'odt' => 'application/vnd.oasis.opendocument.text', + 'oga' => 'audio/ogg', + 'ogg' => 'audio/ogg', + 'ogv' => 'video/ogg', + 'ogx' => 'application/ogg', + 'omdoc' => 'application/omdoc+xml', + 'onepkg' => 'application/onenote', + 'onetmp' => 'application/onenote', + 'onetoc' => 'application/onenote', + 'onetoc2' => 'application/onenote', + 'opf' => 'application/oebps-package+xml', + 'opml' => 'text/x-opml', + 'oprc' => 'application/vnd.palm', + 'org' => 'application/vnd.lotus-organizer', + 'osf' => 'application/vnd.yamaha.openscoreformat', + 'osfpvg' => 'application/vnd.yamaha.openscoreformat.osfpvg+xml', + 'otc' => 'application/vnd.oasis.opendocument.chart-template', + 'otf' => 'application/x-font-otf', + 'otg' => 'application/vnd.oasis.opendocument.graphics-template', + 'oth' => 'application/vnd.oasis.opendocument.text-web', + 'oti' => 'application/vnd.oasis.opendocument.image-template', + 'otp' => 'application/vnd.oasis.opendocument.presentation-template', + 'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template', + 'ott' => 'application/vnd.oasis.opendocument.text-template', + 'oxps' => 'application/oxps', + 'oxt' => 'application/vnd.openofficeorg.extension', + 'p' => 'text/x-pascal', + 'p10' => 'application/pkcs10', + 'p12' => 'application/x-pkcs12', + 'p7b' => 'application/x-pkcs7-certificates', + 'p7c' => 'application/pkcs7-mime', + 'p7m' => 'application/pkcs7-mime', + 'p7r' => 'application/x-pkcs7-certreqresp', + 'p7s' => 'application/pkcs7-signature', + 'p8' => 'application/pkcs8', + 'pas' => 'text/x-pascal', + 'paw' => 'application/vnd.pawaafile', + 'pbd' => 'application/vnd.powerbuilder6', + 'pbm' => 'image/x-portable-bitmap', + 'pcap' => 'application/vnd.tcpdump.pcap', + 'pcf' => 'application/x-font-pcf', + 'pcl' => 'application/vnd.hp-pcl', + 'pclxl' => 'application/vnd.hp-pclxl', + 'pct' => 'image/x-pict', + 'pcurl' => 'application/vnd.curl.pcurl', + 'pcx' => 'image/x-pcx', + 'pdb' => 'application/vnd.palm', + 'pdf' => 'application/pdf', + 'pfa' => 'application/x-font-type1', + 'pfb' => 'application/x-font-type1', + 'pfm' => 'application/x-font-type1', + 'pfr' => 'application/font-tdpfr', + 'pfx' => 'application/x-pkcs12', + 'pgm' => 'image/x-portable-graymap', + 'pgn' => 'application/x-chess-pgn', + 'pgp' => 'application/pgp-encrypted', + 'php' => 'application/x-php', + 'php3' => 'application/x-php', + 'php4' => 'application/x-php', + 'php5' => 'application/x-php', + 'pic' => 'image/x-pict', + 'pkg' => 'application/octet-stream', + 'pki' => 'application/pkixcmp', + 'pkipath' => 'application/pkix-pkipath', + 'plb' => 'application/vnd.3gpp.pic-bw-large', + 'plc' => 'application/vnd.mobius.plc', + 'plf' => 'application/vnd.pocketlearn', + 'pls' => 'application/pls+xml', + 'pml' => 'application/vnd.ctc-posml', + 'png' => 'image/png', + 'pnm' => 'image/x-portable-anymap', + 'portpkg' => 'application/vnd.macports.portpkg', + 'pot' => 'application/vnd.ms-powerpoint', + 'potm' => 'application/vnd.ms-powerpoint.template.macroenabled.12', + 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', + 'ppam' => 'application/vnd.ms-powerpoint.addin.macroenabled.12', + 'ppd' => 'application/vnd.cups-ppd', + 'ppm' => 'image/x-portable-pixmap', + 'pps' => 'application/vnd.ms-powerpoint', + 'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroenabled.12', + 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', + 'ppt' => 'application/vnd.ms-powerpoint', + 'pptm' => 'application/vnd.ms-powerpoint.presentation.macroenabled.12', + 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'pqa' => 'application/vnd.palm', + 'prc' => 'application/x-mobipocket-ebook', + 'pre' => 'application/vnd.lotus-freelance', + 'prf' => 'application/pics-rules', + 'ps' => 'application/postscript', + 'psb' => 'application/vnd.3gpp.pic-bw-small', + 'psd' => 'image/vnd.adobe.photoshop', + 'psf' => 'application/x-font-linux-psf', + 'pskcxml' => 'application/pskc+xml', + 'ptid' => 'application/vnd.pvi.ptid1', + 'pub' => 'application/x-mspublisher', + 'pvb' => 'application/vnd.3gpp.pic-bw-var', + 'pwn' => 'application/vnd.3m.post-it-notes', + 'pya' => 'audio/vnd.ms-playready.media.pya', + 'pyv' => 'video/vnd.ms-playready.media.pyv', + 'qam' => 'application/vnd.epson.quickanime', + 'qbo' => 'application/vnd.intu.qbo', + 'qfx' => 'application/vnd.intu.qfx', + 'qps' => 'application/vnd.publishare-delta-tree', + 'qt' => 'video/quicktime', + 'qwd' => 'application/vnd.quark.quarkxpress', + 'qwt' => 'application/vnd.quark.quarkxpress', + 'qxb' => 'application/vnd.quark.quarkxpress', + 'qxd' => 'application/vnd.quark.quarkxpress', + 'qxl' => 'application/vnd.quark.quarkxpress', + 'qxt' => 'application/vnd.quark.quarkxpress', + 'ra' => 'audio/x-pn-realaudio', + 'ram' => 'audio/x-pn-realaudio', + 'rar' => 'application/x-rar-compressed', + 'ras' => 'image/x-cmu-raster', + 'rcprofile' => 'application/vnd.ipunplugged.rcprofile', + 'rdf' => 'application/rdf+xml', + 'rdz' => 'application/vnd.data-vision.rdz', + 'rep' => 'application/vnd.businessobjects', + 'res' => 'application/x-dtbresource+xml', + 'rgb' => 'image/x-rgb', + 'rif' => 'application/reginfo+xml', + 'rip' => 'audio/vnd.rip', + 'ris' => 'application/x-research-info-systems', + 'rl' => 'application/resource-lists+xml', + 'rlc' => 'image/vnd.fujixerox.edmics-rlc', + 'rld' => 'application/resource-lists-diff+xml', + 'rm' => 'application/vnd.rn-realmedia', + 'rmi' => 'audio/midi', + 'rmp' => 'audio/x-pn-realaudio-plugin', + 'rms' => 'application/vnd.jcp.javame.midlet-rms', + 'rmvb' => 'application/vnd.rn-realmedia-vbr', + 'rnc' => 'application/relax-ng-compact-syntax', + 'roa' => 'application/rpki-roa', + 'roff' => 'text/troff', + 'rp9' => 'application/vnd.cloanto.rp9', + 'rpss' => 'application/vnd.nokia.radio-presets', + 'rpst' => 'application/vnd.nokia.radio-preset', + 'rq' => 'application/sparql-query', + 'rs' => 'application/rls-services+xml', + 'rsd' => 'application/rsd+xml', + 'rss' => 'application/rss+xml', + 'rtf' => 'application/rtf', + 'rtx' => 'text/richtext', + 's' => 'text/x-asm', + 's3m' => 'audio/s3m', + 'saf' => 'application/vnd.yamaha.smaf-audio', + 'sbml' => 'application/sbml+xml', + 'sc' => 'application/vnd.ibm.secure-container', + 'scd' => 'application/x-msschedule', + 'scm' => 'application/vnd.lotus-screencam', + 'scq' => 'application/scvp-cv-request', + 'scs' => 'application/scvp-cv-response', + 'scurl' => 'text/vnd.curl.scurl', + 'sda' => 'application/vnd.stardivision.draw', + 'sdc' => 'application/vnd.stardivision.calc', + 'sdd' => 'application/vnd.stardivision.impress', + 'sdkd' => 'application/vnd.solent.sdkm+xml', + 'sdkm' => 'application/vnd.solent.sdkm+xml', + 'sdp' => 'application/sdp', + 'sdw' => 'application/vnd.stardivision.writer', + 'see' => 'application/vnd.seemail', + 'seed' => 'application/vnd.fdsn.seed', + 'sema' => 'application/vnd.sema', + 'semd' => 'application/vnd.semd', + 'semf' => 'application/vnd.semf', + 'ser' => 'application/java-serialized-object', + 'setpay' => 'application/set-payment-initiation', + 'setreg' => 'application/set-registration-initiation', + 'sfd-hdstx' => 'application/vnd.hydrostatix.sof-data', + 'sfs' => 'application/vnd.spotfire.sfs', + 'sfv' => 'text/x-sfv', + 'sgi' => 'image/sgi', + 'sgl' => 'application/vnd.stardivision.writer-global', + 'sgm' => 'text/sgml', + 'sgml' => 'text/sgml', + 'sh' => 'application/x-sh', + 'shar' => 'application/x-shar', + 'shf' => 'application/shf+xml', + 'sid' => 'image/x-mrsid-image', + 'sig' => 'application/pgp-signature', + 'sil' => 'audio/silk', + 'silo' => 'model/mesh', + 'sis' => 'application/vnd.symbian.install', + 'sisx' => 'application/vnd.symbian.install', + 'sit' => 'application/x-stuffit', + 'sitx' => 'application/x-stuffitx', + 'skd' => 'application/vnd.koan', + 'skm' => 'application/vnd.koan', + 'skp' => 'application/vnd.koan', + 'skt' => 'application/vnd.koan', + 'sldm' => 'application/vnd.ms-powerpoint.slide.macroenabled.12', + 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide', + 'slt' => 'application/vnd.epson.salt', + 'sm' => 'application/vnd.stepmania.stepchart', + 'smf' => 'application/vnd.stardivision.math', + 'smi' => 'application/smil+xml', + 'smil' => 'application/smil+xml', + 'smv' => 'video/x-smv', + 'smzip' => 'application/vnd.stepmania.package', + 'snd' => 'audio/basic', + 'snf' => 'application/x-font-snf', + 'so' => 'application/octet-stream', + 'spc' => 'application/x-pkcs7-certificates', + 'spf' => 'application/vnd.yamaha.smaf-phrase', + 'spl' => 'application/x-futuresplash', + 'spot' => 'text/vnd.in3d.spot', + 'spp' => 'application/scvp-vp-response', + 'spq' => 'application/scvp-vp-request', + 'spx' => 'audio/ogg', + 'sql' => 'application/x-sql', + 'src' => 'application/x-wais-source', + 'srt' => 'application/x-subrip', + 'sru' => 'application/sru+xml', + 'srx' => 'application/sparql-results+xml', + 'ssdl' => 'application/ssdl+xml', + 'sse' => 'application/vnd.kodak-descriptor', + 'ssf' => 'application/vnd.epson.ssf', + 'ssml' => 'application/ssml+xml', + 'st' => 'application/vnd.sailingtracker.track', + 'stc' => 'application/vnd.sun.xml.calc.template', + 'std' => 'application/vnd.sun.xml.draw.template', + 'stf' => 'application/vnd.wt.stf', + 'sti' => 'application/vnd.sun.xml.impress.template', + 'stk' => 'application/hyperstudio', + 'stl' => 'application/vnd.ms-pki.stl', + 'str' => 'application/vnd.pg.format', + 'stw' => 'application/vnd.sun.xml.writer.template', + 'sub' => 'text/vnd.dvb.subtitle', + 'sus' => 'application/vnd.sus-calendar', + 'susp' => 'application/vnd.sus-calendar', + 'sv4cpio' => 'application/x-sv4cpio', + 'sv4crc' => 'application/x-sv4crc', + 'svc' => 'application/vnd.dvb.service', + 'svd' => 'application/vnd.svd', + 'svg' => 'image/svg+xml', + 'svgz' => 'image/svg+xml', + 'swa' => 'application/x-director', + 'swf' => 'application/x-shockwave-flash', + 'swi' => 'application/vnd.aristanetworks.swi', + 'sxc' => 'application/vnd.sun.xml.calc', + 'sxd' => 'application/vnd.sun.xml.draw', + 'sxg' => 'application/vnd.sun.xml.writer.global', + 'sxi' => 'application/vnd.sun.xml.impress', + 'sxm' => 'application/vnd.sun.xml.math', + 'sxw' => 'application/vnd.sun.xml.writer', + 't' => 'text/troff', + 't3' => 'application/x-t3vm-image', + 'taglet' => 'application/vnd.mynfc', + 'tao' => 'application/vnd.tao.intent-module-archive', + 'tar' => 'application/x-tar', + 'tcap' => 'application/vnd.3gpp2.tcap', + 'tcl' => 'application/x-tcl', + 'teacher' => 'application/vnd.smart.teacher', + 'tei' => 'application/tei+xml', + 'teicorpus' => 'application/tei+xml', + 'tex' => 'application/x-tex', + 'texi' => 'application/x-texinfo', + 'texinfo' => 'application/x-texinfo', + 'text' => 'text/plain', + 'tfi' => 'application/thraud+xml', + 'tfm' => 'application/x-tex-tfm', + 'tga' => 'image/x-tga', + 'thmx' => 'application/vnd.ms-officetheme', + 'tif' => 'image/tiff', + 'tiff' => 'image/tiff', + 'tmo' => 'application/vnd.tmobile-livetv', + 'torrent' => 'application/x-bittorrent', + 'tpl' => 'application/vnd.groove-tool-template', + 'tpt' => 'application/vnd.trid.tpt', + 'tr' => 'text/troff', + 'tra' => 'application/vnd.trueapp', + 'trm' => 'application/x-msterminal', + 'tsd' => 'application/timestamped-data', + 'tsv' => 'text/tab-separated-values', + 'ttc' => 'application/x-font-ttf', + 'ttf' => 'application/x-font-ttf', + 'ttl' => 'text/turtle', + 'twd' => 'application/vnd.simtech-mindmapper', + 'twds' => 'application/vnd.simtech-mindmapper', + 'txd' => 'application/vnd.genomatix.tuxedo', + 'txf' => 'application/vnd.mobius.txf', + 'txt' => 'text/plain', + 'u32' => 'application/x-authorware-bin', + 'udeb' => 'application/x-debian-package', + 'ufd' => 'application/vnd.ufdl', + 'ufdl' => 'application/vnd.ufdl', + 'ulx' => 'application/x-glulx', + 'umj' => 'application/vnd.umajin', + 'unityweb' => 'application/vnd.unity', + 'uoml' => 'application/vnd.uoml+xml', + 'uri' => 'text/uri-list', + 'uris' => 'text/uri-list', + 'urls' => 'text/uri-list', + 'ustar' => 'application/x-ustar', + 'utz' => 'application/vnd.uiq.theme', + 'uu' => 'text/x-uuencode', + 'uva' => 'audio/vnd.dece.audio', + 'uvd' => 'application/vnd.dece.data', + 'uvf' => 'application/vnd.dece.data', + 'uvg' => 'image/vnd.dece.graphic', + 'uvh' => 'video/vnd.dece.hd', + 'uvi' => 'image/vnd.dece.graphic', + 'uvm' => 'video/vnd.dece.mobile', + 'uvp' => 'video/vnd.dece.pd', + 'uvs' => 'video/vnd.dece.sd', + 'uvt' => 'application/vnd.dece.ttml+xml', + 'uvu' => 'video/vnd.uvvu.mp4', + 'uvv' => 'video/vnd.dece.video', + 'uvva' => 'audio/vnd.dece.audio', + 'uvvd' => 'application/vnd.dece.data', + 'uvvf' => 'application/vnd.dece.data', + 'uvvg' => 'image/vnd.dece.graphic', + 'uvvh' => 'video/vnd.dece.hd', + 'uvvi' => 'image/vnd.dece.graphic', + 'uvvm' => 'video/vnd.dece.mobile', + 'uvvp' => 'video/vnd.dece.pd', + 'uvvs' => 'video/vnd.dece.sd', + 'uvvt' => 'application/vnd.dece.ttml+xml', + 'uvvu' => 'video/vnd.uvvu.mp4', + 'uvvv' => 'video/vnd.dece.video', + 'uvvx' => 'application/vnd.dece.unspecified', + 'uvvz' => 'application/vnd.dece.zip', + 'uvx' => 'application/vnd.dece.unspecified', + 'uvz' => 'application/vnd.dece.zip', + 'vcard' => 'text/vcard', + 'vcd' => 'application/x-cdlink', + 'vcf' => 'text/x-vcard', + 'vcg' => 'application/vnd.groove-vcard', + 'vcs' => 'text/x-vcalendar', + 'vcx' => 'application/vnd.vcx', + 'vis' => 'application/vnd.visionary', + 'viv' => 'video/vnd.vivo', + 'vob' => 'video/x-ms-vob', + 'vor' => 'application/vnd.stardivision.writer', + 'vox' => 'application/x-authorware-bin', + 'vrml' => 'model/vrml', + 'vsd' => 'application/vnd.visio', + 'vsf' => 'application/vnd.vsf', + 'vss' => 'application/vnd.visio', + 'vst' => 'application/vnd.visio', + 'vsw' => 'application/vnd.visio', + 'vtu' => 'model/vnd.vtu', + 'vxml' => 'application/voicexml+xml', + 'w3d' => 'application/x-director', + 'wad' => 'application/x-doom', + 'wav' => 'audio/x-wav', + 'wax' => 'audio/x-ms-wax', + 'wbmp' => 'image/vnd.wap.wbmp', + 'wbs' => 'application/vnd.criticaltools.wbs+xml', + 'wbxml' => 'application/vnd.wap.wbxml', + 'wcm' => 'application/vnd.ms-works', + 'wdb' => 'application/vnd.ms-works', + 'wdp' => 'image/vnd.ms-photo', + 'weba' => 'audio/webm', + 'webm' => 'video/webm', + 'webp' => 'image/webp', + 'wg' => 'application/vnd.pmi.widget', + 'wgt' => 'application/widget', + 'wks' => 'application/vnd.ms-works', + 'wm' => 'video/x-ms-wm', + 'wma' => 'audio/x-ms-wma', + 'wmd' => 'application/x-ms-wmd', + 'wmf' => 'application/x-msmetafile', + 'wml' => 'text/vnd.wap.wml', + 'wmlc' => 'application/vnd.wap.wmlc', + 'wmls' => 'text/vnd.wap.wmlscript', + 'wmlsc' => 'application/vnd.wap.wmlscriptc', + 'wmv' => 'video/x-ms-wmv', + 'wmx' => 'video/x-ms-wmx', + 'wmz' => 'application/x-msmetafile', + 'woff' => 'application/font-woff', + 'wpd' => 'application/vnd.wordperfect', + 'wpl' => 'application/vnd.ms-wpl', + 'wps' => 'application/vnd.ms-works', + 'wqd' => 'application/vnd.wqd', + 'wri' => 'application/x-mswrite', + 'wrl' => 'model/vrml', + 'wsdl' => 'application/wsdl+xml', + 'wspolicy' => 'application/wspolicy+xml', + 'wtb' => 'application/vnd.webturbo', + 'wvx' => 'video/x-ms-wvx', + 'x32' => 'application/x-authorware-bin', + 'x3d' => 'model/x3d+xml', + 'x3db' => 'model/x3d+binary', + 'x3dbz' => 'model/x3d+binary', + 'x3dv' => 'model/x3d+vrml', + 'x3dvz' => 'model/x3d+vrml', + 'x3dz' => 'model/x3d+xml', + 'xaml' => 'application/xaml+xml', + 'xap' => 'application/x-silverlight-app', + 'xar' => 'application/vnd.xara', + 'xbap' => 'application/x-ms-xbap', + 'xbd' => 'application/vnd.fujixerox.docuworks.binder', + 'xbm' => 'image/x-xbitmap', + 'xdf' => 'application/xcap-diff+xml', + 'xdm' => 'application/vnd.syncml.dm+xml', + 'xdp' => 'application/vnd.adobe.xdp+xml', + 'xdssc' => 'application/dssc+xml', + 'xdw' => 'application/vnd.fujixerox.docuworks', + 'xenc' => 'application/xenc+xml', + 'xer' => 'application/patch-ops-error+xml', + 'xfdf' => 'application/vnd.adobe.xfdf', + 'xfdl' => 'application/vnd.xfdl', + 'xht' => 'application/xhtml+xml', + 'xhtml' => 'application/xhtml+xml', + 'xhvml' => 'application/xv+xml', + 'xif' => 'image/vnd.xiff', + 'xla' => 'application/vnd.ms-excel', + 'xlam' => 'application/vnd.ms-excel.addin.macroenabled.12', + 'xlc' => 'application/vnd.ms-excel', + 'xlf' => 'application/x-xliff+xml', + 'xlm' => 'application/vnd.ms-excel', + 'xls' => 'application/vnd.ms-excel', + 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroenabled.12', + 'xlsm' => 'application/vnd.ms-excel.sheet.macroenabled.12', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'xlt' => 'application/vnd.ms-excel', + 'xltm' => 'application/vnd.ms-excel.template.macroenabled.12', + 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', + 'xlw' => 'application/vnd.ms-excel', + 'xm' => 'audio/xm', + 'xml' => 'application/xml', + 'xo' => 'application/vnd.olpc-sugar', + 'xop' => 'application/xop+xml', + 'xpi' => 'application/x-xpinstall', + 'xpl' => 'application/xproc+xml', + 'xpm' => 'image/x-xpixmap', + 'xpr' => 'application/vnd.is-xpr', + 'xps' => 'application/vnd.ms-xpsdocument', + 'xpw' => 'application/vnd.intercon.formnet', + 'xpx' => 'application/vnd.intercon.formnet', + 'xsl' => 'application/xml', + 'xslt' => 'application/xslt+xml', + 'xsm' => 'application/vnd.syncml+xml', + 'xspf' => 'application/xspf+xml', + 'xul' => 'application/vnd.mozilla.xul+xml', + 'xvm' => 'application/xv+xml', + 'xvml' => 'application/xv+xml', + 'xwd' => 'image/x-xwindowdump', + 'xyz' => 'chemical/x-xyz', + 'xz' => 'application/x-xz', + 'yang' => 'application/yang', + 'yin' => 'application/yin+xml', + 'z1' => 'application/x-zmachine', + 'z2' => 'application/x-zmachine', + 'z3' => 'application/x-zmachine', + 'z4' => 'application/x-zmachine', + 'z5' => 'application/x-zmachine', + 'z6' => 'application/x-zmachine', + 'z7' => 'application/x-zmachine', + 'z8' => 'application/x-zmachine', + 'zaz' => 'application/vnd.zzazz.deck+xml', + 'zip' => 'application/zip', + 'zir' => 'application/vnd.zul', + 'zirz' => 'application/vnd.zul', + 'zmm' => 'application/vnd.handheld-entertainment+xml', + '123' => 'application/vnd.lotus-1-2-3', +); diff --git a/vendor/swiftmailer/swiftmailer/lib/preferences.php b/vendor/swiftmailer/swiftmailer/lib/preferences.php new file mode 100644 index 0000000000..e519501482 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/preferences.php @@ -0,0 +1,25 @@ +setCharset('utf-8'); + +// Without these lines the default caching mechanism is "array" but this uses a lot of memory. +// If possible, use a disk cache to enable attaching large attachments etc. +// You can override the default temporary directory by setting the TMPDIR environment variable. +if (@is_writable($tmpDir = sys_get_temp_dir())) { + $preferences->setTempDir($tmpDir)->setCacheType('disk'); +} + +// this should only be done when Swiftmailer won't use the native QP content encoder +// see mime_deps.php +if (version_compare(phpversion(), '5.4.7', '<')) { + $preferences->setQPDotEscape(false); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/swift_init.php b/vendor/swiftmailer/swiftmailer/lib/swift_init.php new file mode 100644 index 0000000000..ff7196344b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/swift_init.php @@ -0,0 +1,28 @@ + 'application/x-php', + 'php3' => 'application/x-php', + 'php4' => 'application/x-php', + 'php5' => 'application/x-php', + 'zip' => 'application/zip', + 'gif' => 'image/gif', + 'png' => 'image/png', + 'css' => 'text/css', + 'js' => 'text/javascript', + 'txt' => 'text/plain', + 'aif' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', + 'avi' => 'video/avi', + 'bmp' => 'image/bmp', + 'bz2' => 'application/x-bz2', + 'csv' => 'text/csv', + 'dmg' => 'application/x-apple-diskimage', + 'doc' => 'application/msword', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'eml' => 'message/rfc822', + 'aps' => 'application/postscript', + 'exe' => 'application/x-ms-dos-executable', + 'flv' => 'video/x-flv', + 'gz' => 'application/x-gzip', + 'hqx' => 'application/stuffit', + 'htm' => 'text/html', + 'html' => 'text/html', + 'jar' => 'application/x-java-archive', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'm3u' => 'audio/x-mpegurl', + 'm4a' => 'audio/mp4', + 'mdb' => 'application/x-msaccess', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mov' => 'video/quicktime', + 'mp3' => 'audio/mpeg', + 'mp4' => 'video/mp4', + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'odg' => 'vnd.oasis.opendocument.graphics', + 'odp' => 'vnd.oasis.opendocument.presentation', + 'odt' => 'vnd.oasis.opendocument.text', + 'ods' => 'vnd.oasis.opendocument.spreadsheet', + 'ogg' => 'audio/ogg', + 'pdf' => 'application/pdf', + 'ppt' => 'application/vnd.ms-powerpoint', + 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'ps' => 'application/postscript', + 'rar' => 'application/x-rar-compressed', + 'rtf' => 'application/rtf', + 'tar' => 'application/x-tar', + 'sit' => 'application/x-stuffit', + 'svg' => 'image/svg+xml', + 'tif' => 'image/tiff', + 'tiff' => 'image/tiff', + 'ttf' => 'application/x-font-truetype', + 'vcf' => 'text/x-vcard', + 'wav' => 'audio/wav', + 'wma' => 'audio/x-ms-wma', + 'wmv' => 'audio/x-ms-wmv', + 'xls' => 'application/excel', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'xml' => 'application/xml', + ); + + // wrap array for generating file + foreach ($valid_mime_types_preset as $extension => $mime_type) { + // generate array for mimetype to extension resolver (only first match) + $valid_mime_types[$extension] = "'{$extension}' => '{$mime_type}'"; + } + + // collect extensions + $valid_extensions = array(); + + // all extensions from second match + foreach ($matches[2] as $i => $extensions) { + // explode multiple extensions from string + $extensions = explode(' ', strtolower($extensions)); + + // force array for foreach + if (!is_array($extensions)) { + $extensions = array($extensions); + } + + foreach ($extensions as $extension) { + // get mime type + $mime_type = $matches[1][$i]; + + // check if string length lower than 10 + if (strlen($extension) < 10) { + // add extension + $valid_extensions[] = $extension; + + if (!isset($valid_mime_types[$mime_type])) { + // generate array for mimetype to extension resolver (only first match) + $valid_mime_types[$extension] = "'{$extension}' => '{$mime_type}'"; + } + } + } + } + } + + $xml = simplexml_load_string($mime_xml); + + foreach ($xml as $node) { + // check if there is no pattern + if (!isset($node->glob['pattern'])) { + continue; + } + + // get all matching extensions from match + foreach ((array) $node->glob['pattern'] as $extension) { + // skip none glob extensions + if (strpos($extension, '.') === false) { + continue; + } + + // remove get only last part + $extension = explode('.', strtolower($extension)); + $extension = end($extension); + + // maximum length in database column + if (strlen($extension) <= 9) { + $valid_extensions[] = $extension; + } + } + + if (isset($node->glob['pattern'][0])) { + // mime type + $mime_type = strtolower((string) $node['type']); + + // get first extension + $extension = strtolower(trim($node->glob['ddpattern'][0], '*.')); + + // skip none glob extensions and check if string length between 1 and 10 + if (strpos($extension, '.') !== false || strlen($extension) < 1 || strlen($extension) > 9) { + continue; + } + + // check if string length lower than 10 + if (!isset($valid_mime_types[$mime_type])) { + // generate array for mimetype to extension resolver (only first match) + $valid_mime_types[$extension] = "'{$extension}' => '{$mime_type}'"; + } + } + } + + // full list of valid extensions only + $valid_mime_types = array_unique($valid_mime_types); + ksort($valid_mime_types); + + // combine mime types and extensions array + $output = "$preamble\$swift_mime_types = array(\n ".implode($valid_mime_types, ",\n ")."\n);"; + + // write mime_types.php config file + @file_put_contents('./mime_types.php', $output); +} + +generateUpToDateMimeArray(); diff --git a/vendor/swiftmailer/swiftmailer/phpunit.xml.dist b/vendor/swiftmailer/swiftmailer/phpunit.xml.dist new file mode 100644 index 0000000000..606c5b4495 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/phpunit.xml.dist @@ -0,0 +1,39 @@ + + + + + + + + + + + + tests/unit + + + tests/acceptance + + + tests/bug + + + tests/smoke + + + + + + + + diff --git a/vendor/swiftmailer/swiftmailer/tests/IdenticalBinaryConstraint.php b/vendor/swiftmailer/swiftmailer/tests/IdenticalBinaryConstraint.php new file mode 100644 index 0000000000..069d11ab7f --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/IdenticalBinaryConstraint.php @@ -0,0 +1,62 @@ +value = $value; + } + + /** + * Evaluates the constraint for parameter $other. Returns TRUE if the + * constraint is met, FALSE otherwise. + * + * @param mixed $other Value or object to evaluate. + * + * @return bool + */ + public function matches($other) + { + $aHex = $this->asHexString($this->value); + $bHex = $this->asHexString($other); + + return $aHex === $bHex; + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return 'indentical binary'; + } + + /** + * Get the given string of bytes as a stirng of Hexadecimal sequences. + * + * @param string $binary + * + * @return string + */ + private function asHexString($binary) + { + $hex = ''; + + $bytes = unpack('H*', $binary); + + foreach ($bytes as &$byte) { + $byte = strtoupper($byte); + } + + return implode('', $bytes); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/StreamCollector.php b/vendor/swiftmailer/swiftmailer/tests/StreamCollector.php new file mode 100644 index 0000000000..7f079d9837 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/StreamCollector.php @@ -0,0 +1,11 @@ +content .= $arg; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/SwiftMailerSmokeTestCase.php b/vendor/swiftmailer/swiftmailer/tests/SwiftMailerSmokeTestCase.php new file mode 100644 index 0000000000..21d89e83b8 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/SwiftMailerSmokeTestCase.php @@ -0,0 +1,46 @@ +markTestSkipped( + 'Smoke tests are skipped if tests/smoke.conf.php is not edited' + ); + } + } + + protected function _getMailer() + { + switch (SWIFT_SMOKE_TRANSPORT_TYPE) { + case 'smtp': + $transport = Swift_DependencyContainer::getInstance()->lookup('transport.smtp') + ->setHost(SWIFT_SMOKE_SMTP_HOST) + ->setPort(SWIFT_SMOKE_SMTP_PORT) + ->setUsername(SWIFT_SMOKE_SMTP_USER) + ->setPassword(SWIFT_SMOKE_SMTP_PASS) + ->setEncryption(SWIFT_SMOKE_SMTP_ENCRYPTION) + ; + break; + case 'sendmail': + $transport = Swift_DependencyContainer::getInstance()->lookup('transport.sendmail') + ->setCommand(SWIFT_SMOKE_SENDMAIL_COMMAND) + ; + break; + case 'mail': + case 'nativemail': + $transport = Swift_DependencyContainer::getInstance()->lookup('transport.mail'); + break; + default: + throw new Exception('Undefined transport ['.SWIFT_SMOKE_TRANSPORT_TYPE.']'); + } + + return new Swift_Mailer($transport); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/SwiftMailerTestCase.php b/vendor/swiftmailer/swiftmailer/tests/SwiftMailerTestCase.php new file mode 100644 index 0000000000..f0e27361ce --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/SwiftMailerTestCase.php @@ -0,0 +1,34 @@ + \ No newline at end of file diff --git a/vendor/swiftmailer/swiftmailer/tests/_samples/files/swiftmailer.png b/vendor/swiftmailer/swiftmailer/tests/_samples/files/swiftmailer.png new file mode 100644 index 0000000000000000000000000000000000000000..1b95f619af1fcbed1fca382d22022bdf329849cf GIT binary patch literal 3194 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 literal 0 HcmV?d00001 diff --git a/vendor/swiftmailer/swiftmailer/tests/_samples/files/textfile.zip b/vendor/swiftmailer/swiftmailer/tests/_samples/files/textfile.zip new file mode 100644 index 0000000000000000000000000000000000000000..5a580ecbf81ff98445bc5c6d0fce7a9a301f0e39 GIT binary patch literal 202 zcmWIWW@h1HU}9ikII%0tqR&rhgC39#!aNM33?-=*C25&Csd^<9C7~gl49w;G_PK*_ zXax(ySH`c5AsLy)3P4nlSX82rpQezg5L}*_R-)jW2-H=iP-db~oSUDWs!)markTestSkipped( + 'Cannot run test without a writable directory to use ('. + 'define SWIFT_TMP_DIR in tests/config.php if you wish to run this test)' + ); + } + + $this->_tmpDir = SWIFT_TMP_DIR; + $this->_testFile = $this->_tmpDir.'/swift-test-file'.__CLASS__; + file_put_contents($this->_testFile, 'abcdefghijklm'); + } + + public function tearDown() + { + unlink($this->_testFile); + } + + public function testFileDataCanBeRead() + { + $file = $this->_createFileStream($this->_testFile); + $str = ''; + while (false !== $bytes = $file->read(8192)) { + $str .= $bytes; + } + $this->assertEquals('abcdefghijklm', $str); + } + + public function testFileDataCanBeReadSequentially() + { + $file = $this->_createFileStream($this->_testFile); + $this->assertEquals('abcde', $file->read(5)); + $this->assertEquals('fghijklm', $file->read(8)); + $this->assertFalse($file->read(1)); + } + + public function testFilenameIsReturned() + { + $file = $this->_createFileStream($this->_testFile); + $this->assertEquals($this->_testFile, $file->getPath()); + } + + public function testFileCanBeWrittenTo() + { + $file = $this->_createFileStream( + $this->_testFile, true + ); + $file->write('foobar'); + $this->assertEquals('foobar', $file->read(8192)); + } + + public function testReadingFromThenWritingToFile() + { + $file = $this->_createFileStream( + $this->_testFile, true + ); + $file->write('foobar'); + $this->assertEquals('foobar', $file->read(8192)); + $file->write('zipbutton'); + $this->assertEquals('zipbutton', $file->read(8192)); + } + + public function testWritingToFileWithCanonicalization() + { + $file = $this->_createFileStream( + $this->_testFile, true + ); + $file->addFilter($this->_createFilter(array("\r\n", "\r"), "\n"), 'allToLF'); + $file->write("foo\r\nbar\r"); + $file->write("\nzip\r\ntest\r"); + $file->flushBuffers(); + $this->assertEquals("foo\nbar\nzip\ntest\n", file_get_contents($this->_testFile)); + } + + public function testBindingOtherStreamsMirrorsWriteOperations() + { + $file = $this->_createFileStream( + $this->_testFile, true + ); + $is1 = $this->_createMockInputStream(); + $is2 = $this->_createMockInputStream(); + + $is1->expects($this->at(0)) + ->method('write') + ->with('x'); + $is1->expects($this->at(1)) + ->method('write') + ->with('y'); + $is2->expects($this->at(0)) + ->method('write') + ->with('x'); + $is2->expects($this->at(1)) + ->method('write') + ->with('y'); + + $file->bind($is1); + $file->bind($is2); + + $file->write('x'); + $file->write('y'); + } + + public function testBindingOtherStreamsMirrorsFlushOperations() + { + $file = $this->_createFileStream( + $this->_testFile, true + ); + $is1 = $this->_createMockInputStream(); + $is2 = $this->_createMockInputStream(); + + $is1->expects($this->once()) + ->method('flushBuffers'); + $is2->expects($this->once()) + ->method('flushBuffers'); + + $file->bind($is1); + $file->bind($is2); + + $file->flushBuffers(); + } + + public function testUnbindingStreamPreventsFurtherWrites() + { + $file = $this->_createFileStream( + $this->_testFile, true + ); + $is1 = $this->_createMockInputStream(); + $is2 = $this->_createMockInputStream(); + + $is1->expects($this->at(0)) + ->method('write') + ->with('x'); + $is1->expects($this->at(1)) + ->method('write') + ->with('y'); + $is2->expects($this->once()) + ->method('write') + ->with('x'); + + $file->bind($is1); + $file->bind($is2); + + $file->write('x'); + + $file->unbind($is2); + + $file->write('y'); + } + + // -- Creation methods + + private function _createFilter($search, $replace) + { + return new Swift_StreamFilters_StringReplacementFilter($search, $replace); + } + + private function _createMockInputStream() + { + return $this->getMockBuilder('Swift_InputByteStream')->getMock(); + } + + private function _createFileStream($file, $writable = false) + { + return new Swift_ByteStream_FileByteStream($file, $writable); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/CharacterReaderFactory/SimpleCharacterReaderFactoryAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/CharacterReaderFactory/SimpleCharacterReaderFactoryAcceptanceTest.php new file mode 100644 index 0000000000..8ce4a18284 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/CharacterReaderFactory/SimpleCharacterReaderFactoryAcceptanceTest.php @@ -0,0 +1,179 @@ +_factory = new Swift_CharacterReaderFactory_SimpleCharacterReaderFactory(); + } + + public function testCreatingUtf8Reader() + { + foreach (array('utf8', 'utf-8', 'UTF-8', 'UTF8') as $utf8) { + $reader = $this->_factory->getReaderFor($utf8); + $this->assertInstanceof($this->_prefix.'Utf8Reader', $reader); + } + } + + public function testCreatingIso8859XReaders() + { + $charsets = array(); + foreach (range(1, 16) as $number) { + foreach (array('iso', 'iec') as $body) { + $charsets[] = $body.'-8859-'.$number; + $charsets[] = $body.'8859-'.$number; + $charsets[] = strtoupper($body).'-8859-'.$number; + $charsets[] = strtoupper($body).'8859-'.$number; + } + } + + foreach ($charsets as $charset) { + $reader = $this->_factory->getReaderFor($charset); + $this->assertInstanceof($this->_prefix.'GenericFixedWidthReader', $reader); + $this->assertEquals(1, $reader->getInitialByteSize()); + } + } + + public function testCreatingWindows125XReaders() + { + $charsets = array(); + foreach (range(0, 8) as $number) { + $charsets[] = 'windows-125'.$number; + $charsets[] = 'windows125'.$number; + $charsets[] = 'WINDOWS-125'.$number; + $charsets[] = 'WINDOWS125'.$number; + } + + foreach ($charsets as $charset) { + $reader = $this->_factory->getReaderFor($charset); + $this->assertInstanceof($this->_prefix.'GenericFixedWidthReader', $reader); + $this->assertEquals(1, $reader->getInitialByteSize()); + } + } + + public function testCreatingCodePageReaders() + { + $charsets = array(); + foreach (range(0, 8) as $number) { + $charsets[] = 'cp-125'.$number; + $charsets[] = 'cp125'.$number; + $charsets[] = 'CP-125'.$number; + $charsets[] = 'CP125'.$number; + } + + foreach (array(437, 737, 850, 855, 857, 858, 860, + 861, 863, 865, 866, 869, ) as $number) { + $charsets[] = 'cp-'.$number; + $charsets[] = 'cp'.$number; + $charsets[] = 'CP-'.$number; + $charsets[] = 'CP'.$number; + } + + foreach ($charsets as $charset) { + $reader = $this->_factory->getReaderFor($charset); + $this->assertInstanceof($this->_prefix.'GenericFixedWidthReader', $reader); + $this->assertEquals(1, $reader->getInitialByteSize()); + } + } + + public function testCreatingAnsiReader() + { + foreach (array('ansi', 'ANSI') as $ansi) { + $reader = $this->_factory->getReaderFor($ansi); + $this->assertInstanceof($this->_prefix.'GenericFixedWidthReader', $reader); + $this->assertEquals(1, $reader->getInitialByteSize()); + } + } + + public function testCreatingMacintoshReader() + { + foreach (array('macintosh', 'MACINTOSH') as $mac) { + $reader = $this->_factory->getReaderFor($mac); + $this->assertInstanceof($this->_prefix.'GenericFixedWidthReader', $reader); + $this->assertEquals(1, $reader->getInitialByteSize()); + } + } + + public function testCreatingKOIReaders() + { + $charsets = array(); + foreach (array('7', '8-r', '8-u', '8u', '8r') as $end) { + $charsets[] = 'koi-'.$end; + $charsets[] = 'koi'.$end; + $charsets[] = 'KOI-'.$end; + $charsets[] = 'KOI'.$end; + } + + foreach ($charsets as $charset) { + $reader = $this->_factory->getReaderFor($charset); + $this->assertInstanceof($this->_prefix.'GenericFixedWidthReader', $reader); + $this->assertEquals(1, $reader->getInitialByteSize()); + } + } + + public function testCreatingIsciiReaders() + { + foreach (array('iscii', 'ISCII', 'viscii', 'VISCII') as $charset) { + $reader = $this->_factory->getReaderFor($charset); + $this->assertInstanceof($this->_prefix.'GenericFixedWidthReader', $reader); + $this->assertEquals(1, $reader->getInitialByteSize()); + } + } + + public function testCreatingMIKReader() + { + foreach (array('mik', 'MIK') as $charset) { + $reader = $this->_factory->getReaderFor($charset); + $this->assertInstanceof($this->_prefix.'GenericFixedWidthReader', $reader); + $this->assertEquals(1, $reader->getInitialByteSize()); + } + } + + public function testCreatingCorkReader() + { + foreach (array('cork', 'CORK', 't1', 'T1') as $charset) { + $reader = $this->_factory->getReaderFor($charset); + $this->assertInstanceof($this->_prefix.'GenericFixedWidthReader', $reader); + $this->assertEquals(1, $reader->getInitialByteSize()); + } + } + + public function testCreatingUcs2Reader() + { + foreach (array('ucs-2', 'UCS-2', 'ucs2', 'UCS2') as $charset) { + $reader = $this->_factory->getReaderFor($charset); + $this->assertInstanceof($this->_prefix.'GenericFixedWidthReader', $reader); + $this->assertEquals(2, $reader->getInitialByteSize()); + } + } + + public function testCreatingUtf16Reader() + { + foreach (array('utf-16', 'UTF-16', 'utf16', 'UTF16') as $charset) { + $reader = $this->_factory->getReaderFor($charset); + $this->assertInstanceof($this->_prefix.'GenericFixedWidthReader', $reader); + $this->assertEquals(2, $reader->getInitialByteSize()); + } + } + + public function testCreatingUcs4Reader() + { + foreach (array('ucs-4', 'UCS-4', 'ucs4', 'UCS4') as $charset) { + $reader = $this->_factory->getReaderFor($charset); + $this->assertInstanceof($this->_prefix.'GenericFixedWidthReader', $reader); + $this->assertEquals(4, $reader->getInitialByteSize()); + } + } + + public function testCreatingUtf32Reader() + { + foreach (array('utf-32', 'UTF-32', 'utf32', 'UTF32') as $charset) { + $reader = $this->_factory->getReaderFor($charset); + $this->assertInstanceof($this->_prefix.'GenericFixedWidthReader', $reader); + $this->assertEquals(4, $reader->getInitialByteSize()); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/DependencyContainerAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/DependencyContainerAcceptanceTest.php new file mode 100644 index 0000000000..e83c2bf57e --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/DependencyContainerAcceptanceTest.php @@ -0,0 +1,24 @@ +listItems() as $itemName) { + try { + // to be removed in 6.0 + if ('transport.mail' === $itemName) { + continue; + } + $di->lookup($itemName); + } catch (Swift_DependencyException $e) { + $this->fail($e->getMessage()); + } + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/EmbeddedFileAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/EmbeddedFileAcceptanceTest.php new file mode 100644 index 0000000000..fc5a8147df --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/EmbeddedFileAcceptanceTest.php @@ -0,0 +1,12 @@ +_samplesDir = realpath(__DIR__.'/../../../_samples/charsets'); + $this->_encoder = new Swift_Encoder_Base64Encoder(); + } + + public function testEncodingAndDecodingSamples() + { + $sampleFp = opendir($this->_samplesDir); + while (false !== $encodingDir = readdir($sampleFp)) { + if (substr($encodingDir, 0, 1) == '.') { + continue; + } + + $sampleDir = $this->_samplesDir.'/'.$encodingDir; + + if (is_dir($sampleDir)) { + $fileFp = opendir($sampleDir); + while (false !== $sampleFile = readdir($fileFp)) { + if (substr($sampleFile, 0, 1) == '.') { + continue; + } + + $text = file_get_contents($sampleDir.'/'.$sampleFile); + $encodedText = $this->_encoder->encodeString($text); + + $this->assertEquals( + base64_decode($encodedText), $text, + '%s: Encoded string should decode back to original string for sample '. + $sampleDir.'/'.$sampleFile + ); + } + closedir($fileFp); + } + } + closedir($sampleFp); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Encoder/QpEncoderAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Encoder/QpEncoderAcceptanceTest.php new file mode 100644 index 0000000000..1da3b837c9 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Encoder/QpEncoderAcceptanceTest.php @@ -0,0 +1,54 @@ +_samplesDir = realpath(__DIR__.'/../../../_samples/charsets'); + $this->_factory = new Swift_CharacterReaderFactory_SimpleCharacterReaderFactory(); + } + + public function testEncodingAndDecodingSamples() + { + $sampleFp = opendir($this->_samplesDir); + while (false !== $encodingDir = readdir($sampleFp)) { + if (substr($encodingDir, 0, 1) == '.') { + continue; + } + + $encoding = $encodingDir; + $charStream = new Swift_CharacterStream_ArrayCharacterStream( + $this->_factory, $encoding); + $encoder = new Swift_Encoder_QpEncoder($charStream); + + $sampleDir = $this->_samplesDir.'/'.$encodingDir; + + if (is_dir($sampleDir)) { + $fileFp = opendir($sampleDir); + while (false !== $sampleFile = readdir($fileFp)) { + if (substr($sampleFile, 0, 1) == '.') { + continue; + } + + $text = file_get_contents($sampleDir.'/'.$sampleFile); + $encodedText = $encoder->encodeString($text); + + foreach (explode("\r\n", $encodedText) as $line) { + $this->assertLessThanOrEqual(76, strlen($line)); + } + + $this->assertEquals( + quoted_printable_decode($encodedText), $text, + '%s: Encoded string should decode back to original string for sample '. + $sampleDir.'/'.$sampleFile + ); + } + closedir($fileFp); + } + } + closedir($sampleFp); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Encoder/Rfc2231EncoderAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Encoder/Rfc2231EncoderAcceptanceTest.php new file mode 100644 index 0000000000..043ddf8cd0 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Encoder/Rfc2231EncoderAcceptanceTest.php @@ -0,0 +1,50 @@ +_samplesDir = realpath(__DIR__.'/../../../_samples/charsets'); + $this->_factory = new Swift_CharacterReaderFactory_SimpleCharacterReaderFactory(); + } + + public function testEncodingAndDecodingSamples() + { + $sampleFp = opendir($this->_samplesDir); + while (false !== $encodingDir = readdir($sampleFp)) { + if (substr($encodingDir, 0, 1) == '.') { + continue; + } + + $encoding = $encodingDir; + $charStream = new Swift_CharacterStream_ArrayCharacterStream( + $this->_factory, $encoding); + $encoder = new Swift_Encoder_Rfc2231Encoder($charStream); + + $sampleDir = $this->_samplesDir.'/'.$encodingDir; + + if (is_dir($sampleDir)) { + $fileFp = opendir($sampleDir); + while (false !== $sampleFile = readdir($fileFp)) { + if (substr($sampleFile, 0, 1) == '.') { + continue; + } + + $text = file_get_contents($sampleDir.'/'.$sampleFile); + $encodedText = $encoder->encodeString($text); + + $this->assertEquals( + urldecode(implode('', explode("\r\n", $encodedText))), $text, + '%s: Encoded string should decode back to original string for sample '. + $sampleDir.'/'.$sampleFile + ); + } + closedir($fileFp); + } + } + closedir($sampleFp); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/EncodingAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/EncodingAcceptanceTest.php new file mode 100644 index 0000000000..6a4d05d3c8 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/EncodingAcceptanceTest.php @@ -0,0 +1,30 @@ +assertEquals('7bit', $encoder->getName()); + } + + public function testGet8BitEncodingReturns8BitEncoder() + { + $encoder = Swift_Encoding::get8BitEncoding(); + $this->assertEquals('8bit', $encoder->getName()); + } + + public function testGetQpEncodingReturnsQpEncoder() + { + $encoder = Swift_Encoding::getQpEncoding(); + $this->assertEquals('quoted-printable', $encoder->getName()); + } + + public function testGetBase64EncodingReturnsBase64Encoder() + { + $encoder = Swift_Encoding::getBase64Encoding(); + $this->assertEquals('base64', $encoder->getName()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/KeyCache/ArrayKeyCacheAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/KeyCache/ArrayKeyCacheAcceptanceTest.php new file mode 100644 index 0000000000..6b06e2ee65 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/KeyCache/ArrayKeyCacheAcceptanceTest.php @@ -0,0 +1,173 @@ +_cache = new Swift_KeyCache_ArrayKeyCache( + new Swift_KeyCache_SimpleKeyCacheInputStream() + ); + } + + public function testStringDataCanBeSetAndFetched() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->assertEquals('test', $this->_cache->getString($this->_key1, 'foo')); + } + + public function testStringDataCanBeOverwritten() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->_cache->setString( + $this->_key1, 'foo', 'whatever', Swift_KeyCache::MODE_WRITE + ); + $this->assertEquals('whatever', $this->_cache->getString($this->_key1, 'foo')); + } + + public function testStringDataCanBeAppended() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->_cache->setString( + $this->_key1, 'foo', 'ing', Swift_KeyCache::MODE_APPEND + ); + $this->assertEquals('testing', $this->_cache->getString($this->_key1, 'foo')); + } + + public function testHasKeyReturnValue() + { + $this->assertFalse($this->_cache->hasKey($this->_key1, 'foo')); + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->assertTrue($this->_cache->hasKey($this->_key1, 'foo')); + } + + public function testNsKeyIsWellPartitioned() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->_cache->setString( + $this->_key2, 'foo', 'ing', Swift_KeyCache::MODE_WRITE + ); + $this->assertEquals('test', $this->_cache->getString($this->_key1, 'foo')); + $this->assertEquals('ing', $this->_cache->getString($this->_key2, 'foo')); + } + + public function testItemKeyIsWellPartitioned() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->_cache->setString( + $this->_key1, 'bar', 'ing', Swift_KeyCache::MODE_WRITE + ); + $this->assertEquals('test', $this->_cache->getString($this->_key1, 'foo')); + $this->assertEquals('ing', $this->_cache->getString($this->_key1, 'bar')); + } + + public function testByteStreamCanBeImported() + { + $os = new Swift_ByteStream_ArrayByteStream(); + $os->write('abcdef'); + + $this->_cache->importFromByteStream( + $this->_key1, 'foo', $os, Swift_KeyCache::MODE_WRITE + ); + $this->assertEquals('abcdef', $this->_cache->getString($this->_key1, 'foo')); + } + + public function testByteStreamCanBeAppended() + { + $os1 = new Swift_ByteStream_ArrayByteStream(); + $os1->write('abcdef'); + + $os2 = new Swift_ByteStream_ArrayByteStream(); + $os2->write('xyzuvw'); + + $this->_cache->importFromByteStream( + $this->_key1, 'foo', $os1, Swift_KeyCache::MODE_APPEND + ); + $this->_cache->importFromByteStream( + $this->_key1, 'foo', $os2, Swift_KeyCache::MODE_APPEND + ); + + $this->assertEquals('abcdefxyzuvw', $this->_cache->getString($this->_key1, 'foo')); + } + + public function testByteStreamAndStringCanBeAppended() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_APPEND + ); + + $os = new Swift_ByteStream_ArrayByteStream(); + $os->write('abcdef'); + + $this->_cache->importFromByteStream( + $this->_key1, 'foo', $os, Swift_KeyCache::MODE_APPEND + ); + $this->assertEquals('testabcdef', $this->_cache->getString($this->_key1, 'foo')); + } + + public function testDataCanBeExportedToByteStream() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + + $is = new Swift_ByteStream_ArrayByteStream(); + + $this->_cache->exportToByteStream($this->_key1, 'foo', $is); + + $string = ''; + while (false !== $bytes = $is->read(8192)) { + $string .= $bytes; + } + + $this->assertEquals('test', $string); + } + + public function testKeyCanBeCleared() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->assertTrue($this->_cache->hasKey($this->_key1, 'foo')); + $this->_cache->clearKey($this->_key1, 'foo'); + $this->assertFalse($this->_cache->hasKey($this->_key1, 'foo')); + } + + public function testNsKeyCanBeCleared() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->_cache->setString( + $this->_key1, 'bar', 'xyz', Swift_KeyCache::MODE_WRITE + ); + $this->assertTrue($this->_cache->hasKey($this->_key1, 'foo')); + $this->assertTrue($this->_cache->hasKey($this->_key1, 'bar')); + $this->_cache->clearAll($this->_key1); + $this->assertFalse($this->_cache->hasKey($this->_key1, 'foo')); + $this->assertFalse($this->_cache->hasKey($this->_key1, 'bar')); + } + + public function testKeyCacheInputStream() + { + $is = $this->_cache->getInputByteStream($this->_key1, 'foo'); + $is->write('abc'); + $is->write('xyz'); + $this->assertEquals('abcxyz', $this->_cache->getString($this->_key1, 'foo')); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/KeyCache/DiskKeyCacheAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/KeyCache/DiskKeyCacheAcceptanceTest.php new file mode 100644 index 0000000000..392edde8f9 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/KeyCache/DiskKeyCacheAcceptanceTest.php @@ -0,0 +1,183 @@ +markTestSkipped( + 'Cannot run test without a writable directory to use ('. + 'define SWIFT_TMP_DIR in tests/config.php if you wish to run this test)' + ); + } + + $this->_key1 = uniqid(microtime(true), true); + $this->_key2 = uniqid(microtime(true), true); + $this->_cache = new Swift_KeyCache_DiskKeyCache( + new Swift_KeyCache_SimpleKeyCacheInputStream(), + SWIFT_TMP_DIR + ); + } + + public function testStringDataCanBeSetAndFetched() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->assertEquals('test', $this->_cache->getString($this->_key1, 'foo')); + } + + public function testStringDataCanBeOverwritten() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->_cache->setString( + $this->_key1, 'foo', 'whatever', Swift_KeyCache::MODE_WRITE + ); + $this->assertEquals('whatever', $this->_cache->getString($this->_key1, 'foo')); + } + + public function testStringDataCanBeAppended() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->_cache->setString( + $this->_key1, 'foo', 'ing', Swift_KeyCache::MODE_APPEND + ); + $this->assertEquals('testing', $this->_cache->getString($this->_key1, 'foo')); + } + + public function testHasKeyReturnValue() + { + $this->assertFalse($this->_cache->hasKey($this->_key1, 'foo')); + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->assertTrue($this->_cache->hasKey($this->_key1, 'foo')); + } + + public function testNsKeyIsWellPartitioned() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->_cache->setString( + $this->_key2, 'foo', 'ing', Swift_KeyCache::MODE_WRITE + ); + $this->assertEquals('test', $this->_cache->getString($this->_key1, 'foo')); + $this->assertEquals('ing', $this->_cache->getString($this->_key2, 'foo')); + } + + public function testItemKeyIsWellPartitioned() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->_cache->setString( + $this->_key1, 'bar', 'ing', Swift_KeyCache::MODE_WRITE + ); + $this->assertEquals('test', $this->_cache->getString($this->_key1, 'foo')); + $this->assertEquals('ing', $this->_cache->getString($this->_key1, 'bar')); + } + + public function testByteStreamCanBeImported() + { + $os = new Swift_ByteStream_ArrayByteStream(); + $os->write('abcdef'); + + $this->_cache->importFromByteStream( + $this->_key1, 'foo', $os, Swift_KeyCache::MODE_WRITE + ); + $this->assertEquals('abcdef', $this->_cache->getString($this->_key1, 'foo')); + } + + public function testByteStreamCanBeAppended() + { + $os1 = new Swift_ByteStream_ArrayByteStream(); + $os1->write('abcdef'); + + $os2 = new Swift_ByteStream_ArrayByteStream(); + $os2->write('xyzuvw'); + + $this->_cache->importFromByteStream( + $this->_key1, 'foo', $os1, Swift_KeyCache::MODE_APPEND + ); + $this->_cache->importFromByteStream( + $this->_key1, 'foo', $os2, Swift_KeyCache::MODE_APPEND + ); + + $this->assertEquals('abcdefxyzuvw', $this->_cache->getString($this->_key1, 'foo')); + } + + public function testByteStreamAndStringCanBeAppended() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_APPEND + ); + + $os = new Swift_ByteStream_ArrayByteStream(); + $os->write('abcdef'); + + $this->_cache->importFromByteStream( + $this->_key1, 'foo', $os, Swift_KeyCache::MODE_APPEND + ); + $this->assertEquals('testabcdef', $this->_cache->getString($this->_key1, 'foo')); + } + + public function testDataCanBeExportedToByteStream() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + + $is = new Swift_ByteStream_ArrayByteStream(); + + $this->_cache->exportToByteStream($this->_key1, 'foo', $is); + + $string = ''; + while (false !== $bytes = $is->read(8192)) { + $string .= $bytes; + } + + $this->assertEquals('test', $string); + } + + public function testKeyCanBeCleared() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->assertTrue($this->_cache->hasKey($this->_key1, 'foo')); + $this->_cache->clearKey($this->_key1, 'foo'); + $this->assertFalse($this->_cache->hasKey($this->_key1, 'foo')); + } + + public function testNsKeyCanBeCleared() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->_cache->setString( + $this->_key1, 'bar', 'xyz', Swift_KeyCache::MODE_WRITE + ); + $this->assertTrue($this->_cache->hasKey($this->_key1, 'foo')); + $this->assertTrue($this->_cache->hasKey($this->_key1, 'bar')); + $this->_cache->clearAll($this->_key1); + $this->assertFalse($this->_cache->hasKey($this->_key1, 'foo')); + $this->assertFalse($this->_cache->hasKey($this->_key1, 'bar')); + } + + public function testKeyCacheInputStream() + { + $is = $this->_cache->getInputByteStream($this->_key1, 'foo'); + $is->write('abc'); + $is->write('xyz'); + $this->assertEquals('abcxyz', $this->_cache->getString($this->_key1, 'foo')); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/MessageAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/MessageAcceptanceTest.php new file mode 100644 index 0000000000..9372fbfdc4 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/MessageAcceptanceTest.php @@ -0,0 +1,57 @@ +_createMessage(); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + + $id = $message->getId(); + $date = $message->getDate(); + $boundary = $message->getBoundary(); + + $message->addPart('foo', 'text/plain', 'iso-8859-1'); + $message->addPart('test foo', 'text/html', 'iso-8859-1'); + + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/alternative;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/plain; charset=iso-8859-1'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'foo'. + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/html; charset=iso-8859-1'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'test foo'. + "\r\n\r\n". + '--'.$boundary.'--'."\r\n", + $message->toString() + ); + } + + // -- Private helpers + + protected function _createMessage() + { + Swift_DependencyContainer::getInstance() + ->register('properties.charset')->asValue(null); + + return Swift_Message::newInstance(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/AttachmentAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/AttachmentAcceptanceTest.php new file mode 100644 index 0000000000..e925367e98 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/AttachmentAcceptanceTest.php @@ -0,0 +1,125 @@ +_cache = new Swift_KeyCache_ArrayKeyCache( + new Swift_KeyCache_SimpleKeyCacheInputStream() + ); + $factory = new Swift_CharacterReaderFactory_SimpleCharacterReaderFactory(); + $this->_contentEncoder = new Swift_Mime_ContentEncoder_Base64ContentEncoder(); + + $headerEncoder = new Swift_Mime_HeaderEncoder_QpHeaderEncoder( + new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8') + ); + $paramEncoder = new Swift_Encoder_Rfc2231Encoder( + new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8') + ); + $this->_grammar = new Swift_Mime_Grammar(); + $this->_headers = new Swift_Mime_SimpleHeaderSet( + new Swift_Mime_SimpleHeaderFactory($headerEncoder, $paramEncoder, $this->_grammar) + ); + } + + public function testDispositionIsSetInHeader() + { + $attachment = $this->_createAttachment(); + $attachment->setContentType('application/pdf'); + $attachment->setDisposition('inline'); + $this->assertEquals( + 'Content-Type: application/pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: inline'."\r\n", + $attachment->toString() + ); + } + + public function testDispositionIsAttachmentByDefault() + { + $attachment = $this->_createAttachment(); + $attachment->setContentType('application/pdf'); + $this->assertEquals( + 'Content-Type: application/pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: attachment'."\r\n", + $attachment->toString() + ); + } + + public function testFilenameIsSetInHeader() + { + $attachment = $this->_createAttachment(); + $attachment->setContentType('application/pdf'); + $attachment->setFilename('foo.pdf'); + $this->assertEquals( + 'Content-Type: application/pdf; name=foo.pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: attachment; filename=foo.pdf'."\r\n", + $attachment->toString() + ); + } + + public function testSizeIsSetInHeader() + { + $attachment = $this->_createAttachment(); + $attachment->setContentType('application/pdf'); + $attachment->setSize(12340); + $this->assertEquals( + 'Content-Type: application/pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: attachment; size=12340'."\r\n", + $attachment->toString() + ); + } + + public function testMultipleParametersInHeader() + { + $attachment = $this->_createAttachment(); + $attachment->setContentType('application/pdf'); + $attachment->setFilename('foo.pdf'); + $attachment->setSize(12340); + $this->assertEquals( + 'Content-Type: application/pdf; name=foo.pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: attachment; filename=foo.pdf; size=12340'."\r\n", + $attachment->toString() + ); + } + + public function testEndToEnd() + { + $attachment = $this->_createAttachment(); + $attachment->setContentType('application/pdf'); + $attachment->setFilename('foo.pdf'); + $attachment->setSize(12340); + $attachment->setBody('abcd'); + $this->assertEquals( + 'Content-Type: application/pdf; name=foo.pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: attachment; filename=foo.pdf; size=12340'."\r\n". + "\r\n". + base64_encode('abcd'), + $attachment->toString() + ); + } + + // -- Private helpers + + protected function _createAttachment() + { + $entity = new Swift_Mime_Attachment( + $this->_headers, + $this->_contentEncoder, + $this->_cache, + $this->_grammar + ); + + return $entity; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/Base64ContentEncoderAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/Base64ContentEncoderAcceptanceTest.php new file mode 100644 index 0000000000..2a5c562b40 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/Base64ContentEncoderAcceptanceTest.php @@ -0,0 +1,56 @@ +_samplesDir = realpath(__DIR__.'/../../../../_samples/charsets'); + $this->_encoder = new Swift_Mime_ContentEncoder_Base64ContentEncoder(); + } + + public function testEncodingAndDecodingSamples() + { + $sampleFp = opendir($this->_samplesDir); + while (false !== $encodingDir = readdir($sampleFp)) { + if (substr($encodingDir, 0, 1) == '.') { + continue; + } + + $sampleDir = $this->_samplesDir.'/'.$encodingDir; + + if (is_dir($sampleDir)) { + $fileFp = opendir($sampleDir); + while (false !== $sampleFile = readdir($fileFp)) { + if (substr($sampleFile, 0, 1) == '.') { + continue; + } + + $text = file_get_contents($sampleDir.'/'.$sampleFile); + + $os = new Swift_ByteStream_ArrayByteStream(); + $os->write($text); + + $is = new Swift_ByteStream_ArrayByteStream(); + + $this->_encoder->encodeByteStream($os, $is); + + $encoded = ''; + while (false !== $bytes = $is->read(8192)) { + $encoded .= $bytes; + } + + $this->assertEquals( + base64_decode($encoded), $text, + '%s: Encoded string should decode back to original string for sample '. + $sampleDir.'/'.$sampleFile + ); + } + closedir($fileFp); + } + } + closedir($sampleFp); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/NativeQpContentEncoderAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/NativeQpContentEncoderAcceptanceTest.php new file mode 100644 index 0000000000..ecd7fcd13f --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/NativeQpContentEncoderAcceptanceTest.php @@ -0,0 +1,88 @@ +_samplesDir = realpath(__DIR__.'/../../../../_samples/charsets'); + $this->_encoder = new Swift_Mime_ContentEncoder_NativeQpContentEncoder(); + } + + public function testEncodingAndDecodingSamples() + { + $sampleFp = opendir($this->_samplesDir); + while (false !== $encodingDir = readdir($sampleFp)) { + if (substr($encodingDir, 0, 1) == '.') { + continue; + } + + $sampleDir = $this->_samplesDir.'/'.$encodingDir; + + if (is_dir($sampleDir)) { + $fileFp = opendir($sampleDir); + while (false !== $sampleFile = readdir($fileFp)) { + if (substr($sampleFile, 0, 1) == '.') { + continue; + } + + $text = file_get_contents($sampleDir.'/'.$sampleFile); + + $os = new Swift_ByteStream_ArrayByteStream(); + $os->write($text); + + $is = new Swift_ByteStream_ArrayByteStream(); + $this->_encoder->encodeByteStream($os, $is); + + $encoded = ''; + while (false !== $bytes = $is->read(8192)) { + $encoded .= $bytes; + } + + $this->assertEquals( + quoted_printable_decode($encoded), + // CR and LF are converted to CRLF + preg_replace('~\r(?!\n)|(?_createEncoderFromContainer(); + $this->assertSame('=C3=A4=C3=B6=C3=BC=C3=9F', $encoder->encodeString('äöüß')); + } + + /** + * @expectedException RuntimeException + */ + public function testCharsetChangeNotImplemented() + { + $this->_encoder->charsetChanged('utf-8'); + $this->_encoder->charsetChanged('charset'); + $this->_encoder->encodeString('foo'); + } + + public function testGetName() + { + $this->assertSame('quoted-printable', $this->_encoder->getName()); + } + + private function _createEncoderFromContainer() + { + return Swift_DependencyContainer::getInstance() + ->lookup('mime.nativeqpcontentencoder') + ; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/PlainContentEncoderAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/PlainContentEncoderAcceptanceTest.php new file mode 100644 index 0000000000..1541b7eac3 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/PlainContentEncoderAcceptanceTest.php @@ -0,0 +1,88 @@ +_samplesDir = realpath(__DIR__.'/../../../../_samples/charsets'); + $this->_encoder = new Swift_Mime_ContentEncoder_PlainContentEncoder('8bit'); + } + + public function testEncodingAndDecodingSamplesString() + { + $sampleFp = opendir($this->_samplesDir); + while (false !== $encodingDir = readdir($sampleFp)) { + if (substr($encodingDir, 0, 1) == '.') { + continue; + } + + $sampleDir = $this->_samplesDir.'/'.$encodingDir; + + if (is_dir($sampleDir)) { + $fileFp = opendir($sampleDir); + while (false !== $sampleFile = readdir($fileFp)) { + if (substr($sampleFile, 0, 1) == '.') { + continue; + } + + $text = file_get_contents($sampleDir.'/'.$sampleFile); + $encodedText = $this->_encoder->encodeString($text); + + $this->assertEquals( + $encodedText, $text, + '%s: Encoded string should be identical to original string for sample '. + $sampleDir.'/'.$sampleFile + ); + } + closedir($fileFp); + } + } + closedir($sampleFp); + } + + public function testEncodingAndDecodingSamplesByteStream() + { + $sampleFp = opendir($this->_samplesDir); + while (false !== $encodingDir = readdir($sampleFp)) { + if (substr($encodingDir, 0, 1) == '.') { + continue; + } + + $sampleDir = $this->_samplesDir.'/'.$encodingDir; + + if (is_dir($sampleDir)) { + $fileFp = opendir($sampleDir); + while (false !== $sampleFile = readdir($fileFp)) { + if (substr($sampleFile, 0, 1) == '.') { + continue; + } + + $text = file_get_contents($sampleDir.'/'.$sampleFile); + + $os = new Swift_ByteStream_ArrayByteStream(); + $os->write($text); + + $is = new Swift_ByteStream_ArrayByteStream(); + + $this->_encoder->encodeByteStream($os, $is); + + $encoded = ''; + while (false !== $bytes = $is->read(8192)) { + $encoded .= $bytes; + } + + $this->assertEquals( + $encoded, $text, + '%s: Encoded string should be identical to original string for sample '. + $sampleDir.'/'.$sampleFile + ); + } + closedir($fileFp); + } + } + closedir($sampleFp); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/QpContentEncoderAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/QpContentEncoderAcceptanceTest.php new file mode 100644 index 0000000000..84f7e03bcc --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/QpContentEncoderAcceptanceTest.php @@ -0,0 +1,160 @@ +_samplesDir = realpath(__DIR__.'/../../../../_samples/charsets'); + $this->_factory = new Swift_CharacterReaderFactory_SimpleCharacterReaderFactory(); + } + + public function tearDown() + { + Swift_Preferences::getInstance()->setQPDotEscape(false); + } + + public function testEncodingAndDecodingSamples() + { + $sampleFp = opendir($this->_samplesDir); + while (false !== $encodingDir = readdir($sampleFp)) { + if (substr($encodingDir, 0, 1) == '.') { + continue; + } + + $encoding = $encodingDir; + $charStream = new Swift_CharacterStream_NgCharacterStream( + $this->_factory, $encoding); + $encoder = new Swift_Mime_ContentEncoder_QpContentEncoder($charStream); + + $sampleDir = $this->_samplesDir.'/'.$encodingDir; + + if (is_dir($sampleDir)) { + $fileFp = opendir($sampleDir); + while (false !== $sampleFile = readdir($fileFp)) { + if (substr($sampleFile, 0, 1) == '.') { + continue; + } + + $text = file_get_contents($sampleDir.'/'.$sampleFile); + + $os = new Swift_ByteStream_ArrayByteStream(); + $os->write($text); + + $is = new Swift_ByteStream_ArrayByteStream(); + $encoder->encodeByteStream($os, $is); + + $encoded = ''; + while (false !== $bytes = $is->read(8192)) { + $encoded .= $bytes; + } + + $this->assertEquals( + quoted_printable_decode($encoded), $text, + '%s: Encoded string should decode back to original string for sample '. + $sampleDir.'/'.$sampleFile + ); + } + closedir($fileFp); + } + } + closedir($sampleFp); + } + + public function testEncodingAndDecodingSamplesFromDiConfiguredInstance() + { + $sampleFp = opendir($this->_samplesDir); + while (false !== $encodingDir = readdir($sampleFp)) { + if (substr($encodingDir, 0, 1) == '.') { + continue; + } + + $encoding = $encodingDir; + $encoder = $this->_createEncoderFromContainer(); + + $sampleDir = $this->_samplesDir.'/'.$encodingDir; + + if (is_dir($sampleDir)) { + $fileFp = opendir($sampleDir); + while (false !== $sampleFile = readdir($fileFp)) { + if (substr($sampleFile, 0, 1) == '.') { + continue; + } + + $text = file_get_contents($sampleDir.'/'.$sampleFile); + + $os = new Swift_ByteStream_ArrayByteStream(); + $os->write($text); + + $is = new Swift_ByteStream_ArrayByteStream(); + $encoder->encodeByteStream($os, $is); + + $encoded = ''; + while (false !== $bytes = $is->read(8192)) { + $encoded .= $bytes; + } + + $this->assertEquals( + str_replace("\r\n", "\n", quoted_printable_decode($encoded)), str_replace("\r\n", "\n", $text), + '%s: Encoded string should decode back to original string for sample '. + $sampleDir.'/'.$sampleFile + ); + } + closedir($fileFp); + } + } + closedir($sampleFp); + } + + public function testEncodingLFTextWithDiConfiguredInstance() + { + $encoder = $this->_createEncoderFromContainer(); + $this->assertEquals("a\r\nb\r\nc", $encoder->encodeString("a\nb\nc")); + } + + public function testEncodingCRTextWithDiConfiguredInstance() + { + $encoder = $this->_createEncoderFromContainer(); + $this->assertEquals("a\r\nb\r\nc", $encoder->encodeString("a\rb\rc")); + } + + public function testEncodingLFCRTextWithDiConfiguredInstance() + { + $encoder = $this->_createEncoderFromContainer(); + $this->assertEquals("a\r\n\r\nb\r\n\r\nc", $encoder->encodeString("a\n\rb\n\rc")); + } + + public function testEncodingCRLFTextWithDiConfiguredInstance() + { + $encoder = $this->_createEncoderFromContainer(); + $this->assertEquals("a\r\nb\r\nc", $encoder->encodeString("a\r\nb\r\nc")); + } + + public function testEncodingDotStuffingWithDiConfiguredInstance() + { + // Enable DotEscaping + Swift_Preferences::getInstance()->setQPDotEscape(true); + $encoder = $this->_createEncoderFromContainer(); + $this->assertEquals("a=2E\r\n=2E\r\n=2Eb\r\nc", $encoder->encodeString("a.\r\n.\r\n.b\r\nc")); + // Return to default + Swift_Preferences::getInstance()->setQPDotEscape(false); + $encoder = $this->_createEncoderFromContainer(); + $this->assertEquals("a.\r\n.\r\n.b\r\nc", $encoder->encodeString("a.\r\n.\r\n.b\r\nc")); + } + + public function testDotStuffingEncodingAndDecodingSamplesFromDiConfiguredInstance() + { + // Enable DotEscaping + Swift_Preferences::getInstance()->setQPDotEscape(true); + $this->testEncodingAndDecodingSamplesFromDiConfiguredInstance(); + } + + private function _createEncoderFromContainer() + { + return Swift_DependencyContainer::getInstance() + ->lookup('mime.qpcontentencoder') + ; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/EmbeddedFileAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/EmbeddedFileAcceptanceTest.php new file mode 100644 index 0000000000..8a04df6b05 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/EmbeddedFileAcceptanceTest.php @@ -0,0 +1,138 @@ +_cache = new Swift_KeyCache_ArrayKeyCache( + new Swift_KeyCache_SimpleKeyCacheInputStream() + ); + $factory = new Swift_CharacterReaderFactory_SimpleCharacterReaderFactory(); + $this->_contentEncoder = new Swift_Mime_ContentEncoder_Base64ContentEncoder(); + + $headerEncoder = new Swift_Mime_HeaderEncoder_QpHeaderEncoder( + new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8') + ); + $paramEncoder = new Swift_Encoder_Rfc2231Encoder( + new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8') + ); + $this->_grammar = new Swift_Mime_Grammar(); + $this->_headers = new Swift_Mime_SimpleHeaderSet( + new Swift_Mime_SimpleHeaderFactory($headerEncoder, $paramEncoder, $this->_grammar) + ); + } + + public function testContentIdIsSetInHeader() + { + $file = $this->_createEmbeddedFile(); + $file->setContentType('application/pdf'); + $file->setId('foo@bar'); + $this->assertEquals( + 'Content-Type: application/pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-ID: '."\r\n". + 'Content-Disposition: inline'."\r\n", + $file->toString() + ); + } + + public function testDispositionIsSetInHeader() + { + $file = $this->_createEmbeddedFile(); + $id = $file->getId(); + $file->setContentType('application/pdf'); + $file->setDisposition('attachment'); + $this->assertEquals( + 'Content-Type: application/pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-ID: <'.$id.'>'."\r\n". + 'Content-Disposition: attachment'."\r\n", + $file->toString() + ); + } + + public function testFilenameIsSetInHeader() + { + $file = $this->_createEmbeddedFile(); + $id = $file->getId(); + $file->setContentType('application/pdf'); + $file->setFilename('foo.pdf'); + $this->assertEquals( + 'Content-Type: application/pdf; name=foo.pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-ID: <'.$id.'>'."\r\n". + 'Content-Disposition: inline; filename=foo.pdf'."\r\n", + $file->toString() + ); + } + + public function testSizeIsSetInHeader() + { + $file = $this->_createEmbeddedFile(); + $id = $file->getId(); + $file->setContentType('application/pdf'); + $file->setSize(12340); + $this->assertEquals( + 'Content-Type: application/pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-ID: <'.$id.'>'."\r\n". + 'Content-Disposition: inline; size=12340'."\r\n", + $file->toString() + ); + } + + public function testMultipleParametersInHeader() + { + $file = $this->_createEmbeddedFile(); + $id = $file->getId(); + $file->setContentType('application/pdf'); + $file->setFilename('foo.pdf'); + $file->setSize(12340); + + $this->assertEquals( + 'Content-Type: application/pdf; name=foo.pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-ID: <'.$id.'>'."\r\n". + 'Content-Disposition: inline; filename=foo.pdf; size=12340'."\r\n", + $file->toString() + ); + } + + public function testEndToEnd() + { + $file = $this->_createEmbeddedFile(); + $id = $file->getId(); + $file->setContentType('application/pdf'); + $file->setFilename('foo.pdf'); + $file->setSize(12340); + $file->setBody('abcd'); + $this->assertEquals( + 'Content-Type: application/pdf; name=foo.pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-ID: <'.$id.'>'."\r\n". + 'Content-Disposition: inline; filename=foo.pdf; size=12340'."\r\n". + "\r\n". + base64_encode('abcd'), + $file->toString() + ); + } + + // -- Private helpers + + protected function _createEmbeddedFile() + { + $entity = new Swift_Mime_EmbeddedFile( + $this->_headers, + $this->_contentEncoder, + $this->_cache, + $this->_grammar + ); + + return $entity; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/HeaderEncoder/Base64HeaderEncoderAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/HeaderEncoder/Base64HeaderEncoderAcceptanceTest.php new file mode 100644 index 0000000000..304867a484 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/HeaderEncoder/Base64HeaderEncoderAcceptanceTest.php @@ -0,0 +1,32 @@ +_encoder = new Swift_Mime_HeaderEncoder_Base64HeaderEncoder(); + } + + public function testEncodingJIS() + { + if (function_exists('mb_convert_encoding')) { + // base64_encode and split cannot handle long JIS text to fold + $subject = '長い長い長い長い長い長い長い長い長い長い長い長い長い長い長い長い長い長い長い長い件名'; + + $encodedWrapperLength = strlen('=?iso-2022-jp?'.$this->_encoder->getName().'??='); + + $old = mb_internal_encoding(); + mb_internal_encoding('utf-8'); + $newstring = mb_encode_mimeheader($subject, 'iso-2022-jp', 'B', "\r\n"); + mb_internal_encoding($old); + + $encoded = $this->_encoder->encodeString($subject, 0, 75 - $encodedWrapperLength, 'iso-2022-jp'); + $this->assertEquals( + $encoded, $newstring, + 'Encoded string should decode back to original string for sample ' + ); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/MimePartAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/MimePartAcceptanceTest.php new file mode 100644 index 0000000000..8232fe63bd --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/MimePartAcceptanceTest.php @@ -0,0 +1,129 @@ +_cache = new Swift_KeyCache_ArrayKeyCache( + new Swift_KeyCache_SimpleKeyCacheInputStream() + ); + $factory = new Swift_CharacterReaderFactory_SimpleCharacterReaderFactory(); + $this->_contentEncoder = new Swift_Mime_ContentEncoder_QpContentEncoder( + new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8'), + new Swift_StreamFilters_ByteArrayReplacementFilter( + array(array(0x0D, 0x0A), array(0x0D), array(0x0A)), + array(array(0x0A), array(0x0A), array(0x0D, 0x0A)) + ) + ); + + $headerEncoder = new Swift_Mime_HeaderEncoder_QpHeaderEncoder( + new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8') + ); + $paramEncoder = new Swift_Encoder_Rfc2231Encoder( + new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8') + ); + $this->_grammar = new Swift_Mime_Grammar(); + $this->_headers = new Swift_Mime_SimpleHeaderSet( + new Swift_Mime_SimpleHeaderFactory($headerEncoder, $paramEncoder, $this->_grammar) + ); + } + + public function testCharsetIsSetInHeader() + { + $part = $this->_createMimePart(); + $part->setContentType('text/plain'); + $part->setCharset('utf-8'); + $part->setBody('foobar'); + $this->assertEquals( + 'Content-Type: text/plain; charset=utf-8'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'foobar', + $part->toString() + ); + } + + public function testFormatIsSetInHeaders() + { + $part = $this->_createMimePart(); + $part->setContentType('text/plain'); + $part->setFormat('flowed'); + $part->setBody('> foobar'); + $this->assertEquals( + 'Content-Type: text/plain; format=flowed'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + '> foobar', + $part->toString() + ); + } + + public function testDelSpIsSetInHeaders() + { + $part = $this->_createMimePart(); + $part->setContentType('text/plain'); + $part->setDelSp(true); + $part->setBody('foobar'); + $this->assertEquals( + 'Content-Type: text/plain; delsp=yes'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'foobar', + $part->toString() + ); + } + + public function testAll3ParamsInHeaders() + { + $part = $this->_createMimePart(); + $part->setContentType('text/plain'); + $part->setCharset('utf-8'); + $part->setFormat('fixed'); + $part->setDelSp(true); + $part->setBody('foobar'); + $this->assertEquals( + 'Content-Type: text/plain; charset=utf-8; format=fixed; delsp=yes'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'foobar', + $part->toString() + ); + } + + public function testBodyIsCanonicalized() + { + $part = $this->_createMimePart(); + $part->setContentType('text/plain'); + $part->setCharset('utf-8'); + $part->setBody("foobar\r\rtest\ning\r"); + $this->assertEquals( + 'Content-Type: text/plain; charset=utf-8'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + "foobar\r\n". + "\r\n". + "test\r\n". + "ing\r\n", + $part->toString() + ); + } + + // -- Private helpers + + protected function _createMimePart() + { + $entity = new Swift_Mime_MimePart( + $this->_headers, + $this->_contentEncoder, + $this->_cache, + $this->_grammar + ); + + return $entity; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/SimpleMessageAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/SimpleMessageAcceptanceTest.php new file mode 100644 index 0000000000..cd6d910d60 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/SimpleMessageAcceptanceTest.php @@ -0,0 +1,1251 @@ +setCharset(null); //TODO: Test with the charset defined + } + + public function testBasicHeaders() + { + /* -- RFC 2822, 3.6. + */ + + $message = $this->_createMessage(); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'From: '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString(), + '%s: Only required headers, and non-empty headers should be displayed' + ); + } + + public function testSubjectIsDisplayedIfSet() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testDateCanBeSet() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $id = $message->getId(); + $message->setDate(1234); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', 1234)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testMessageIdCanBeSet() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setId('foo@bar'); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: '."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testContentTypeCanBeChanged() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setContentType('text/html'); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/html'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testCharsetCanBeSet() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setContentType('text/html'); + $message->setCharset('iso-8859-1'); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/html; charset=iso-8859-1'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testFormatCanBeSet() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setFormat('flowed'); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain; format=flowed'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testEncoderCanBeSet() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setContentType('text/html'); + $message->setEncoder( + new Swift_Mime_ContentEncoder_PlainContentEncoder('7bit') + ); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/html'."\r\n". + 'Content-Transfer-Encoding: 7bit'."\r\n", + $message->toString() + ); + } + + public function testFromAddressCanBeSet() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setFrom('chris.corbyn@swiftmailer.org'); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: chris.corbyn@swiftmailer.org'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testFromAddressCanBeSetWithName() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setFrom(array('chris.corbyn@swiftmailer.org' => 'Chris Corbyn')); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testMultipleFromAddressesCanBeSet() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', + 'mark@swiftmailer.org', + )); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn , mark@swiftmailer.org'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testReturnPathAddressCanBeSet() + { + $message = $this->_createMessage(); + $message->setReturnPath('chris@w3style.co.uk'); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Return-Path: '."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testEmptyReturnPathHeaderCanBeUsed() + { + $message = $this->_createMessage(); + $message->setReturnPath(''); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Return-Path: <>'."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testSenderCanBeSet() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setSender('chris.corbyn@swiftmailer.org'); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Sender: chris.corbyn@swiftmailer.org'."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testSenderCanBeSetWithName() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setSender(array('chris.corbyn@swiftmailer.org' => 'Chris')); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Sender: Chris '."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testReplyToCanBeSet() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setFrom(array('chris.corbyn@swiftmailer.org' => 'Chris')); + $message->setReplyTo(array('chris@w3style.co.uk' => 'Myself')); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris '."\r\n". + 'Reply-To: Myself '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testMultipleReplyAddressCanBeUsed() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setFrom(array('chris.corbyn@swiftmailer.org' => 'Chris')); + $message->setReplyTo(array( + 'chris@w3style.co.uk' => 'Myself', + 'my.other@address.com' => 'Me', + )); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris '."\r\n". + 'Reply-To: Myself , Me '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testToAddressCanBeSet() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setFrom(array('chris.corbyn@swiftmailer.org' => 'Chris')); + $message->setReplyTo(array( + 'chris@w3style.co.uk' => 'Myself', + 'my.other@address.com' => 'Me', + )); + $message->setTo('mark@swiftmailer.org'); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris '."\r\n". + 'Reply-To: Myself , Me '."\r\n". + 'To: mark@swiftmailer.org'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testMultipleToAddressesCanBeSet() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setFrom(array('chris.corbyn@swiftmailer.org' => 'Chris')); + $message->setReplyTo(array( + 'chris@w3style.co.uk' => 'Myself', + 'my.other@address.com' => 'Me', + )); + $message->setTo(array( + 'mark@swiftmailer.org', 'chris@swiftmailer.org' => 'Chris Corbyn', + )); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris '."\r\n". + 'Reply-To: Myself , Me '."\r\n". + 'To: mark@swiftmailer.org, Chris Corbyn '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testCcAddressCanBeSet() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setFrom(array('chris.corbyn@swiftmailer.org' => 'Chris')); + $message->setReplyTo(array( + 'chris@w3style.co.uk' => 'Myself', + 'my.other@address.com' => 'Me', + )); + $message->setTo(array( + 'mark@swiftmailer.org', 'chris@swiftmailer.org' => 'Chris Corbyn', + )); + $message->setCc('john@some-site.com'); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris '."\r\n". + 'Reply-To: Myself , Me '."\r\n". + 'To: mark@swiftmailer.org, Chris Corbyn '."\r\n". + 'Cc: john@some-site.com'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testMultipleCcAddressesCanBeSet() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setFrom(array('chris.corbyn@swiftmailer.org' => 'Chris')); + $message->setReplyTo(array( + 'chris@w3style.co.uk' => 'Myself', + 'my.other@address.com' => 'Me', + )); + $message->setTo(array( + 'mark@swiftmailer.org', 'chris@swiftmailer.org' => 'Chris Corbyn', + )); + $message->setCc(array( + 'john@some-site.com' => 'John West', + 'fred@another-site.co.uk' => 'Big Fred', + )); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris '."\r\n". + 'Reply-To: Myself , Me '."\r\n". + 'To: mark@swiftmailer.org, Chris Corbyn '."\r\n". + 'Cc: John West , Big Fred '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testBccAddressCanBeSet() + { + //Obviously Transports need to setBcc(array()) and send to each Bcc recipient + // separately in accordance with RFC 2822/2821 + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setFrom(array('chris.corbyn@swiftmailer.org' => 'Chris')); + $message->setReplyTo(array( + 'chris@w3style.co.uk' => 'Myself', + 'my.other@address.com' => 'Me', + )); + $message->setTo(array( + 'mark@swiftmailer.org', 'chris@swiftmailer.org' => 'Chris Corbyn', + )); + $message->setCc(array( + 'john@some-site.com' => 'John West', + 'fred@another-site.co.uk' => 'Big Fred', + )); + $message->setBcc('x@alphabet.tld'); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris '."\r\n". + 'Reply-To: Myself , Me '."\r\n". + 'To: mark@swiftmailer.org, Chris Corbyn '."\r\n". + 'Cc: John West , Big Fred '."\r\n". + 'Bcc: x@alphabet.tld'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testMultipleBccAddressesCanBeSet() + { + //Obviously Transports need to setBcc(array()) and send to each Bcc recipient + // separately in accordance with RFC 2822/2821 + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setFrom(array('chris.corbyn@swiftmailer.org' => 'Chris')); + $message->setReplyTo(array( + 'chris@w3style.co.uk' => 'Myself', + 'my.other@address.com' => 'Me', + )); + $message->setTo(array( + 'mark@swiftmailer.org', 'chris@swiftmailer.org' => 'Chris Corbyn', + )); + $message->setCc(array( + 'john@some-site.com' => 'John West', + 'fred@another-site.co.uk' => 'Big Fred', + )); + $message->setBcc(array('x@alphabet.tld', 'a@alphabet.tld' => 'A')); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris '."\r\n". + 'Reply-To: Myself , Me '."\r\n". + 'To: mark@swiftmailer.org, Chris Corbyn '."\r\n". + 'Cc: John West , Big Fred '."\r\n". + 'Bcc: x@alphabet.tld, A '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testStringBodyIsAppended() + { + $message = $this->_createMessage(); + $message->setReturnPath('chris@w3style.co.uk'); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + $message->setBody( + 'just a test body'."\r\n". + 'with a new line' + ); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Return-Path: '."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'just a test body'."\r\n". + 'with a new line', + $message->toString() + ); + } + + public function testStringBodyIsEncoded() + { + $message = $this->_createMessage(); + $message->setReturnPath('chris@w3style.co.uk'); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + $message->setBody( + 'Just s'.pack('C*', 0xC2, 0x01, 0x01).'me multi-'."\r\n". + 'line message!' + ); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Return-Path: '."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'Just s=C2=01=01me multi-'."\r\n". + 'line message!', + $message->toString() + ); + } + + public function testChildrenCanBeAttached() + { + $message = $this->_createMessage(); + $message->setReturnPath('chris@w3style.co.uk'); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + + $id = $message->getId(); + $date = $message->getDate(); + $boundary = $message->getBoundary(); + + $part1 = $this->_createMimePart(); + $part1->setContentType('text/plain'); + $part1->setCharset('iso-8859-1'); + $part1->setBody('foo'); + + $message->attach($part1); + + $part2 = $this->_createMimePart(); + $part2->setContentType('text/html'); + $part2->setCharset('iso-8859-1'); + $part2->setBody('test foo'); + + $message->attach($part2); + + $this->assertEquals( + 'Return-Path: '."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/alternative;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/plain; charset=iso-8859-1'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'foo'. + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/html; charset=iso-8859-1'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'test foo'. + "\r\n\r\n". + '--'.$boundary.'--'."\r\n", + $message->toString() + ); + } + + public function testAttachmentsBeingAttached() + { + $message = $this->_createMessage(); + $message->setReturnPath('chris@w3style.co.uk'); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + + $id = $message->getId(); + $date = preg_quote(date('r', $message->getDate()), '~'); + $boundary = $message->getBoundary(); + + $part = $this->_createMimePart(); + $part->setContentType('text/plain'); + $part->setCharset('iso-8859-1'); + $part->setBody('foo'); + + $message->attach($part); + + $attachment = $this->_createAttachment(); + $attachment->setContentType('application/pdf'); + $attachment->setFilename('foo.pdf'); + $attachment->setBody(''); + + $message->attach($attachment); + + $this->assertRegExp( + '~^'. + 'Return-Path: '."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.$date."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/mixed;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: multipart/alternative;'."\r\n". + ' boundary="(.*?)"'."\r\n". + "\r\n\r\n". + '--\\1'."\r\n". + 'Content-Type: text/plain; charset=iso-8859-1'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'foo'. + "\r\n\r\n". + '--\\1--'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: application/pdf; name=foo.pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: attachment; filename=foo.pdf'."\r\n". + "\r\n". + preg_quote(base64_encode(''), '~'). + "\r\n\r\n". + '--'.$boundary.'--'."\r\n". + '$~D', + $message->toString() + ); + } + + public function testAttachmentsAndEmbeddedFilesBeingAttached() + { + $message = $this->_createMessage(); + $message->setReturnPath('chris@w3style.co.uk'); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + + $id = $message->getId(); + $date = preg_quote(date('r', $message->getDate()), '~'); + $boundary = $message->getBoundary(); + + $part = $this->_createMimePart(); + $part->setContentType('text/plain'); + $part->setCharset('iso-8859-1'); + $part->setBody('foo'); + + $message->attach($part); + + $attachment = $this->_createAttachment(); + $attachment->setContentType('application/pdf'); + $attachment->setFilename('foo.pdf'); + $attachment->setBody(''); + + $message->attach($attachment); + + $file = $this->_createEmbeddedFile(); + $file->setContentType('image/jpeg'); + $file->setFilename('myimage.jpg'); + $file->setBody(''); + + $message->attach($file); + + $cid = $file->getId(); + + $this->assertRegExp( + '~^'. + 'Return-Path: '."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.$date."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/mixed;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: multipart/alternative;'."\r\n". + ' boundary="(.*?)"'."\r\n". + "\r\n\r\n". + '--\\1'."\r\n". + 'Content-Type: text/plain; charset=iso-8859-1'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'foo'. + + "\r\n\r\n". + '--\\1'."\r\n". + 'Content-Type: multipart/related;'."\r\n". + ' boundary="(.*?)"'."\r\n". + "\r\n\r\n". + '--\\2'."\r\n". + 'Content-Type: image/jpeg; name=myimage.jpg'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-ID: <'.$cid.'>'."\r\n". + 'Content-Disposition: inline; filename=myimage.jpg'."\r\n". + "\r\n". + preg_quote(base64_encode(''), '~'). + "\r\n\r\n". + '--\\2--'."\r\n". + "\r\n\r\n". + '--\\1--'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: application/pdf; name=foo.pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: attachment; filename=foo.pdf'."\r\n". + "\r\n". + preg_quote(base64_encode(''), '~'). + "\r\n\r\n". + '--'.$boundary.'--'."\r\n". + '$~D', + $message->toString() + ); + } + + public function testComplexEmbeddingOfContent() + { + $message = $this->_createMessage(); + $message->setReturnPath('chris@w3style.co.uk'); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + + $id = $message->getId(); + $date = preg_quote(date('r', $message->getDate()), '~'); + $boundary = $message->getBoundary(); + + $attachment = $this->_createAttachment(); + $attachment->setContentType('application/pdf'); + $attachment->setFilename('foo.pdf'); + $attachment->setBody(''); + + $message->attach($attachment); + + $file = $this->_createEmbeddedFile(); + $file->setContentType('image/jpeg'); + $file->setFilename('myimage.jpg'); + $file->setBody(''); + + $part = $this->_createMimePart(); + $part->setContentType('text/html'); + $part->setCharset('iso-8859-1'); + $part->setBody('foo '); + + $message->attach($part); + + $cid = $file->getId(); + + $this->assertRegExp( + '~^'. + 'Return-Path: '."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.$date."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/mixed;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: multipart/related;'."\r\n". + ' boundary="(.*?)"'."\r\n". + "\r\n\r\n". + '--\\1'."\r\n". + 'Content-Type: text/html; charset=iso-8859-1'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'foo './/=3D is just = in QP + "\r\n\r\n". + '--\\1'."\r\n". + 'Content-Type: image/jpeg; name=myimage.jpg'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-ID: <'.$cid.'>'."\r\n". + 'Content-Disposition: inline; filename=myimage.jpg'."\r\n". + "\r\n". + preg_quote(base64_encode(''), '~'). + "\r\n\r\n". + '--\\1--'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: application/pdf; name=foo.pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: attachment; filename=foo.pdf'."\r\n". + "\r\n". + preg_quote(base64_encode(''), '~'). + "\r\n\r\n". + '--'.$boundary.'--'."\r\n". + '$~D', + $message->toString() + ); + } + + public function testAttachingAndDetachingContent() + { + $message = $this->_createMessage(); + $message->setReturnPath('chris@w3style.co.uk'); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + + $id = $message->getId(); + $date = preg_quote(date('r', $message->getDate()), '~'); + $boundary = $message->getBoundary(); + + $part = $this->_createMimePart(); + $part->setContentType('text/plain'); + $part->setCharset('iso-8859-1'); + $part->setBody('foo'); + + $message->attach($part); + + $attachment = $this->_createAttachment(); + $attachment->setContentType('application/pdf'); + $attachment->setFilename('foo.pdf'); + $attachment->setBody(''); + + $message->attach($attachment); + + $file = $this->_createEmbeddedFile(); + $file->setContentType('image/jpeg'); + $file->setFilename('myimage.jpg'); + $file->setBody(''); + + $message->attach($file); + + $cid = $file->getId(); + + $message->detach($attachment); + + $this->assertRegExp( + '~^'. + 'Return-Path: '."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.$date."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/alternative;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/plain; charset=iso-8859-1'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'foo'. + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: multipart/related;'."\r\n". + ' boundary="(.*?)"'."\r\n". + "\r\n\r\n". + '--\\1'."\r\n". + 'Content-Type: image/jpeg; name=myimage.jpg'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-ID: <'.$cid.'>'."\r\n". + 'Content-Disposition: inline; filename=myimage.jpg'."\r\n". + "\r\n". + preg_quote(base64_encode(''), '~'). + "\r\n\r\n". + '--\\1--'."\r\n". + "\r\n\r\n". + '--'.$boundary.'--'."\r\n". + '$~D', + $message->toString(), + '%s: Attachment should have been detached' + ); + } + + public function testBoundaryDoesNotAppearAfterAllPartsAreDetached() + { + $message = $this->_createMessage(); + $message->setReturnPath('chris@w3style.co.uk'); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + + $id = $message->getId(); + $date = $message->getDate(); + $boundary = $message->getBoundary(); + + $part1 = $this->_createMimePart(); + $part1->setContentType('text/plain'); + $part1->setCharset('iso-8859-1'); + $part1->setBody('foo'); + + $message->attach($part1); + + $part2 = $this->_createMimePart(); + $part2->setContentType('text/html'); + $part2->setCharset('iso-8859-1'); + $part2->setBody('test foo'); + + $message->attach($part2); + + $message->detach($part1); + $message->detach($part2); + + $this->assertEquals( + 'Return-Path: '."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString(), + '%s: Message should be restored to orignal state after parts are detached' + ); + } + + public function testCharsetFormatOrDelSpAreNotShownWhenBoundaryIsSet() + { + $message = $this->_createMessage(); + $message->setReturnPath('chris@w3style.co.uk'); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + $message->setCharset('utf-8'); + $message->setFormat('flowed'); + $message->setDelSp(true); + + $id = $message->getId(); + $date = $message->getDate(); + $boundary = $message->getBoundary(); + + $part1 = $this->_createMimePart(); + $part1->setContentType('text/plain'); + $part1->setCharset('iso-8859-1'); + $part1->setBody('foo'); + + $message->attach($part1); + + $part2 = $this->_createMimePart(); + $part2->setContentType('text/html'); + $part2->setCharset('iso-8859-1'); + $part2->setBody('test foo'); + + $message->attach($part2); + + $this->assertEquals( + 'Return-Path: '."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/alternative;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/plain; charset=iso-8859-1'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'foo'. + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/html; charset=iso-8859-1'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'test foo'. + "\r\n\r\n". + '--'.$boundary.'--'."\r\n", + $message->toString() + ); + } + + public function testBodyCanBeSetWithAttachments() + { + $message = $this->_createMessage(); + $message->setReturnPath('chris@w3style.co.uk'); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + $message->setContentType('text/html'); + $message->setCharset('iso-8859-1'); + $message->setBody('foo'); + + $id = $message->getId(); + $date = date('r', $message->getDate()); + $boundary = $message->getBoundary(); + + $attachment = $this->_createAttachment(); + $attachment->setContentType('application/pdf'); + $attachment->setFilename('foo.pdf'); + $attachment->setBody(''); + + $message->attach($attachment); + + $this->assertEquals( + 'Return-Path: '."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.$date."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/mixed;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/html; charset=iso-8859-1'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'foo'. + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: application/pdf; name=foo.pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: attachment; filename=foo.pdf'."\r\n". + "\r\n". + base64_encode(''). + "\r\n\r\n". + '--'.$boundary.'--'."\r\n", + $message->toString() + ); + } + + public function testHtmlPartAlwaysAppearsLast() + { + $message = $this->_createMessage(); + $message->setReturnPath('chris@w3style.co.uk'); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + + $id = $message->getId(); + $date = date('r', $message->getDate()); + $boundary = $message->getBoundary(); + + $part1 = $this->_createMimePart(); + $part1->setContentType('text/html'); + $part1->setBody('foo'); + + $part2 = $this->_createMimePart(); + $part2->setContentType('text/plain'); + $part2->setBody('bar'); + + $message->attach($part1); + $message->attach($part2); + + $this->assertEquals( + 'Return-Path: '."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.$date."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/alternative;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'bar'. + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/html'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'foo'. + "\r\n\r\n". + '--'.$boundary.'--'."\r\n", + $message->toString() + ); + } + + public function testBodyBecomesPartIfOtherPartsAttached() + { + $message = $this->_createMessage(); + $message->setReturnPath('chris@w3style.co.uk'); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + $message->setContentType('text/html'); + $message->setBody('foo'); + + $id = $message->getId(); + $date = date('r', $message->getDate()); + $boundary = $message->getBoundary(); + + $part2 = $this->_createMimePart(); + $part2->setContentType('text/plain'); + $part2->setBody('bar'); + + $message->attach($part2); + + $this->assertEquals( + 'Return-Path: '."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.$date."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/alternative;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'bar'. + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/html'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'foo'. + "\r\n\r\n". + '--'.$boundary.'--'."\r\n", + $message->toString() + ); + } + + public function testBodyIsCanonicalized() + { + $message = $this->_createMessage(); + $message->setReturnPath('chris@w3style.co.uk'); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + $message->setBody( + 'just a test body'."\n". + 'with a new line' + ); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Return-Path: '."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'just a test body'."\r\n". + 'with a new line', + $message->toString() + ); + } + + // -- Private helpers + + protected function _createMessage() + { + return new Swift_Message(); + } + + protected function _createMimePart() + { + return new Swift_MimePart(); + } + + protected function _createAttachment() + { + return new Swift_Attachment(); + } + + protected function _createEmbeddedFile() + { + return new Swift_EmbeddedFile(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/MimePartAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/MimePartAcceptanceTest.php new file mode 100644 index 0000000000..f42405dfd1 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/MimePartAcceptanceTest.php @@ -0,0 +1,15 @@ +register('properties.charset')->asValue(null); + + return Swift_MimePart::newInstance(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/AbstractStreamBufferAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/AbstractStreamBufferAcceptanceTest.php new file mode 100644 index 0000000000..dc0d0a1de4 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/AbstractStreamBufferAcceptanceTest.php @@ -0,0 +1,133 @@ +markTestSkipped( + 'Will fail on travis-ci if not skipped due to travis blocking '. + 'socket mailing tcp connections.' + ); + } + + $this->_buffer = new Swift_Transport_StreamBuffer( + $this->getMockBuilder('Swift_ReplacementFilterFactory')->getMock() + ); + } + + public function testReadLine() + { + $this->_initializeBuffer(); + + $line = $this->_buffer->readLine(0); + $this->assertRegExp('/^[0-9]{3}.*?\r\n$/D', $line); + $seq = $this->_buffer->write("QUIT\r\n"); + $this->assertTrue((bool) $seq); + $line = $this->_buffer->readLine($seq); + $this->assertRegExp('/^[0-9]{3}.*?\r\n$/D', $line); + $this->_buffer->terminate(); + } + + public function testWrite() + { + $this->_initializeBuffer(); + + $line = $this->_buffer->readLine(0); + $this->assertRegExp('/^[0-9]{3}.*?\r\n$/D', $line); + + $seq = $this->_buffer->write("HELO foo\r\n"); + $this->assertTrue((bool) $seq); + $line = $this->_buffer->readLine($seq); + $this->assertRegExp('/^[0-9]{3}.*?\r\n$/D', $line); + + $seq = $this->_buffer->write("QUIT\r\n"); + $this->assertTrue((bool) $seq); + $line = $this->_buffer->readLine($seq); + $this->assertRegExp('/^[0-9]{3}.*?\r\n$/D', $line); + $this->_buffer->terminate(); + } + + public function testBindingOtherStreamsMirrorsWriteOperations() + { + $this->_initializeBuffer(); + + $is1 = $this->_createMockInputStream(); + $is2 = $this->_createMockInputStream(); + + $is1->expects($this->at(0)) + ->method('write') + ->with('x'); + $is1->expects($this->at(1)) + ->method('write') + ->with('y'); + $is2->expects($this->at(0)) + ->method('write') + ->with('x'); + $is2->expects($this->at(1)) + ->method('write') + ->with('y'); + + $this->_buffer->bind($is1); + $this->_buffer->bind($is2); + + $this->_buffer->write('x'); + $this->_buffer->write('y'); + } + + public function testBindingOtherStreamsMirrorsFlushOperations() + { + $this->_initializeBuffer(); + + $is1 = $this->_createMockInputStream(); + $is2 = $this->_createMockInputStream(); + + $is1->expects($this->once()) + ->method('flushBuffers'); + $is2->expects($this->once()) + ->method('flushBuffers'); + + $this->_buffer->bind($is1); + $this->_buffer->bind($is2); + + $this->_buffer->flushBuffers(); + } + + public function testUnbindingStreamPreventsFurtherWrites() + { + $this->_initializeBuffer(); + + $is1 = $this->_createMockInputStream(); + $is2 = $this->_createMockInputStream(); + + $is1->expects($this->at(0)) + ->method('write') + ->with('x'); + $is1->expects($this->at(1)) + ->method('write') + ->with('y'); + $is2->expects($this->once()) + ->method('write') + ->with('x'); + + $this->_buffer->bind($is1); + $this->_buffer->bind($is2); + + $this->_buffer->write('x'); + + $this->_buffer->unbind($is2); + + $this->_buffer->write('y'); + } + + // -- Creation Methods + + private function _createMockInputStream() + { + return $this->getMockBuilder('Swift_InputByteStream')->getMock(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/BasicSocketAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/BasicSocketAcceptanceTest.php new file mode 100644 index 0000000000..51c998c66e --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/BasicSocketAcceptanceTest.php @@ -0,0 +1,33 @@ +markTestSkipped( + 'Cannot run test without an SMTP host to connect to (define '. + 'SWIFT_SMTP_HOST in tests/acceptance.conf.php if you wish to run this test)' + ); + } + parent::setUp(); + } + + protected function _initializeBuffer() + { + $parts = explode(':', SWIFT_SMTP_HOST); + $host = $parts[0]; + $port = isset($parts[1]) ? $parts[1] : 25; + + $this->_buffer->initialize(array( + 'type' => Swift_Transport_IoBuffer::TYPE_SOCKET, + 'host' => $host, + 'port' => $port, + 'protocol' => 'tcp', + 'blocking' => 1, + 'timeout' => 15, + )); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/ProcessAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/ProcessAcceptanceTest.php new file mode 100644 index 0000000000..0e2924eb79 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/ProcessAcceptanceTest.php @@ -0,0 +1,26 @@ +markTestSkipped( + 'Cannot run test without a path to sendmail (define '. + 'SWIFT_SENDMAIL_PATH in tests/acceptance.conf.php if you wish to run this test)' + ); + } + + parent::setUp(); + } + + protected function _initializeBuffer() + { + $this->_buffer->initialize(array( + 'type' => Swift_Transport_IoBuffer::TYPE_PROCESS, + 'command' => SWIFT_SENDMAIL_PATH.' -bs', + )); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/SocketTimeoutTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/SocketTimeoutTest.php new file mode 100644 index 0000000000..d550f818c3 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/SocketTimeoutTest.php @@ -0,0 +1,67 @@ +markTestSkipped( + 'Cannot run test without an SMTP host to connect to (define '. + 'SWIFT_SMTP_HOST in tests/acceptance.conf.php if you wish to run this test)' + ); + } + + $serverStarted = false; + for ($i = 0; $i < 5; ++$i) { + $this->_randomHighPort = rand(50000, 65000); + $this->_server = stream_socket_server('tcp://127.0.0.1:'.$this->_randomHighPort); + if ($this->_server) { + $serverStarted = true; + } + } + + $this->_buffer = new Swift_Transport_StreamBuffer( + $this->getMockBuilder('Swift_ReplacementFilterFactory')->getMock() + ); + } + + protected function _initializeBuffer() + { + $host = '127.0.0.1'; + $port = $this->_randomHighPort; + + $this->_buffer->initialize(array( + 'type' => Swift_Transport_IoBuffer::TYPE_SOCKET, + 'host' => $host, + 'port' => $port, + 'protocol' => 'tcp', + 'blocking' => 1, + 'timeout' => 1, + )); + } + + public function testTimeoutException() + { + $this->_initializeBuffer(); + $e = null; + try { + $line = $this->_buffer->readLine(0); + } catch (Exception $e) { + } + $this->assertInstanceof('Swift_IoException', $e, 'IO Exception Not Thrown On Connection Timeout'); + $this->assertRegExp('/Connection to .* Timed Out/', $e->getMessage()); + } + + public function tearDown() + { + if ($this->_server) { + stream_socket_shutdown($this->_server, STREAM_SHUT_RDWR); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/SslSocketAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/SslSocketAcceptanceTest.php new file mode 100644 index 0000000000..2863f96550 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/SslSocketAcceptanceTest.php @@ -0,0 +1,40 @@ +markTestSkipped( + 'SSL is not configured for your system. It is not possible to run this test' + ); + } + if (!defined('SWIFT_SSL_HOST')) { + $this->markTestSkipped( + 'Cannot run test without an SSL enabled SMTP host to connect to (define '. + 'SWIFT_SSL_HOST in tests/acceptance.conf.php if you wish to run this test)' + ); + } + + parent::setUp(); + } + + protected function _initializeBuffer() + { + $parts = explode(':', SWIFT_SSL_HOST); + $host = $parts[0]; + $port = isset($parts[1]) ? $parts[1] : 25; + + $this->_buffer->initialize(array( + 'type' => Swift_Transport_IoBuffer::TYPE_SOCKET, + 'host' => $host, + 'port' => $port, + 'protocol' => 'ssl', + 'blocking' => 1, + 'timeout' => 15, + )); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/TlsSocketAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/TlsSocketAcceptanceTest.php new file mode 100644 index 0000000000..6fc8505755 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/TlsSocketAcceptanceTest.php @@ -0,0 +1,39 @@ +markTestSkipped( + 'TLS is not configured for your system. It is not possible to run this test' + ); + } + if (!defined('SWIFT_TLS_HOST')) { + $this->markTestSkipped( + 'Cannot run test without a TLS enabled SMTP host to connect to (define '. + 'SWIFT_TLS_HOST in tests/acceptance.conf.php if you wish to run this test)' + ); + } + parent::setUp(); + } + + protected function _initializeBuffer() + { + $parts = explode(':', SWIFT_TLS_HOST); + $host = $parts[0]; + $port = isset($parts[1]) ? $parts[1] : 25; + + $this->_buffer->initialize(array( + 'type' => Swift_Transport_IoBuffer::TYPE_SOCKET, + 'host' => $host, + 'port' => $port, + 'protocol' => 'tls', + 'blocking' => 1, + 'timeout' => 15, + )); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/bootstrap.php b/vendor/swiftmailer/swiftmailer/tests/bootstrap.php new file mode 100644 index 0000000000..27091a28e0 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bootstrap.php @@ -0,0 +1,21 @@ +allowMockingNonExistentMethods(true); + +if (is_file(__DIR__.'/acceptance.conf.php')) { + require_once __DIR__.'/acceptance.conf.php'; +} +if (is_file(__DIR__.'/smoke.conf.php')) { + require_once __DIR__.'/smoke.conf.php'; +} +require_once __DIR__.'/StreamCollector.php'; +require_once __DIR__.'/IdenticalBinaryConstraint.php'; +require_once __DIR__.'/SwiftMailerTestCase.php'; +require_once __DIR__.'/SwiftMailerSmokeTestCase.php'; diff --git a/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug111Test.php b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug111Test.php new file mode 100644 index 0000000000..ba29ba87a9 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug111Test.php @@ -0,0 +1,42 @@ + array( + 'email1@example.com', + 'email2@example.com', + 'email3@example.com', + 'email4@example.com', + 'email5@example.com', + ), + 'sub' => array( + '-name-' => array( + 'email1', + '"email2"', + 'email3\\', + 'email4', + 'email5', + ), + '-url-' => array( + 'http://google.com', + 'http://yahoo.com', + 'http://hotmail.com', + 'http://aol.com', + 'http://facebook.com', + ), + ), + ); + $json = json_encode($complicated_header); + + $message = new Swift_Message(); + $headers = $message->getHeaders(); + $headers->addTextHeader('X-SMTPAPI', $json); + $header = $headers->get('X-SMTPAPI'); + + $this->assertEquals('Swift_Mime_Headers_UnstructuredHeader', get_class($header)); + $this->assertEquals($json, $header->getFieldBody()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug118Test.php b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug118Test.php new file mode 100644 index 0000000000..bd10c71637 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug118Test.php @@ -0,0 +1,20 @@ +_message = new Swift_Message(); + } + + public function testCallingGenerateIdChangesTheMessageId() + { + $currentId = $this->_message->getId(); + $this->_message->generateId(); + $newId = $this->_message->getId(); + + $this->assertNotEquals($currentId, $newId); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug206Test.php b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug206Test.php new file mode 100644 index 0000000000..fdfa5302c6 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug206Test.php @@ -0,0 +1,38 @@ +_factory = new Swift_Mime_SimpleHeaderFactory($headerEncoder, $paramEncoder, $grammar); + } + + public function testMailboxHeaderEncoding() + { + $this->_testHeaderIsFullyEncoded('email@example.org', 'Family Name, Name', ' "Family Name, Name" '); + $this->_testHeaderIsFullyEncoded('email@example.org', 'Family Namé, Name', ' Family =?utf-8?Q?Nam=C3=A9=2C?= Name'); + $this->_testHeaderIsFullyEncoded('email@example.org', 'Family Namé , Name', ' Family =?utf-8?Q?Nam=C3=A9_=2C?= Name'); + $this->_testHeaderIsFullyEncoded('email@example.org', 'Family Namé ;Name', ' Family =?utf-8?Q?Nam=C3=A9_=3BName?= '); + } + + private function _testHeaderIsFullyEncoded($email, $name, $expected) + { + $mailboxHeader = $this->_factory->createMailboxHeader('To', array( + $email => $name, + )); + + $headerBody = substr($mailboxHeader->toString(), 3, strlen($expected)); + + $this->assertEquals($expected, $headerBody); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug274Test.php b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug274Test.php new file mode 100644 index 0000000000..f5f057aeed --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug274Test.php @@ -0,0 +1,21 @@ +setExpectedException('Swift_IoException', 'The path cannot be empty'); + $message->attach(Swift_Attachment::fromPath('')); + } + + public function testNonEmptyFileNameAsAttachment() + { + $message = new Swift_Message(); + try { + $message->attach(Swift_Attachment::fromPath(__FILE__)); + } catch (Exception $e) { + $this->fail('Path should not be empty'); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug34Test.php b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug34Test.php new file mode 100644 index 0000000000..9c76ceb3dd --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug34Test.php @@ -0,0 +1,75 @@ +setCharset('utf-8'); + } + + public function testEmbeddedFilesWithMultipartDataCreateMultipartRelatedContentAsAnAlternative() + { + $message = Swift_Message::newInstance(); + $message->setCharset('utf-8'); + $message->setSubject('test subject'); + $message->addPart('plain part', 'text/plain'); + + $image = Swift_Image::newInstance('', 'image.gif', 'image/gif'); + $cid = $message->embed($image); + + $message->setBody('', 'text/html'); + + $message->setTo(array('user@domain.tld' => 'User')); + + $message->setFrom(array('other@domain.tld' => 'Other')); + $message->setSender(array('other@domain.tld' => 'Other')); + + $id = $message->getId(); + $date = preg_quote(date('r', $message->getDate()), '~'); + $boundary = $message->getBoundary(); + $cidVal = $image->getId(); + + $this->assertRegExp( + '~^'. + 'Sender: Other '."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.$date."\r\n". + 'Subject: test subject'."\r\n". + 'From: Other '."\r\n". + 'To: User '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/alternative;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/plain; charset=utf-8'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'plain part'. + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: multipart/related;'."\r\n". + ' boundary="(.*?)"'."\r\n". + "\r\n\r\n". + '--\\1'."\r\n". + 'Content-Type: text/html; charset=utf-8'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + ''. + "\r\n\r\n". + '--\\1'."\r\n". + 'Content-Type: image/gif; name=image.gif'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-ID: <'.$cidVal.'>'."\r\n". + 'Content-Disposition: inline; filename=image.gif'."\r\n". + "\r\n". + preg_quote(base64_encode(''), '~'). + "\r\n\r\n". + '--\\1--'."\r\n". + "\r\n\r\n". + '--'.$boundary.'--'."\r\n". + '$~D', + $message->toString() + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug35Test.php b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug35Test.php new file mode 100644 index 0000000000..e07ee8f0bb --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug35Test.php @@ -0,0 +1,73 @@ +setCharset('utf-8'); + } + + public function testHTMLPartAppearsLastEvenWhenAttachmentsAdded() + { + $message = Swift_Message::newInstance(); + $message->setCharset('utf-8'); + $message->setSubject('test subject'); + $message->addPart('plain part', 'text/plain'); + + $attachment = Swift_Attachment::newInstance('', 'image.gif', 'image/gif'); + $message->attach($attachment); + + $message->setBody('HTML part', 'text/html'); + + $message->setTo(array('user@domain.tld' => 'User')); + + $message->setFrom(array('other@domain.tld' => 'Other')); + $message->setSender(array('other@domain.tld' => 'Other')); + + $id = $message->getId(); + $date = preg_quote(date('r', $message->getDate()), '~'); + $boundary = $message->getBoundary(); + + $this->assertRegExp( + '~^'. + 'Sender: Other '."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.$date."\r\n". + 'Subject: test subject'."\r\n". + 'From: Other '."\r\n". + 'To: User '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/mixed;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: multipart/alternative;'."\r\n". + ' boundary="(.*?)"'."\r\n". + "\r\n\r\n". + '--\\1'."\r\n". + 'Content-Type: text/plain; charset=utf-8'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'plain part'. + "\r\n\r\n". + '--\\1'."\r\n". + 'Content-Type: text/html; charset=utf-8'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'HTML part'. + "\r\n\r\n". + '--\\1--'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: image/gif; name=image.gif'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: attachment; filename=image.gif'."\r\n". + "\r\n". + preg_quote(base64_encode(''), '~'). + "\r\n\r\n". + '--'.$boundary.'--'."\r\n". + '$~D', + $message->toString() + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug38Test.php b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug38Test.php new file mode 100644 index 0000000000..a8a9212cff --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug38Test.php @@ -0,0 +1,194 @@ +_attFileName = 'data.txt'; + $this->_attFileType = 'text/plain'; + $this->_attFile = __DIR__.'/../../_samples/files/data.txt'; + Swift_Preferences::getInstance()->setCharset('utf-8'); + } + + public function testWritingMessageToByteStreamProducesCorrectStructure() + { + $message = new Swift_Message(); + $message->setSubject('test subject'); + $message->setTo('user@domain.tld'); + $message->setCc('other@domain.tld'); + $message->setFrom('user@domain.tld'); + + $image = new Swift_Image('', 'image.gif', 'image/gif'); + + $cid = $message->embed($image); + $message->setBody('HTML part', 'text/html'); + + $id = $message->getId(); + $date = preg_quote(date('r', $message->getDate()), '~'); + $boundary = $message->getBoundary(); + $imgId = $image->getId(); + + $stream = new Swift_ByteStream_ArrayByteStream(); + + $message->toByteStream($stream); + + $this->assertPatternInStream( + '~^'. + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.$date."\r\n". + 'Subject: test subject'."\r\n". + 'From: user@domain.tld'."\r\n". + 'To: user@domain.tld'."\r\n". + 'Cc: other@domain.tld'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/related;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/html; charset=utf-8'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'HTML part'. + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: image/gif; name=image.gif'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-ID: <'.preg_quote($imgId, '~').'>'."\r\n". + 'Content-Disposition: inline; filename=image.gif'."\r\n". + "\r\n". + preg_quote(base64_encode(''), '~'). + "\r\n\r\n". + '--'.$boundary.'--'."\r\n". + '$~D', + $stream + ); + } + + public function testWritingMessageToByteStreamTwiceProducesCorrectStructure() + { + $message = new Swift_Message(); + $message->setSubject('test subject'); + $message->setTo('user@domain.tld'); + $message->setCc('other@domain.tld'); + $message->setFrom('user@domain.tld'); + + $image = new Swift_Image('', 'image.gif', 'image/gif'); + + $cid = $message->embed($image); + $message->setBody('HTML part', 'text/html'); + + $id = $message->getId(); + $date = preg_quote(date('r', $message->getDate()), '~'); + $boundary = $message->getBoundary(); + $imgId = $image->getId(); + + $pattern = '~^'. + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.$date."\r\n". + 'Subject: test subject'."\r\n". + 'From: user@domain.tld'."\r\n". + 'To: user@domain.tld'."\r\n". + 'Cc: other@domain.tld'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/related;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/html; charset=utf-8'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'HTML part'. + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: image/gif; name=image.gif'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-ID: <'.preg_quote($imgId, '~').'>'."\r\n". + 'Content-Disposition: inline; filename=image.gif'."\r\n". + "\r\n". + preg_quote(base64_encode(''), '~'). + "\r\n\r\n". + '--'.$boundary.'--'."\r\n". + '$~D' + ; + + $streamA = new Swift_ByteStream_ArrayByteStream(); + $streamB = new Swift_ByteStream_ArrayByteStream(); + + $message->toByteStream($streamA); + $message->toByteStream($streamB); + + $this->assertPatternInStream($pattern, $streamA); + $this->assertPatternInStream($pattern, $streamB); + } + + public function testWritingMessageToByteStreamTwiceUsingAFileAttachment() + { + $message = new Swift_Message(); + $message->setSubject('test subject'); + $message->setTo('user@domain.tld'); + $message->setCc('other@domain.tld'); + $message->setFrom('user@domain.tld'); + + $attachment = Swift_Attachment::fromPath($this->_attFile); + + $message->attach($attachment); + + $message->setBody('HTML part', 'text/html'); + + $id = $message->getId(); + $date = preg_quote(date('r', $message->getDate()), '~'); + $boundary = $message->getBoundary(); + + $streamA = new Swift_ByteStream_ArrayByteStream(); + $streamB = new Swift_ByteStream_ArrayByteStream(); + + $pattern = '~^'. + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.$date."\r\n". + 'Subject: test subject'."\r\n". + 'From: user@domain.tld'."\r\n". + 'To: user@domain.tld'."\r\n". + 'Cc: other@domain.tld'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/mixed;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/html; charset=utf-8'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'HTML part'. + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: '.$this->_attFileType.'; name='.$this->_attFileName."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: attachment; filename='.$this->_attFileName."\r\n". + "\r\n". + preg_quote(base64_encode(file_get_contents($this->_attFile)), '~'). + "\r\n\r\n". + '--'.$boundary.'--'."\r\n". + '$~D' + ; + + $message->toByteStream($streamA); + $message->toByteStream($streamB); + + $this->assertPatternInStream($pattern, $streamA); + $this->assertPatternInStream($pattern, $streamB); + } + + // -- Helpers + + public function assertPatternInStream($pattern, $stream, $message = '%s') + { + $string = ''; + while (false !== $bytes = $stream->read(8192)) { + $string .= $bytes; + } + $this->assertRegExp($pattern, $string, $message); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug518Test.php b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug518Test.php new file mode 100644 index 0000000000..b83984fec9 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug518Test.php @@ -0,0 +1,38 @@ +setTo('foo@bar.com'); + + $that = $this; + $messageValidation = function ($m) use ($that) { + //the getTo should return the same value as we put in + $that->assertEquals('foo@bar.com', key($m->getTo()), 'The message has changed after it was put to the memory queue'); + + return true; + }; + + $transport = m::mock('Swift_Transport'); + $transport->shouldReceive('isStarted')->andReturn(true); + $transport->shouldReceive('send') + ->with(m::on($messageValidation), $failedRecipients) + ->andReturn(1); + + $memorySpool = new Swift_MemorySpool(); + $memorySpool->queueMessage($message); + + /* + * The message is queued in memory. + * Lets change the message + */ + $message->setTo('other@value.com'); + + $memorySpool->flushQueue($transport, $failedRecipients); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug51Test.php b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug51Test.php new file mode 100644 index 0000000000..b9c33b09d3 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug51Test.php @@ -0,0 +1,121 @@ +markTestSkipped( + 'Cannot run test without a writable directory to use ('. + 'define SWIFT_TMP_DIR in tests/config.php if you wish to run this test)' + ); + } + + $this->_attachmentFile = SWIFT_TMP_DIR.'/attach.rand.bin'; + file_put_contents($this->_attachmentFile, ''); + + $this->_outputFile = SWIFT_TMP_DIR.'/attach.out.bin'; + file_put_contents($this->_outputFile, ''); + } + + public function tearDown() + { + unlink($this->_attachmentFile); + unlink($this->_outputFile); + } + + public function testAttachmentsDoNotGetTruncatedUsingToByteStream() + { + //Run 100 times with 10KB attachments + for ($i = 0; $i < 10; ++$i) { + $message = $this->_createMessageWithRandomAttachment( + 10000, $this->_attachmentFile + ); + + file_put_contents($this->_outputFile, ''); + $message->toByteStream( + new Swift_ByteStream_FileByteStream($this->_outputFile, true) + ); + + $emailSource = file_get_contents($this->_outputFile); + + $this->assertAttachmentFromSourceMatches( + file_get_contents($this->_attachmentFile), + $emailSource + ); + } + } + + public function testAttachmentsDoNotGetTruncatedUsingToString() + { + //Run 100 times with 10KB attachments + for ($i = 0; $i < 10; ++$i) { + $message = $this->_createMessageWithRandomAttachment( + 10000, $this->_attachmentFile + ); + + $emailSource = $message->toString(); + + $this->assertAttachmentFromSourceMatches( + file_get_contents($this->_attachmentFile), + $emailSource + ); + } + } + + // -- Custom Assertions + + public function assertAttachmentFromSourceMatches($attachmentData, $source) + { + $encHeader = 'Content-Transfer-Encoding: base64'; + $base64declaration = strpos($source, $encHeader); + + $attachmentDataStart = strpos($source, "\r\n\r\n", $base64declaration); + $attachmentDataEnd = strpos($source, "\r\n--", $attachmentDataStart); + + if (false === $attachmentDataEnd) { + $attachmentBase64 = trim(substr($source, $attachmentDataStart)); + } else { + $attachmentBase64 = trim(substr( + $source, $attachmentDataStart, + $attachmentDataEnd - $attachmentDataStart + )); + } + + $this->assertIdenticalBinary($attachmentData, base64_decode($attachmentBase64)); + } + + // -- Creation Methods + + private function _fillFileWithRandomBytes($byteCount, $file) + { + // I was going to use dd with if=/dev/random but this way seems more + // cross platform even if a hella expensive!! + + file_put_contents($file, ''); + $fp = fopen($file, 'wb'); + for ($i = 0; $i < $byteCount; ++$i) { + $byteVal = rand(0, 255); + fwrite($fp, pack('i', $byteVal)); + } + fclose($fp); + } + + private function _createMessageWithRandomAttachment($size, $attachmentPath) + { + $this->_fillFileWithRandomBytes($size, $attachmentPath); + + $message = Swift_Message::newInstance() + ->setSubject('test') + ->setBody('test') + ->setFrom('a@b.c') + ->setTo('d@e.f') + ->attach(Swift_Attachment::fromPath($attachmentPath)) + ; + + return $message; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug534Test.php b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug534Test.php new file mode 100644 index 0000000000..263cae51ac --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug534Test.php @@ -0,0 +1,38 @@ +setFrom('from@example.com') + ->setTo('to@example.com') + ->setSubject('test') + ; + $cid = $message->embed(Swift_Image::fromPath(__DIR__.'/../../_samples/files/swiftmailer.png')); + $message->setBody('', 'text/html'); + + $that = $this; + $messageValidation = function (Swift_Mime_Message $message) use ($that) { + preg_match('/cid:(.*)"/', $message->toString(), $matches); + $cid = $matches[1]; + preg_match('/Content-ID: <(.*)>/', $message->toString(), $matches); + $contentId = $matches[1]; + $that->assertEquals($cid, $contentId, 'cid in body and mime part Content-ID differ'); + + return true; + }; + + $failedRecipients = array(); + + $transport = m::mock('Swift_Transport'); + $transport->shouldReceive('isStarted')->andReturn(true); + $transport->shouldReceive('send')->with(m::on($messageValidation), $failedRecipients)->andReturn(1); + + $memorySpool = new Swift_MemorySpool(); + $memorySpool->queueMessage($message); + $memorySpool->flushQueue($transport, $failedRecipients); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug650Test.php b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug650Test.php new file mode 100644 index 0000000000..3393fb8001 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug650Test.php @@ -0,0 +1,36 @@ +setCharset('utf-8'); + + $header->setNameAddresses(array( + 'test@example.com' => $name, + )); + + $this->assertSame('To: '.$expectedEncodedName." \r\n", $header->toString()); + } + + public function encodingDataProvider() + { + return array( + array('this is " a test ö', 'this is =?utf-8?Q?=22?= a test =?utf-8?Q?=C3=B6?='), + array(': this is a test ö', '=?utf-8?Q?=3A?= this is a test =?utf-8?Q?=C3=B6?='), + array('( test ö', '=?utf-8?Q?=28?= test =?utf-8?Q?=C3=B6?='), + array('[ test ö', '=?utf-8?Q?=5B?= test =?utf-8?Q?=C3=B6?='), + array('@ test ö)', '=?utf-8?Q?=40?= test =?utf-8?Q?=C3=B6=29?='), + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug71Test.php b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug71Test.php new file mode 100644 index 0000000000..4b80453704 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug71Test.php @@ -0,0 +1,20 @@ +_message = new Swift_Message('test'); + } + + public function testCallingToStringAfterSettingNewBodyReflectsChanges() + { + $this->_message->setBody('BODY1'); + $this->assertRegExp('/BODY1/', $this->_message->toString()); + + $this->_message->setBody('BODY2'); + $this->assertRegExp('/BODY2/', $this->_message->toString()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug76Test.php b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug76Test.php new file mode 100644 index 0000000000..c2b12cb4c8 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug76Test.php @@ -0,0 +1,82 @@ +markTestSkipped( + 'Cannot run test without a writable directory to use ('. + 'define SWIFT_TMP_DIR in tests/config.php if you wish to run this test)' + ); + } + + $this->_inputFile = SWIFT_TMP_DIR.'/in.bin'; + file_put_contents($this->_inputFile, ''); + + $this->_outputFile = SWIFT_TMP_DIR.'/out.bin'; + file_put_contents($this->_outputFile, ''); + + $this->_encoder = $this->_createEncoder(); + } + + public function tearDown() + { + unlink($this->_inputFile); + unlink($this->_outputFile); + } + + public function testBase64EncodedLineLengthNeverExceeds76CharactersEvenIfArgsDo() + { + $this->_fillFileWithRandomBytes(1000, $this->_inputFile); + + $os = $this->_createStream($this->_inputFile); + $is = $this->_createStream($this->_outputFile); + + $this->_encoder->encodeByteStream($os, $is, 0, 80); //Exceeds 76 + + $this->assertMaxLineLength(76, $this->_outputFile, + '%s: Line length should not exceed 76 characters' + ); + } + + // -- Custom Assertions + + public function assertMaxLineLength($length, $filePath, $message = '%s') + { + $lines = file($filePath); + foreach ($lines as $line) { + $this->assertTrue((strlen(trim($line)) <= 76), $message); + } + } + + // -- Creation Methods + + private function _fillFileWithRandomBytes($byteCount, $file) + { + // I was going to use dd with if=/dev/random but this way seems more + // cross platform even if a hella expensive!! + + file_put_contents($file, ''); + $fp = fopen($file, 'wb'); + for ($i = 0; $i < $byteCount; ++$i) { + $byteVal = rand(0, 255); + fwrite($fp, pack('i', $byteVal)); + } + fclose($fp); + } + + private function _createEncoder() + { + return new Swift_Mime_ContentEncoder_Base64ContentEncoder(); + } + + private function _createStream($file) + { + return new Swift_ByteStream_FileByteStream($file, true); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/bug/Swift/BugFileByteStreamConsecutiveReadCallsTest.php b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/BugFileByteStreamConsecutiveReadCallsTest.php new file mode 100644 index 0000000000..35733ec55a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/BugFileByteStreamConsecutiveReadCallsTest.php @@ -0,0 +1,19 @@ +read(100); + } catch (\Swift_IoException $exc) { + $fbs->read(100); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/fixtures/MimeEntityFixture.php b/vendor/swiftmailer/swiftmailer/tests/fixtures/MimeEntityFixture.php new file mode 100644 index 0000000000..e2cf86a590 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/fixtures/MimeEntityFixture.php @@ -0,0 +1,59 @@ +level = $level; + $this->string = $string; + $this->contentType = $contentType; + } + + public function getNestingLevel() + { + return $this->level; + } + + public function toString() + { + return $this->string; + } + + public function getContentType() + { + return $this->contentType; + } + + // These methods are here to account for the implemented interfaces + public function getId() + { + } + public function getHeaders() + { + } + public function getBody() + { + } + public function setBody($body, $contentType = null) + { + } + public function toByteStream(Swift_InputByteStream $is) + { + } + public function charsetChanged($charset) + { + } + public function encoderChanged(Swift_Mime_ContentEncoder $encoder) + { + } + public function getChildren() + { + } + public function setChildren(array $children) + { + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/smoke.conf.php.default b/vendor/swiftmailer/swiftmailer/tests/smoke.conf.php.default new file mode 100644 index 0000000000..0de2763fa1 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/smoke.conf.php.default @@ -0,0 +1,63 @@ +_attFile = __DIR__.'/../../../_samples/files/textfile.zip'; + } + + public function testAttachmentSending() + { + $mailer = $this->_getMailer(); + $message = Swift_Message::newInstance() + ->setSubject('[Swift Mailer] AttachmentSmokeTest') + ->setFrom(array(SWIFT_SMOKE_EMAIL_ADDRESS => 'Swift Mailer')) + ->setTo(SWIFT_SMOKE_EMAIL_ADDRESS) + ->setBody('This message should contain an attached ZIP file (named "textfile.zip").'.PHP_EOL. + 'When unzipped, the archive should produce a text file which reads:'.PHP_EOL. + '"This is part of a Swift Mailer v4 smoke test."' + ) + ->attach(Swift_Attachment::fromPath($this->_attFile)) + ; + $this->assertEquals(1, $mailer->send($message), + '%s: The smoke test should send a single message' + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/smoke/Swift/Smoke/BasicSmokeTest.php b/vendor/swiftmailer/swiftmailer/tests/smoke/Swift/Smoke/BasicSmokeTest.php new file mode 100644 index 0000000000..c7501d46e3 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/smoke/Swift/Smoke/BasicSmokeTest.php @@ -0,0 +1,23 @@ +_getMailer(); + $message = Swift_Message::newInstance() + ->setSubject('[Swift Mailer] BasicSmokeTest') + ->setFrom(array(SWIFT_SMOKE_EMAIL_ADDRESS => 'Swift Mailer')) + ->setTo(SWIFT_SMOKE_EMAIL_ADDRESS) + ->setBody('One, two, three, four, five...'.PHP_EOL. + 'six, seven, eight...' + ) + ; + $this->assertEquals(1, $mailer->send($message), + '%s: The smoke test should send a single message' + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/smoke/Swift/Smoke/HtmlWithAttachmentSmokeTest.php b/vendor/swiftmailer/swiftmailer/tests/smoke/Swift/Smoke/HtmlWithAttachmentSmokeTest.php new file mode 100644 index 0000000000..dca14341df --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/smoke/Swift/Smoke/HtmlWithAttachmentSmokeTest.php @@ -0,0 +1,31 @@ +_attFile = __DIR__.'/../../../_samples/files/textfile.zip'; + } + + public function testAttachmentSending() + { + $mailer = $this->_getMailer(); + $message = Swift_Message::newInstance('[Swift Mailer] HtmlWithAttachmentSmokeTest') + ->setFrom(array(SWIFT_SMOKE_EMAIL_ADDRESS => 'Swift Mailer')) + ->setTo(SWIFT_SMOKE_EMAIL_ADDRESS) + ->attach(Swift_Attachment::fromPath($this->_attFile)) + ->setBody('

This HTML-formatted message should contain an attached ZIP file (named "textfile.zip").'.PHP_EOL. + 'When unzipped, the archive should produce a text file which reads:

'.PHP_EOL. + '

This is part of a Swift Mailer v4 smoke test.

', 'text/html' + ) + ; + $this->assertEquals(1, $mailer->send($message), + '%s: The smoke test should send a single message' + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/smoke/Swift/Smoke/InternationalSmokeTest.php b/vendor/swiftmailer/swiftmailer/tests/smoke/Swift/Smoke/InternationalSmokeTest.php new file mode 100644 index 0000000000..fafd72714c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/smoke/Swift/Smoke/InternationalSmokeTest.php @@ -0,0 +1,40 @@ +_attFile = __DIR__.'/../../../_samples/files/textfile.zip'; + } + + public function testAttachmentSending() + { + $mailer = $this->_getMailer(); + $message = Swift_Message::newInstance() + ->setCharset('utf-8') + ->setSubject('[Swift Mailer] InternationalSmokeTest (διεθνής)') + ->setFrom(array(SWIFT_SMOKE_EMAIL_ADDRESS => 'Χριστοφορου (Swift Mailer)')) + ->setTo(SWIFT_SMOKE_EMAIL_ADDRESS) + ->setBody('This message should contain an attached ZIP file (named "κείμενο, εδάφιο, θέμα.zip").'.PHP_EOL. + 'When unzipped, the archive should produce a text file which reads:'.PHP_EOL. + '"This is part of a Swift Mailer v4 smoke test."'.PHP_EOL. + PHP_EOL. + 'Following is some arbitrary Greek text:'.PHP_EOL. + 'Δεν βρέθηκαν λέξεις.' + ) + ->attach(Swift_Attachment::fromPath($this->_attFile) + ->setContentType('application/zip') + ->setFilename('κείμενο, εδάφιο, θέμα.zip') + ) + ; + $this->assertEquals(1, $mailer->send($message), + '%s: The smoke test should send a single message' + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/ByteStream/ArrayByteStreamTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/ByteStream/ArrayByteStreamTest.php new file mode 100644 index 0000000000..2a50b190e0 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/ByteStream/ArrayByteStreamTest.php @@ -0,0 +1,203 @@ +_createArrayStream($input); + $output = array(); + while (false !== $bytes = $bs->read(1)) { + $output[] = $bytes; + } + $this->assertEquals($input, $output, + '%s: Bytes read from stream should be the same as bytes in constructor' + ); + } + + public function testReadingMultipleBytesFromBaseInput() + { + $input = array('a', 'b', 'c', 'd'); + $bs = $this->_createArrayStream($input); + $output = array(); + while (false !== $bytes = $bs->read(2)) { + $output[] = $bytes; + } + $this->assertEquals(array('ab', 'cd'), $output, + '%s: Bytes read from stream should be in pairs' + ); + } + + public function testReadingOddOffsetOnLastByte() + { + $input = array('a', 'b', 'c', 'd', 'e'); + $bs = $this->_createArrayStream($input); + $output = array(); + while (false !== $bytes = $bs->read(2)) { + $output[] = $bytes; + } + $this->assertEquals(array('ab', 'cd', 'e'), $output, + '%s: Bytes read from stream should be in pairs except final read' + ); + } + + public function testSettingPointerPartway() + { + $input = array('a', 'b', 'c'); + $bs = $this->_createArrayStream($input); + $bs->setReadPointer(1); + $this->assertEquals('b', $bs->read(1), + '%s: Byte should be second byte since pointer as at offset 1' + ); + } + + public function testResettingPointerAfterExhaustion() + { + $input = array('a', 'b', 'c'); + $bs = $this->_createArrayStream($input); + while (false !== $bs->read(1)); + + $bs->setReadPointer(0); + $this->assertEquals('a', $bs->read(1), + '%s: Byte should be first byte since pointer as at offset 0' + ); + } + + public function testPointerNeverSetsBelowZero() + { + $input = array('a', 'b', 'c'); + $bs = $this->_createArrayStream($input); + + $bs->setReadPointer(-1); + $this->assertEquals('a', $bs->read(1), + '%s: Byte should be first byte since pointer should be at offset 0' + ); + } + + public function testPointerNeverSetsAboveStackSize() + { + $input = array('a', 'b', 'c'); + $bs = $this->_createArrayStream($input); + + $bs->setReadPointer(3); + $this->assertFalse($bs->read(1), + '%s: Stream should be at end and thus return false' + ); + } + + public function testBytesCanBeWrittenToStream() + { + $input = array('a', 'b', 'c'); + $bs = $this->_createArrayStream($input); + + $bs->write('de'); + + $output = array(); + while (false !== $bytes = $bs->read(1)) { + $output[] = $bytes; + } + $this->assertEquals(array('a', 'b', 'c', 'd', 'e'), $output, + '%s: Bytes read from stream should be from initial stack + written' + ); + } + + public function testContentsCanBeFlushed() + { + $input = array('a', 'b', 'c'); + $bs = $this->_createArrayStream($input); + + $bs->flushBuffers(); + + $this->assertFalse($bs->read(1), + '%s: Contents have been flushed so read() should return false' + ); + } + + public function testConstructorCanTakeStringArgument() + { + $bs = $this->_createArrayStream('abc'); + $output = array(); + while (false !== $bytes = $bs->read(1)) { + $output[] = $bytes; + } + $this->assertEquals(array('a', 'b', 'c'), $output, + '%s: Bytes read from stream should be the same as bytes in constructor' + ); + } + + public function testBindingOtherStreamsMirrorsWriteOperations() + { + $bs = $this->_createArrayStream(''); + $is1 = $this->getMockBuilder('Swift_InputByteStream')->getMock(); + $is2 = $this->getMockBuilder('Swift_InputByteStream')->getMock(); + + $is1->expects($this->at(0)) + ->method('write') + ->with('x'); + $is1->expects($this->at(1)) + ->method('write') + ->with('y'); + $is2->expects($this->at(0)) + ->method('write') + ->with('x'); + $is2->expects($this->at(1)) + ->method('write') + ->with('y'); + + $bs->bind($is1); + $bs->bind($is2); + + $bs->write('x'); + $bs->write('y'); + } + + public function testBindingOtherStreamsMirrorsFlushOperations() + { + $bs = $this->_createArrayStream(''); + $is1 = $this->getMockBuilder('Swift_InputByteStream')->getMock(); + $is2 = $this->getMockBuilder('Swift_InputByteStream')->getMock(); + + $is1->expects($this->once()) + ->method('flushBuffers'); + $is2->expects($this->once()) + ->method('flushBuffers'); + + $bs->bind($is1); + $bs->bind($is2); + + $bs->flushBuffers(); + } + + public function testUnbindingStreamPreventsFurtherWrites() + { + $bs = $this->_createArrayStream(''); + $is1 = $this->getMockBuilder('Swift_InputByteStream')->getMock(); + $is2 = $this->getMockBuilder('Swift_InputByteStream')->getMock(); + + $is1->expects($this->at(0)) + ->method('write') + ->with('x'); + $is1->expects($this->at(1)) + ->method('write') + ->with('y'); + $is2->expects($this->once()) + ->method('write') + ->with('x'); + + $bs->bind($is1); + $bs->bind($is2); + + $bs->write('x'); + + $bs->unbind($is2); + + $bs->write('y'); + } + + // -- Creation Methods + + private function _createArrayStream($input) + { + return new Swift_ByteStream_ArrayByteStream($input); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterReader/GenericFixedWidthReaderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterReader/GenericFixedWidthReaderTest.php new file mode 100644 index 0000000000..3f7a46cf59 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterReader/GenericFixedWidthReaderTest.php @@ -0,0 +1,43 @@ +assertSame(1, $reader->getInitialByteSize()); + + $reader = new Swift_CharacterReader_GenericFixedWidthReader(4); + $this->assertSame(4, $reader->getInitialByteSize()); + } + + public function testValidationValueIsBasedOnOctetCount() + { + $reader = new Swift_CharacterReader_GenericFixedWidthReader(4); + + $this->assertSame( + 1, $reader->validateByteSequence(array(0x01, 0x02, 0x03), 3) + ); //3 octets + + $this->assertSame( + 2, $reader->validateByteSequence(array(0x01, 0x0A), 2) + ); //2 octets + + $this->assertSame( + 3, $reader->validateByteSequence(array(0xFE), 1) + ); //1 octet + + $this->assertSame( + 0, $reader->validateByteSequence(array(0xFE, 0x03, 0x67, 0x9A), 4) + ); //All 4 octets + } + + public function testValidationFailsIfTooManyOctets() + { + $reader = new Swift_CharacterReader_GenericFixedWidthReader(6); + + $this->assertSame(-1, $reader->validateByteSequence( + array(0xFE, 0x03, 0x67, 0x9A, 0x10, 0x09, 0x85), 7 + )); //7 octets + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterReader/UsAsciiReaderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterReader/UsAsciiReaderTest.php new file mode 100644 index 0000000000..41f8f70351 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterReader/UsAsciiReaderTest.php @@ -0,0 +1,52 @@ +read($size); ) { + $c .= $bytes; + $size = $v->validateCharacter($c); + if (-1 == $size) { + throw new Exception( ... invalid char .. ); + } elseif (0 == $size) { + return $c; //next character in $os + } + } + + */ + + private $_reader; + + public function setUp() + { + $this->_reader = new Swift_CharacterReader_UsAsciiReader(); + } + + public function testAllValidAsciiCharactersReturnZero() + { + for ($ordinal = 0x00; $ordinal <= 0x7F; ++$ordinal) { + $this->assertSame( + 0, $this->_reader->validateByteSequence(array($ordinal), 1) + ); + } + } + + public function testMultipleBytesAreInvalid() + { + for ($ordinal = 0x00; $ordinal <= 0x7F; $ordinal += 2) { + $this->assertSame( + -1, $this->_reader->validateByteSequence(array($ordinal, $ordinal + 1), 2) + ); + } + } + + public function testBytesAboveAsciiRangeAreInvalid() + { + for ($ordinal = 0x80; $ordinal <= 0xFF; ++$ordinal) { + $this->assertSame( + -1, $this->_reader->validateByteSequence(array($ordinal), 1) + ); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterReader/Utf8ReaderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterReader/Utf8ReaderTest.php new file mode 100644 index 0000000000..34e9c91def --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterReader/Utf8ReaderTest.php @@ -0,0 +1,65 @@ +_reader = new Swift_CharacterReader_Utf8Reader(); + } + + public function testLeading7BitOctetCausesReturnZero() + { + for ($ordinal = 0x00; $ordinal <= 0x7F; ++$ordinal) { + $this->assertSame( + 0, $this->_reader->validateByteSequence(array($ordinal), 1) + ); + } + } + + public function testLeadingByteOf2OctetCharCausesReturn1() + { + for ($octet = 0xC0; $octet <= 0xDF; ++$octet) { + $this->assertSame( + 1, $this->_reader->validateByteSequence(array($octet), 1) + ); + } + } + + public function testLeadingByteOf3OctetCharCausesReturn2() + { + for ($octet = 0xE0; $octet <= 0xEF; ++$octet) { + $this->assertSame( + 2, $this->_reader->validateByteSequence(array($octet), 1) + ); + } + } + + public function testLeadingByteOf4OctetCharCausesReturn3() + { + for ($octet = 0xF0; $octet <= 0xF7; ++$octet) { + $this->assertSame( + 3, $this->_reader->validateByteSequence(array($octet), 1) + ); + } + } + + public function testLeadingByteOf5OctetCharCausesReturn4() + { + for ($octet = 0xF8; $octet <= 0xFB; ++$octet) { + $this->assertSame( + 4, $this->_reader->validateByteSequence(array($octet), 1) + ); + } + } + + public function testLeadingByteOf6OctetCharCausesReturn5() + { + for ($octet = 0xFC; $octet <= 0xFD; ++$octet) { + $this->assertSame( + 5, $this->_reader->validateByteSequence(array($octet), 1) + ); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterStream/ArrayCharacterStreamTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterStream/ArrayCharacterStreamTest.php new file mode 100644 index 0000000000..cbd412bd8d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterStream/ArrayCharacterStreamTest.php @@ -0,0 +1,360 @@ +_getReader(); + $factory = $this->_getFactory($reader); + + $stream = new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8'); + + $reader->shouldReceive('getInitialByteSize') + ->zeroOrMoreTimes() + ->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD1), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + + $stream->importString(pack('C*', + 0xD0, 0x94, + 0xD0, 0xB6, + 0xD0, 0xBE, + 0xD1, 0x8D, + 0xD0, 0xBB, + 0xD0, 0xB0 + ) + ); + } + + public function testCharactersWrittenUseValidator() + { + $reader = $this->_getReader(); + $factory = $this->_getFactory($reader); + + $stream = new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8'); + + $reader->shouldReceive('getInitialByteSize') + ->zeroOrMoreTimes() + ->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD1), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD1), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD1), 1)->andReturn(1); + + $stream->importString(pack('C*', 0xD0, 0x94, 0xD0, 0xB6, 0xD0, 0xBE)); + + $stream->write(pack('C*', + 0xD0, 0xBB, + 0xD1, 0x8E, + 0xD0, 0xB1, + 0xD1, 0x8B, + 0xD1, 0x85 + ) + ); + } + + public function testReadCharactersAreInTact() + { + $reader = $this->_getReader(); + $factory = $this->_getFactory($reader); + + $stream = new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8'); + + $reader->shouldReceive('getInitialByteSize') + ->zeroOrMoreTimes() + ->andReturn(1); + //String + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + //Stream + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD1), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD1), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD1), 1)->andReturn(1); + + $stream->importString(pack('C*', 0xD0, 0x94, 0xD0, 0xB6, 0xD0, 0xBE)); + + $stream->write(pack('C*', + 0xD0, 0xBB, + 0xD1, 0x8E, + 0xD0, 0xB1, + 0xD1, 0x8B, + 0xD1, 0x85 + ) + ); + + $this->assertIdenticalBinary(pack('C*', 0xD0, 0x94), $stream->read(1)); + $this->assertIdenticalBinary( + pack('C*', 0xD0, 0xB6, 0xD0, 0xBE), $stream->read(2) + ); + $this->assertIdenticalBinary(pack('C*', 0xD0, 0xBB), $stream->read(1)); + $this->assertIdenticalBinary( + pack('C*', 0xD1, 0x8E, 0xD0, 0xB1, 0xD1, 0x8B), $stream->read(3) + ); + $this->assertIdenticalBinary(pack('C*', 0xD1, 0x85), $stream->read(1)); + + $this->assertFalse($stream->read(1)); + } + + public function testCharactersCanBeReadAsByteArrays() + { + $reader = $this->_getReader(); + $factory = $this->_getFactory($reader); + + $stream = new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8'); + + $reader->shouldReceive('getInitialByteSize') + ->zeroOrMoreTimes() + ->andReturn(1); + //String + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + //Stream + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD1), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD1), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD1), 1)->andReturn(1); + + $stream->importString(pack('C*', 0xD0, 0x94, 0xD0, 0xB6, 0xD0, 0xBE)); + + $stream->write(pack('C*', + 0xD0, 0xBB, + 0xD1, 0x8E, + 0xD0, 0xB1, + 0xD1, 0x8B, + 0xD1, 0x85 + ) + ); + + $this->assertEquals(array(0xD0, 0x94), $stream->readBytes(1)); + $this->assertEquals(array(0xD0, 0xB6, 0xD0, 0xBE), $stream->readBytes(2)); + $this->assertEquals(array(0xD0, 0xBB), $stream->readBytes(1)); + $this->assertEquals( + array(0xD1, 0x8E, 0xD0, 0xB1, 0xD1, 0x8B), $stream->readBytes(3) + ); + $this->assertEquals(array(0xD1, 0x85), $stream->readBytes(1)); + + $this->assertFalse($stream->readBytes(1)); + } + + public function testRequestingLargeCharCountPastEndOfStream() + { + $reader = $this->_getReader(); + $factory = $this->_getFactory($reader); + + $stream = new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8'); + + $reader->shouldReceive('getInitialByteSize') + ->zeroOrMoreTimes() + ->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + + $stream->importString(pack('C*', 0xD0, 0x94, 0xD0, 0xB6, 0xD0, 0xBE)); + + $this->assertIdenticalBinary(pack('C*', 0xD0, 0x94, 0xD0, 0xB6, 0xD0, 0xBE), + $stream->read(100) + ); + + $this->assertFalse($stream->read(1)); + } + + public function testRequestingByteArrayCountPastEndOfStream() + { + $reader = $this->_getReader(); + $factory = $this->_getFactory($reader); + + $stream = new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8'); + + $reader->shouldReceive('getInitialByteSize') + ->zeroOrMoreTimes() + ->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + + $stream->importString(pack('C*', 0xD0, 0x94, 0xD0, 0xB6, 0xD0, 0xBE)); + + $this->assertEquals(array(0xD0, 0x94, 0xD0, 0xB6, 0xD0, 0xBE), + $stream->readBytes(100) + ); + + $this->assertFalse($stream->readBytes(1)); + } + + public function testPointerOffsetCanBeSet() + { + $reader = $this->_getReader(); + $factory = $this->_getFactory($reader); + + $stream = new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8'); + + $reader->shouldReceive('getInitialByteSize') + ->zeroOrMoreTimes() + ->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + + $stream->importString(pack('C*', 0xD0, 0x94, 0xD0, 0xB6, 0xD0, 0xBE)); + + $this->assertIdenticalBinary(pack('C*', 0xD0, 0x94), $stream->read(1)); + + $stream->setPointer(0); + + $this->assertIdenticalBinary(pack('C*', 0xD0, 0x94), $stream->read(1)); + + $stream->setPointer(2); + + $this->assertIdenticalBinary(pack('C*', 0xD0, 0xBE), $stream->read(1)); + } + + public function testContentsCanBeFlushed() + { + $reader = $this->_getReader(); + $factory = $this->_getFactory($reader); + + $stream = new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8'); + + $reader->shouldReceive('getInitialByteSize') + ->zeroOrMoreTimes() + ->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + + $stream->importString(pack('C*', 0xD0, 0x94, 0xD0, 0xB6, 0xD0, 0xBE)); + + $stream->flushContents(); + + $this->assertFalse($stream->read(1)); + } + + public function testByteStreamCanBeImportingUsesValidator() + { + $reader = $this->_getReader(); + $factory = $this->_getFactory($reader); + $os = $this->_getByteStream(); + + $stream = new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8'); + + $os->shouldReceive('setReadPointer') + ->between(0, 1) + ->with(0); + $os->shouldReceive('read')->once()->andReturn(pack('C*', 0xD0)); + $os->shouldReceive('read')->once()->andReturn(pack('C*', 0x94)); + $os->shouldReceive('read')->once()->andReturn(pack('C*', 0xD0)); + $os->shouldReceive('read')->once()->andReturn(pack('C*', 0xB6)); + $os->shouldReceive('read')->once()->andReturn(pack('C*', 0xD0)); + $os->shouldReceive('read')->once()->andReturn(pack('C*', 0xBE)); + $os->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturn(false); + + $reader->shouldReceive('getInitialByteSize') + ->zeroOrMoreTimes() + ->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + + $stream->importByteStream($os); + } + + public function testImportingStreamProducesCorrectCharArray() + { + $reader = $this->_getReader(); + $factory = $this->_getFactory($reader); + $os = $this->_getByteStream(); + + $stream = new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8'); + + $os->shouldReceive('setReadPointer') + ->between(0, 1) + ->with(0); + $os->shouldReceive('read')->once()->andReturn(pack('C*', 0xD0)); + $os->shouldReceive('read')->once()->andReturn(pack('C*', 0x94)); + $os->shouldReceive('read')->once()->andReturn(pack('C*', 0xD0)); + $os->shouldReceive('read')->once()->andReturn(pack('C*', 0xB6)); + $os->shouldReceive('read')->once()->andReturn(pack('C*', 0xD0)); + $os->shouldReceive('read')->once()->andReturn(pack('C*', 0xBE)); + $os->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturn(false); + + $reader->shouldReceive('getInitialByteSize') + ->zeroOrMoreTimes() + ->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + + $stream->importByteStream($os); + + $this->assertIdenticalBinary(pack('C*', 0xD0, 0x94), $stream->read(1)); + $this->assertIdenticalBinary(pack('C*', 0xD0, 0xB6), $stream->read(1)); + $this->assertIdenticalBinary(pack('C*', 0xD0, 0xBE), $stream->read(1)); + + $this->assertFalse($stream->read(1)); + } + + public function testAlgorithmWithFixedWidthCharsets() + { + $reader = $this->_getReader(); + $factory = $this->_getFactory($reader); + + $reader->shouldReceive('getInitialByteSize') + ->zeroOrMoreTimes() + ->andReturn(2); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD1, 0x8D), 2); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0, 0xBB), 2); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0, 0xB0), 2); + + $stream = new Swift_CharacterStream_ArrayCharacterStream( + $factory, 'utf-8' + ); + $stream->importString(pack('C*', 0xD1, 0x8D, 0xD0, 0xBB, 0xD0, 0xB0)); + + $this->assertIdenticalBinary(pack('C*', 0xD1, 0x8D), $stream->read(1)); + $this->assertIdenticalBinary(pack('C*', 0xD0, 0xBB), $stream->read(1)); + $this->assertIdenticalBinary(pack('C*', 0xD0, 0xB0), $stream->read(1)); + + $this->assertFalse($stream->read(1)); + } + + // -- Creation methods + + private function _getReader() + { + return $this->getMockery('Swift_CharacterReader'); + } + + private function _getFactory($reader) + { + $factory = $this->getMockery('Swift_CharacterReaderFactory'); + $factory->shouldReceive('getReaderFor') + ->zeroOrMoreTimes() + ->with('utf-8') + ->andReturn($reader); + + return $factory; + } + + private function _getByteStream() + { + return $this->getMockery('Swift_OutputByteStream'); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/DependencyContainerTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/DependencyContainerTest.php new file mode 100644 index 0000000000..70690e2dce --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/DependencyContainerTest.php @@ -0,0 +1,175 @@ +arg1 = $arg1; + $this->arg2 = $arg2; + } +} + +class Swift_DependencyContainerTest extends \PHPUnit_Framework_TestCase +{ + private $_container; + + public function setUp() + { + $this->_container = new Swift_DependencyContainer(); + } + + public function testRegisterAndLookupValue() + { + $this->_container->register('foo')->asValue('bar'); + $this->assertEquals('bar', $this->_container->lookup('foo')); + } + + public function testHasReturnsTrueForRegisteredValue() + { + $this->_container->register('foo')->asValue('bar'); + $this->assertTrue($this->_container->has('foo')); + } + + public function testHasReturnsFalseForUnregisteredValue() + { + $this->assertFalse($this->_container->has('foo')); + } + + public function testRegisterAndLookupNewInstance() + { + $this->_container->register('one')->asNewInstanceOf('One'); + $this->assertInstanceof('One', $this->_container->lookup('one')); + } + + public function testHasReturnsTrueForRegisteredInstance() + { + $this->_container->register('one')->asNewInstanceOf('One'); + $this->assertTrue($this->_container->has('one')); + } + + public function testNewInstanceIsAlwaysNew() + { + $this->_container->register('one')->asNewInstanceOf('One'); + $a = $this->_container->lookup('one'); + $b = $this->_container->lookup('one'); + $this->assertEquals($a, $b); + } + + public function testRegisterAndLookupSharedInstance() + { + $this->_container->register('one')->asSharedInstanceOf('One'); + $this->assertInstanceof('One', $this->_container->lookup('one')); + } + + public function testHasReturnsTrueForSharedInstance() + { + $this->_container->register('one')->asSharedInstanceOf('One'); + $this->assertTrue($this->_container->has('one')); + } + + public function testMultipleSharedInstancesAreSameInstance() + { + $this->_container->register('one')->asSharedInstanceOf('One'); + $a = $this->_container->lookup('one'); + $b = $this->_container->lookup('one'); + $this->assertEquals($a, $b); + } + + public function testNewInstanceWithDependencies() + { + $this->_container->register('foo')->asValue('FOO'); + $this->_container->register('one')->asNewInstanceOf('One') + ->withDependencies(array('foo')); + $obj = $this->_container->lookup('one'); + $this->assertSame('FOO', $obj->arg1); + } + + public function testNewInstanceWithMultipleDependencies() + { + $this->_container->register('foo')->asValue('FOO'); + $this->_container->register('bar')->asValue(42); + $this->_container->register('one')->asNewInstanceOf('One') + ->withDependencies(array('foo', 'bar')); + $obj = $this->_container->lookup('one'); + $this->assertSame('FOO', $obj->arg1); + $this->assertSame(42, $obj->arg2); + } + + public function testNewInstanceWithInjectedObjects() + { + $this->_container->register('foo')->asValue('FOO'); + $this->_container->register('one')->asNewInstanceOf('One'); + $this->_container->register('two')->asNewInstanceOf('One') + ->withDependencies(array('one', 'foo')); + $obj = $this->_container->lookup('two'); + $this->assertEquals($this->_container->lookup('one'), $obj->arg1); + $this->assertSame('FOO', $obj->arg2); + } + + public function testNewInstanceWithAddConstructorValue() + { + $this->_container->register('one')->asNewInstanceOf('One') + ->addConstructorValue('x') + ->addConstructorValue(99); + $obj = $this->_container->lookup('one'); + $this->assertSame('x', $obj->arg1); + $this->assertSame(99, $obj->arg2); + } + + public function testNewInstanceWithAddConstructorLookup() + { + $this->_container->register('foo')->asValue('FOO'); + $this->_container->register('bar')->asValue(42); + $this->_container->register('one')->asNewInstanceOf('One') + ->addConstructorLookup('foo') + ->addConstructorLookup('bar'); + + $obj = $this->_container->lookup('one'); + $this->assertSame('FOO', $obj->arg1); + $this->assertSame(42, $obj->arg2); + } + + public function testResolvedDependenciesCanBeLookedUp() + { + $this->_container->register('foo')->asValue('FOO'); + $this->_container->register('one')->asNewInstanceOf('One'); + $this->_container->register('two')->asNewInstanceOf('One') + ->withDependencies(array('one', 'foo')); + $deps = $this->_container->createDependenciesFor('two'); + $this->assertEquals( + array($this->_container->lookup('one'), 'FOO'), $deps + ); + } + + public function testArrayOfDependenciesCanBeSpecified() + { + $this->_container->register('foo')->asValue('FOO'); + $this->_container->register('one')->asNewInstanceOf('One'); + $this->_container->register('two')->asNewInstanceOf('One') + ->withDependencies(array(array('one', 'foo'), 'foo')); + + $obj = $this->_container->lookup('two'); + $this->assertEquals(array($this->_container->lookup('one'), 'FOO'), $obj->arg1); + $this->assertSame('FOO', $obj->arg2); + } + + public function testAliasCanBeSet() + { + $this->_container->register('foo')->asValue('FOO'); + $this->_container->register('bar')->asAliasOf('foo'); + + $this->assertSame('FOO', $this->_container->lookup('bar')); + } + + public function testAliasOfAliasCanBeSet() + { + $this->_container->register('foo')->asValue('FOO'); + $this->_container->register('bar')->asAliasOf('foo'); + $this->_container->register('zip')->asAliasOf('bar'); + $this->_container->register('button')->asAliasOf('zip'); + + $this->assertSame('FOO', $this->_container->lookup('button')); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Encoder/Base64EncoderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Encoder/Base64EncoderTest.php new file mode 100644 index 0000000000..1e712fe528 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Encoder/Base64EncoderTest.php @@ -0,0 +1,173 @@ +_encoder = new Swift_Encoder_Base64Encoder(); + } + + /* + There's really no point in testing the entire base64 encoding to the + level QP encoding has been tested. base64_encode() has been in PHP for + years. + */ + + public function testInputOutputRatioIs3to4Bytes() + { + /* + RFC 2045, 6.8 + + The encoding process represents 24-bit groups of input bits as output + strings of 4 encoded characters. Proceeding from left to right, a + 24-bit input group is formed by concatenating 3 8bit input groups. + These 24 bits are then treated as 4 concatenated 6-bit groups, each + of which is translated into a single digit in the base64 alphabet. + */ + + $this->assertEquals( + 'MTIz', $this->_encoder->encodeString('123'), + '%s: 3 bytes of input should yield 4 bytes of output' + ); + $this->assertEquals( + 'MTIzNDU2', $this->_encoder->encodeString('123456'), + '%s: 6 bytes in input should yield 8 bytes of output' + ); + $this->assertEquals( + 'MTIzNDU2Nzg5', $this->_encoder->encodeString('123456789'), + '%s: 9 bytes in input should yield 12 bytes of output' + ); + } + + public function testPadLength() + { + /* + RFC 2045, 6.8 + + Special processing is performed if fewer than 24 bits are available + at the end of the data being encoded. A full encoding quantum is + always completed at the end of a body. When fewer than 24 input bits + are available in an input group, zero bits are added (on the right) + to form an integral number of 6-bit groups. Padding at the end of + the data is performed using the "=" character. Since all base64 + input is an integral number of octets, only the following cases can + arise: (1) the final quantum of encoding input is an integral + multiple of 24 bits; here, the final unit of encoded output will be + an integral multiple of 4 characters with no "=" padding, (2) the + final quantum of encoding input is exactly 8 bits; here, the final + unit of encoded output will be two characters followed by two "=" + padding characters, or (3) the final quantum of encoding input is + exactly 16 bits; here, the final unit of encoded output will be three + characters followed by one "=" padding character. + */ + + for ($i = 0; $i < 30; ++$i) { + $input = pack('C', rand(0, 255)); + $this->assertRegExp( + '~^[a-zA-Z0-9/\+]{2}==$~', $this->_encoder->encodeString($input), + '%s: A single byte should have 2 bytes of padding' + ); + } + + for ($i = 0; $i < 30; ++$i) { + $input = pack('C*', rand(0, 255), rand(0, 255)); + $this->assertRegExp( + '~^[a-zA-Z0-9/\+]{3}=$~', $this->_encoder->encodeString($input), + '%s: Two bytes should have 1 byte of padding' + ); + } + + for ($i = 0; $i < 30; ++$i) { + $input = pack('C*', rand(0, 255), rand(0, 255), rand(0, 255)); + $this->assertRegExp( + '~^[a-zA-Z0-9/\+]{4}$~', $this->_encoder->encodeString($input), + '%s: Three bytes should have no padding' + ); + } + } + + public function testMaximumLineLengthIs76Characters() + { + /* + The encoded output stream must be represented in lines of no more + than 76 characters each. All line breaks or other characters not + found in Table 1 must be ignored by decoding software. + */ + + $input = + 'abcdefghijklmnopqrstuvwxyz'. + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'. + '1234567890'. + 'abcdefghijklmnopqrstuvwxyz'. + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'. + '1234567890'. + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + + $output = + 'YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQk'.//38 + 'NERUZHSElKS0xNTk9QUVJTVFVWV1hZWjEyMzQ1'."\r\n".//76 * + 'Njc4OTBhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3'.//38 + 'h5ekFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFla'."\r\n".//76 * + 'MTIzNDU2Nzg5MEFCQ0RFRkdISUpLTE1OT1BRUl'.//38 + 'NUVVZXWFla'; //48 + + $this->assertEquals( + $output, $this->_encoder->encodeString($input), + '%s: Lines should be no more than 76 characters' + ); + } + + public function testMaximumLineLengthCanBeSpecified() + { + $input = + 'abcdefghijklmnopqrstuvwxyz'. + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'. + '1234567890'. + 'abcdefghijklmnopqrstuvwxyz'. + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'. + '1234567890'. + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + + $output = + 'YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQk'.//38 + 'NERUZHSElKS0'."\r\n".//50 * + 'xNTk9QUVJTVFVWV1hZWjEyMzQ1Njc4OTBhYmNk'.//38 + 'ZWZnaGlqa2xt'."\r\n".//50 * + 'bm9wcXJzdHV2d3h5ekFCQ0RFRkdISUpLTE1OT1'.//38 + 'BRUlNUVVZXWF'."\r\n".//50 * + 'laMTIzNDU2Nzg5MEFCQ0RFRkdISUpLTE1OT1BR'.//38 + 'UlNUVVZXWFla'; //50 * + + $this->assertEquals( + $output, $this->_encoder->encodeString($input, 0, 50), + '%s: Lines should be no more than 100 characters' + ); + } + + public function testFirstLineLengthCanBeDifferent() + { + $input = + 'abcdefghijklmnopqrstuvwxyz'. + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'. + '1234567890'. + 'abcdefghijklmnopqrstuvwxyz'. + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'. + '1234567890'. + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + + $output = + 'YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQk'.//38 + 'NERUZHSElKS0xNTk9QU'."\r\n".//57 * + 'VJTVFVWV1hZWjEyMzQ1Njc4OTBhYmNkZWZnaGl'.//38 + 'qa2xtbm9wcXJzdHV2d3h5ekFCQ0RFRkdISUpLT'."\r\n".//76 * + 'E1OT1BRUlNUVVZXWFlaMTIzNDU2Nzg5MEFCQ0R'.//38 + 'FRkdISUpLTE1OT1BRUlNUVVZXWFla'; //67 + + $this->assertEquals( + $output, $this->_encoder->encodeString($input, 19), + '%s: First line offset is 19 so first line should be 57 chars long' + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Encoder/QpEncoderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Encoder/QpEncoderTest.php new file mode 100644 index 0000000000..0bf4d1818d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Encoder/QpEncoderTest.php @@ -0,0 +1,402 @@ +_createCharStream(); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importString') + ->once() + ->with($char); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array($ordinal)); + $charStream->shouldReceive('readBytes') + ->atLeast()->times(1) + ->andReturn(false); + + $encoder = new Swift_Encoder_QpEncoder($charStream); + + $this->assertIdenticalBinary($char, $encoder->encodeString($char)); + } + } + + public function testWhiteSpaceAtLineEndingIsEncoded() + { + /* -- RFC 2045, 6.7 -- + (3) (White Space) Octets with values of 9 and 32 MAY be + represented as US-ASCII TAB (HT) and SPACE characters, + respectively, but MUST NOT be so represented at the end + of an encoded line. Any TAB (HT) or SPACE characters + on an encoded line MUST thus be followed on that line + by a printable character. In particular, an "=" at the + end of an encoded line, indicating a soft line break + (see rule #5) may follow one or more TAB (HT) or SPACE + characters. It follows that an octet with decimal + value 9 or 32 appearing at the end of an encoded line + must be represented according to Rule #1. This rule is + necessary because some MTAs (Message Transport Agents, + programs which transport messages from one user to + another, or perform a portion of such transfers) are + known to pad lines of text with SPACEs, and others are + known to remove "white space" characters from the end + of a line. Therefore, when decoding a Quoted-Printable + body, any trailing white space on a line must be + deleted, as it will necessarily have been added by + intermediate transport agents. + */ + + $HT = chr(0x09); //9 + $SPACE = chr(0x20); //32 + + //HT + $string = 'a'.$HT.$HT."\r\n".'b'; + + $charStream = $this->_createCharStream(); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importString') + ->once() + ->with($string); + + $charStream->shouldReceive('readBytes')->once()->andReturn(array(ord('a'))); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(0x09)); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(0x09)); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(0x0D)); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(0x0A)); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(ord('b'))); + $charStream->shouldReceive('readBytes')->once()->andReturn(false); + + $encoder = new Swift_Encoder_QpEncoder($charStream); + $this->assertEquals( + 'a'.$HT.'=09'."\r\n".'b', + $encoder->encodeString($string) + ); + + //SPACE + $string = 'a'.$SPACE.$SPACE."\r\n".'b'; + + $charStream = $this->_createCharStream(); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importString') + ->once() + ->with($string); + + $charStream->shouldReceive('readBytes')->once()->andReturn(array(ord('a'))); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(0x20)); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(0x20)); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(0x0D)); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(0x0A)); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(ord('b'))); + $charStream->shouldReceive('readBytes')->once()->andReturn(false); + + $encoder = new Swift_Encoder_QpEncoder($charStream); + $this->assertEquals( + 'a'.$SPACE.'=20'."\r\n".'b', + $encoder->encodeString($string) + ); + } + + public function testCRLFIsLeftAlone() + { + /* + (4) (Line Breaks) A line break in a text body, represented + as a CRLF sequence in the text canonical form, must be + represented by a (RFC 822) line break, which is also a + CRLF sequence, in the Quoted-Printable encoding. Since + the canonical representation of media types other than + text do not generally include the representation of + line breaks as CRLF sequences, no hard line breaks + (i.e. line breaks that are intended to be meaningful + and to be displayed to the user) can occur in the + quoted-printable encoding of such types. Sequences + like "=0D", "=0A", "=0A=0D" and "=0D=0A" will routinely + appear in non-text data represented in quoted- + printable, of course. + + Note that many implementations may elect to encode the + local representation of various content types directly + rather than converting to canonical form first, + encoding, and then converting back to local + representation. In particular, this may apply to plain + text material on systems that use newline conventions + other than a CRLF terminator sequence. Such an + implementation optimization is permissible, but only + when the combined canonicalization-encoding step is + equivalent to performing the three steps separately. + */ + + $string = 'a'."\r\n".'b'."\r\n".'c'."\r\n"; + + $charStream = $this->_createCharStream(); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importString') + ->once() + ->with($string); + + $charStream->shouldReceive('readBytes')->once()->andReturn(array(ord('a'))); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(0x0D)); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(0x0A)); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(ord('b'))); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(0x0D)); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(0x0A)); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(ord('c'))); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(0x0D)); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(0x0A)); + $charStream->shouldReceive('readBytes')->once()->andReturn(false); + + $encoder = new Swift_Encoder_QpEncoder($charStream); + $this->assertEquals($string, $encoder->encodeString($string)); + } + + public function testLinesLongerThan76CharactersAreSoftBroken() + { + /* + (5) (Soft Line Breaks) The Quoted-Printable encoding + REQUIRES that encoded lines be no more than 76 + characters long. If longer lines are to be encoded + with the Quoted-Printable encoding, "soft" line breaks + must be used. An equal sign as the last character on a + encoded line indicates such a non-significant ("soft") + line break in the encoded text. + */ + + $input = str_repeat('a', 140); + + $charStream = $this->_createCharStream(); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importString') + ->once() + ->with($input); + + $output = ''; + for ($i = 0; $i < 140; ++$i) { + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('a'))); + + if (75 == $i) { + $output .= "=\r\n"; + } + $output .= 'a'; + } + + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(false); + + $encoder = new Swift_Encoder_QpEncoder($charStream); + $this->assertEquals($output, $encoder->encodeString($input)); + } + + public function testMaxLineLengthCanBeSpecified() + { + $input = str_repeat('a', 100); + + $charStream = $this->_createCharStream(); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importString') + ->once() + ->with($input); + + $output = ''; + for ($i = 0; $i < 100; ++$i) { + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('a'))); + + if (53 == $i) { + $output .= "=\r\n"; + } + $output .= 'a'; + } + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(false); + + $encoder = new Swift_Encoder_QpEncoder($charStream); + $this->assertEquals($output, $encoder->encodeString($input, 0, 54)); + } + + public function testBytesBelowPermittedRangeAreEncoded() + { + /* + According to Rule (1 & 2) + */ + + foreach (range(0, 32) as $ordinal) { + $char = chr($ordinal); + + $charStream = $this->_createCharStream(); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importString') + ->once() + ->with($char); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array($ordinal)); + $charStream->shouldReceive('readBytes') + ->atLeast()->times(1) + ->andReturn(false); + + $encoder = new Swift_Encoder_QpEncoder($charStream); + + $this->assertEquals( + sprintf('=%02X', $ordinal), $encoder->encodeString($char) + ); + } + } + + public function testDecimalByte61IsEncoded() + { + /* + According to Rule (1 & 2) + */ + + $char = '='; + + $charStream = $this->_createCharStream(); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importString') + ->once() + ->with($char); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(61)); + $charStream->shouldReceive('readBytes') + ->atLeast()->times(1) + ->andReturn(false); + + $encoder = new Swift_Encoder_QpEncoder($charStream); + + $this->assertEquals('=3D', $encoder->encodeString('=')); + } + + public function testBytesAbovePermittedRangeAreEncoded() + { + /* + According to Rule (1 & 2) + */ + + foreach (range(127, 255) as $ordinal) { + $char = chr($ordinal); + + $charStream = $this->_createCharStream(); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importString') + ->once() + ->with($char); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array($ordinal)); + $charStream->shouldReceive('readBytes') + ->atLeast()->times(1) + ->andReturn(false); + + $encoder = new Swift_Encoder_QpEncoder($charStream); + + $this->assertEquals( + sprintf('=%02X', $ordinal), $encoder->encodeString($char) + ); + } + } + + public function testFirstLineLengthCanBeDifferent() + { + $input = str_repeat('a', 140); + + $charStream = $this->_createCharStream(); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importString') + ->once() + ->with($input); + + $output = ''; + for ($i = 0; $i < 140; ++$i) { + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('a'))); + + if (53 == $i || 53 + 75 == $i) { + $output .= "=\r\n"; + } + $output .= 'a'; + } + + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(false); + + $encoder = new Swift_Encoder_QpEncoder($charStream); + $this->assertEquals( + $output, $encoder->encodeString($input, 22), + '%s: First line should start at offset 22 so can only have max length 54' + ); + } + + public function testTextIsPreWrapped() + { + $encoder = $this->createEncoder(); + + $input = str_repeat('a', 70)."\r\n". + str_repeat('a', 70)."\r\n". + str_repeat('a', 70); + + $this->assertEquals( + $input, $encoder->encodeString($input) + ); + } + + // -- Creation methods + + private function _createCharStream() + { + return $this->getMockery('Swift_CharacterStream')->shouldIgnoreMissing(); + } + + private function createEncoder() + { + $factory = new Swift_CharacterReaderFactory_SimpleCharacterReaderFactory(); + $charStream = new Swift_CharacterStream_NgCharacterStream($factory, 'utf-8'); + + return new Swift_Encoder_QpEncoder($charStream); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Encoder/Rfc2231EncoderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Encoder/Rfc2231EncoderTest.php new file mode 100644 index 0000000000..28eae6f862 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Encoder/Rfc2231EncoderTest.php @@ -0,0 +1,141 @@ +getMockery('Swift_CharacterStream'); + + $string = ''; + foreach (range(0x00, 0x7F) as $octet) { + $char = pack('C', $octet); + $string .= $char; + $charStream->shouldReceive('read') + ->once() + ->andReturn($char); + } + + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importString') + ->once() + ->with($string); + $charStream->shouldReceive('read') + ->atLeast()->times(1) + ->andReturn(false); + + $encoder = new Swift_Encoder_Rfc2231Encoder($charStream); + $encoded = $encoder->encodeString($string); + + foreach (explode("\r\n", $encoded) as $line) { + $this->assertRegExp($this->_rfc2045Token, $line, + '%s: Encoder should always return a valid RFC 2045 token.'); + } + } + + public function testEncodingNonAsciiCharactersProducesValidToken() + { + $charStream = $this->getMockery('Swift_CharacterStream'); + + $string = ''; + foreach (range(0x80, 0xFF) as $octet) { + $char = pack('C', $octet); + $string .= $char; + $charStream->shouldReceive('read') + ->once() + ->andReturn($char); + } + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importString') + ->once() + ->with($string); + $charStream->shouldReceive('read') + ->atLeast()->times(1) + ->andReturn(false); + $encoder = new Swift_Encoder_Rfc2231Encoder($charStream); + + $encoded = $encoder->encodeString($string); + + foreach (explode("\r\n", $encoded) as $line) { + $this->assertRegExp($this->_rfc2045Token, $line, + '%s: Encoder should always return a valid RFC 2045 token.'); + } + } + + public function testMaximumLineLengthCanBeSet() + { + $charStream = $this->getMockery('Swift_CharacterStream'); + + $string = ''; + for ($x = 0; $x < 200; ++$x) { + $char = 'a'; + $string .= $char; + $charStream->shouldReceive('read') + ->once() + ->andReturn($char); + } + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importString') + ->once() + ->with($string); + $charStream->shouldReceive('read') + ->atLeast()->times(1) + ->andReturn(false); + $encoder = new Swift_Encoder_Rfc2231Encoder($charStream); + + $encoded = $encoder->encodeString($string, 0, 75); + + $this->assertEquals( + str_repeat('a', 75)."\r\n". + str_repeat('a', 75)."\r\n". + str_repeat('a', 50), + $encoded, + '%s: Lines should be wrapped at each 75 characters' + ); + } + + public function testFirstLineCanHaveShorterLength() + { + $charStream = $this->getMockery('Swift_CharacterStream'); + + $string = ''; + for ($x = 0; $x < 200; ++$x) { + $char = 'a'; + $string .= $char; + $charStream->shouldReceive('read') + ->once() + ->andReturn($char); + } + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importString') + ->once() + ->with($string); + $charStream->shouldReceive('read') + ->atLeast()->times(1) + ->andReturn(false); + $encoder = new Swift_Encoder_Rfc2231Encoder($charStream); + $encoded = $encoder->encodeString($string, 25, 75); + + $this->assertEquals( + str_repeat('a', 50)."\r\n". + str_repeat('a', 75)."\r\n". + str_repeat('a', 75), + $encoded, + '%s: First line should be 25 bytes shorter than the others.' + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/CommandEventTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/CommandEventTest.php new file mode 100644 index 0000000000..3849e09e0d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/CommandEventTest.php @@ -0,0 +1,36 @@ +_createEvent($this->_createTransport(), "FOO\r\n"); + $this->assertEquals("FOO\r\n", $evt->getCommand()); + } + + public function testSuccessCodesCanBeFetchedViaGetter() + { + $evt = $this->_createEvent($this->_createTransport(), "FOO\r\n", array(250)); + $this->assertEquals(array(250), $evt->getSuccessCodes()); + } + + public function testSourceIsBuffer() + { + $transport = $this->_createTransport(); + $evt = $this->_createEvent($transport, "FOO\r\n"); + $ref = $evt->getSource(); + $this->assertEquals($transport, $ref); + } + + // -- Creation Methods + + private function _createEvent(Swift_Transport $source, $command, $successCodes = array()) + { + return new Swift_Events_CommandEvent($source, $command, $successCodes); + } + + private function _createTransport() + { + return $this->getMockBuilder('Swift_Transport')->getMock(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/EventObjectTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/EventObjectTest.php new file mode 100644 index 0000000000..62a91be545 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/EventObjectTest.php @@ -0,0 +1,34 @@ +_createEvent($source); + $ref = $evt->getSource(); + $this->assertEquals($source, $ref); + } + + public function testEventDoesNotHaveCancelledBubbleWhenNew() + { + $source = new stdClass(); + $evt = $this->_createEvent($source); + $this->assertFalse($evt->bubbleCancelled()); + } + + public function testBubbleCanBeCancelledInEvent() + { + $source = new stdClass(); + $evt = $this->_createEvent($source); + $evt->cancelBubble(); + $this->assertTrue($evt->bubbleCancelled()); + } + + // -- Creation Methods + + private function _createEvent($source) + { + return new Swift_Events_EventObject($source); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/ResponseEventTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/ResponseEventTest.php new file mode 100644 index 0000000000..1028e8e94d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/ResponseEventTest.php @@ -0,0 +1,40 @@ +_createEvent($this->_createTransport(), "250 Ok\r\n", true); + $this->assertEquals("250 Ok\r\n", $evt->getResponse(), + '%s: Response should be available via getResponse()' + ); + } + + public function testResultCanBeFetchedViaGetter() + { + $evt = $this->_createEvent($this->_createTransport(), "250 Ok\r\n", false); + $this->assertFalse($evt->isValid(), + '%s: Result should be checkable via isValid()' + ); + } + + public function testSourceIsBuffer() + { + $transport = $this->_createTransport(); + $evt = $this->_createEvent($transport, "250 Ok\r\n", true); + $ref = $evt->getSource(); + $this->assertEquals($transport, $ref); + } + + // -- Creation Methods + + private function _createEvent(Swift_Transport $source, $response, $result) + { + return new Swift_Events_ResponseEvent($source, $response, $result); + } + + private function _createTransport() + { + return $this->getMockBuilder('Swift_Transport')->getMock(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/SendEventTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/SendEventTest.php new file mode 100644 index 0000000000..8a3fe92f1e --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/SendEventTest.php @@ -0,0 +1,99 @@ +_createMessage(); + $transport = $this->_createTransport(); + + $evt = $this->_createEvent($transport, $message); + + $ref = $evt->getMessage(); + $this->assertEquals($message, $ref, + '%s: Message should be returned from getMessage()' + ); + } + + public function testTransportCanBeFetchViaGetter() + { + $message = $this->_createMessage(); + $transport = $this->_createTransport(); + + $evt = $this->_createEvent($transport, $message); + + $ref = $evt->getTransport(); + $this->assertEquals($transport, $ref, + '%s: Transport should be returned from getTransport()' + ); + } + + public function testTransportCanBeFetchViaGetSource() + { + $message = $this->_createMessage(); + $transport = $this->_createTransport(); + + $evt = $this->_createEvent($transport, $message); + + $ref = $evt->getSource(); + $this->assertEquals($transport, $ref, + '%s: Transport should be returned from getSource()' + ); + } + + public function testResultCanBeSetAndGet() + { + $message = $this->_createMessage(); + $transport = $this->_createTransport(); + + $evt = $this->_createEvent($transport, $message); + + $evt->setResult( + Swift_Events_SendEvent::RESULT_SUCCESS | Swift_Events_SendEvent::RESULT_TENTATIVE + ); + + $this->assertTrue((bool) ($evt->getResult() & Swift_Events_SendEvent::RESULT_SUCCESS)); + $this->assertTrue((bool) ($evt->getResult() & Swift_Events_SendEvent::RESULT_TENTATIVE)); + } + + public function testFailedRecipientsCanBeSetAndGet() + { + $message = $this->_createMessage(); + $transport = $this->_createTransport(); + + $evt = $this->_createEvent($transport, $message); + + $evt->setFailedRecipients(array('foo@bar', 'zip@button')); + + $this->assertEquals(array('foo@bar', 'zip@button'), $evt->getFailedRecipients(), + '%s: FailedRecipients should be returned from getter' + ); + } + + public function testFailedRecipientsGetsPickedUpCorrectly() + { + $message = $this->_createMessage(); + $transport = $this->_createTransport(); + + $evt = $this->_createEvent($transport, $message); + $this->assertEquals(array(), $evt->getFailedRecipients()); + } + + // -- Creation Methods + + private function _createEvent(Swift_Transport $source, + Swift_Mime_Message $message) + { + return new Swift_Events_SendEvent($source, $message); + } + + private function _createTransport() + { + return $this->getMockBuilder('Swift_Transport')->getMock(); + } + + private function _createMessage() + { + return $this->getMockBuilder('Swift_Mime_Message')->getMock(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/SimpleEventDispatcherTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/SimpleEventDispatcherTest.php new file mode 100644 index 0000000000..fdc9c138cd --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/SimpleEventDispatcherTest.php @@ -0,0 +1,142 @@ +_dispatcher = new Swift_Events_SimpleEventDispatcher(); + } + + public function testSendEventCanBeCreated() + { + $transport = $this->getMockBuilder('Swift_Transport')->getMock(); + $message = $this->getMockBuilder('Swift_Mime_Message')->getMock(); + $evt = $this->_dispatcher->createSendEvent($transport, $message); + $this->assertInstanceof('Swift_Events_SendEvent', $evt); + $this->assertSame($message, $evt->getMessage()); + $this->assertSame($transport, $evt->getTransport()); + } + + public function testCommandEventCanBeCreated() + { + $buf = $this->getMockBuilder('Swift_Transport')->getMock(); + $evt = $this->_dispatcher->createCommandEvent($buf, "FOO\r\n", array(250)); + $this->assertInstanceof('Swift_Events_CommandEvent', $evt); + $this->assertSame($buf, $evt->getSource()); + $this->assertEquals("FOO\r\n", $evt->getCommand()); + $this->assertEquals(array(250), $evt->getSuccessCodes()); + } + + public function testResponseEventCanBeCreated() + { + $buf = $this->getMockBuilder('Swift_Transport')->getMock(); + $evt = $this->_dispatcher->createResponseEvent($buf, "250 Ok\r\n", true); + $this->assertInstanceof('Swift_Events_ResponseEvent', $evt); + $this->assertSame($buf, $evt->getSource()); + $this->assertEquals("250 Ok\r\n", $evt->getResponse()); + $this->assertTrue($evt->isValid()); + } + + public function testTransportChangeEventCanBeCreated() + { + $transport = $this->getMockBuilder('Swift_Transport')->getMock(); + $evt = $this->_dispatcher->createTransportChangeEvent($transport); + $this->assertInstanceof('Swift_Events_TransportChangeEvent', $evt); + $this->assertSame($transport, $evt->getSource()); + } + + public function testTransportExceptionEventCanBeCreated() + { + $transport = $this->getMockBuilder('Swift_Transport')->getMock(); + $ex = new Swift_TransportException(''); + $evt = $this->_dispatcher->createTransportExceptionEvent($transport, $ex); + $this->assertInstanceof('Swift_Events_TransportExceptionEvent', $evt); + $this->assertSame($transport, $evt->getSource()); + $this->assertSame($ex, $evt->getException()); + } + + public function testListenersAreNotifiedOfDispatchedEvent() + { + $transport = $this->getMockBuilder('Swift_Transport')->getMock(); + + $evt = $this->_dispatcher->createTransportChangeEvent($transport); + + $listenerA = $this->getMockBuilder('Swift_Events_TransportChangeListener')->getMock(); + $listenerB = $this->getMockBuilder('Swift_Events_TransportChangeListener')->getMock(); + + $this->_dispatcher->bindEventListener($listenerA); + $this->_dispatcher->bindEventListener($listenerB); + + $listenerA->expects($this->once()) + ->method('transportStarted') + ->with($evt); + $listenerB->expects($this->once()) + ->method('transportStarted') + ->with($evt); + + $this->_dispatcher->dispatchEvent($evt, 'transportStarted'); + } + + public function testListenersAreOnlyCalledIfImplementingCorrectInterface() + { + $transport = $this->getMockBuilder('Swift_Transport')->getMock(); + $message = $this->getMockBuilder('Swift_Mime_Message')->getMock(); + + $evt = $this->_dispatcher->createSendEvent($transport, $message); + + $targetListener = $this->getMockBuilder('Swift_Events_SendListener')->getMock(); + $otherListener = $this->getMockBuilder('DummyListener')->getMock(); + + $this->_dispatcher->bindEventListener($targetListener); + $this->_dispatcher->bindEventListener($otherListener); + + $targetListener->expects($this->once()) + ->method('sendPerformed') + ->with($evt); + $otherListener->expects($this->never()) + ->method('sendPerformed'); + + $this->_dispatcher->dispatchEvent($evt, 'sendPerformed'); + } + + public function testListenersCanCancelBubblingOfEvent() + { + $transport = $this->getMockBuilder('Swift_Transport')->getMock(); + $message = $this->getMockBuilder('Swift_Mime_Message')->getMock(); + + $evt = $this->_dispatcher->createSendEvent($transport, $message); + + $listenerA = $this->getMockBuilder('Swift_Events_SendListener')->getMock(); + $listenerB = $this->getMockBuilder('Swift_Events_SendListener')->getMock(); + + $this->_dispatcher->bindEventListener($listenerA); + $this->_dispatcher->bindEventListener($listenerB); + + $listenerA->expects($this->once()) + ->method('sendPerformed') + ->with($evt) + ->will($this->returnCallback(function ($object) { + $object->cancelBubble(true); + })); + $listenerB->expects($this->never()) + ->method('sendPerformed'); + + $this->_dispatcher->dispatchEvent($evt, 'sendPerformed'); + + $this->assertTrue($evt->bubbleCancelled()); + } + + private function _createDispatcher(array $map) + { + return new Swift_Events_SimpleEventDispatcher($map); + } +} + +class DummyListener implements Swift_Events_EventListener +{ + public function sendPerformed(Swift_Events_SendEvent $evt) + { + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/TransportChangeEventTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/TransportChangeEventTest.php new file mode 100644 index 0000000000..8d7b23769c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/TransportChangeEventTest.php @@ -0,0 +1,32 @@ +_createTransport(); + $evt = $this->_createEvent($transport); + $ref = $evt->getTransport(); + $this->assertEquals($transport, $ref); + } + + public function testSourceIsTransport() + { + $transport = $this->_createTransport(); + $evt = $this->_createEvent($transport); + $ref = $evt->getSource(); + $this->assertEquals($transport, $ref); + } + + // -- Creation Methods + + private function _createEvent(Swift_Transport $source) + { + return new Swift_Events_TransportChangeEvent($source); + } + + private function _createTransport() + { + return $this->getMockBuilder('Swift_Transport')->getMock(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/TransportExceptionEventTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/TransportExceptionEventTest.php new file mode 100644 index 0000000000..0324cdb874 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/TransportExceptionEventTest.php @@ -0,0 +1,43 @@ +_createException(); + $transport = $this->_createTransport(); + $evt = $this->_createEvent($transport, $ex); + $ref = $evt->getException(); + $this->assertEquals($ex, $ref, + '%s: Exception should be available via getException()' + ); + } + + public function testSourceIsTransport() + { + $ex = $this->_createException(); + $transport = $this->_createTransport(); + $evt = $this->_createEvent($transport, $ex); + $ref = $evt->getSource(); + $this->assertEquals($transport, $ref, + '%s: Transport should be available via getSource()' + ); + } + + // -- Creation Methods + + private function _createEvent(Swift_Transport $transport, Swift_TransportException $ex) + { + return new Swift_Events_TransportExceptionEvent($transport, $ex); + } + + private function _createTransport() + { + return $this->getMockBuilder('Swift_Transport')->getMock(); + } + + private function _createException() + { + return new Swift_TransportException(''); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/KeyCache/ArrayKeyCacheTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/KeyCache/ArrayKeyCacheTest.php new file mode 100644 index 0000000000..634b7ad098 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/KeyCache/ArrayKeyCacheTest.php @@ -0,0 +1,242 @@ +_createKeyCacheInputStream(); + $cache = $this->_createCache($is); + $cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->assertEquals('test', $cache->getString($this->_key1, 'foo')); + } + + public function testStringDataCanBeOverwritten() + { + $is = $this->_createKeyCacheInputStream(); + $cache = $this->_createCache($is); + $cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $cache->setString( + $this->_key1, 'foo', 'whatever', Swift_KeyCache::MODE_WRITE + ); + + $this->assertEquals('whatever', $cache->getString($this->_key1, 'foo')); + } + + public function testStringDataCanBeAppended() + { + $is = $this->_createKeyCacheInputStream(); + $cache = $this->_createCache($is); + $cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $cache->setString( + $this->_key1, 'foo', 'ing', Swift_KeyCache::MODE_APPEND + ); + + $this->assertEquals('testing', $cache->getString($this->_key1, 'foo')); + } + + public function testHasKeyReturnValue() + { + $is = $this->_createKeyCacheInputStream(); + $cache = $this->_createCache($is); + $cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + + $this->assertTrue($cache->hasKey($this->_key1, 'foo')); + } + + public function testNsKeyIsWellPartitioned() + { + $is = $this->_createKeyCacheInputStream(); + $cache = $this->_createCache($is); + $cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $cache->setString( + $this->_key2, 'foo', 'ing', Swift_KeyCache::MODE_WRITE + ); + + $this->assertEquals('test', $cache->getString($this->_key1, 'foo')); + $this->assertEquals('ing', $cache->getString($this->_key2, 'foo')); + } + + public function testItemKeyIsWellPartitioned() + { + $is = $this->_createKeyCacheInputStream(); + $cache = $this->_createCache($is); + $cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $cache->setString( + $this->_key1, 'bar', 'ing', Swift_KeyCache::MODE_WRITE + ); + + $this->assertEquals('test', $cache->getString($this->_key1, 'foo')); + $this->assertEquals('ing', $cache->getString($this->_key1, 'bar')); + } + + public function testByteStreamCanBeImported() + { + $os = $this->_createOutputStream(); + $os->expects($this->at(0)) + ->method('read') + ->will($this->returnValue('abc')); + $os->expects($this->at(1)) + ->method('read') + ->will($this->returnValue('def')); + $os->expects($this->at(2)) + ->method('read') + ->will($this->returnValue(false)); + + $is = $this->_createKeyCacheInputStream(); + $cache = $this->_createCache($is); + $cache->importFromByteStream( + $this->_key1, 'foo', $os, Swift_KeyCache::MODE_WRITE + ); + $this->assertEquals('abcdef', $cache->getString($this->_key1, 'foo')); + } + + public function testByteStreamCanBeAppended() + { + $os1 = $this->_createOutputStream(); + $os1->expects($this->at(0)) + ->method('read') + ->will($this->returnValue('abc')); + $os1->expects($this->at(1)) + ->method('read') + ->will($this->returnValue('def')); + $os1->expects($this->at(2)) + ->method('read') + ->will($this->returnValue(false)); + + $os2 = $this->_createOutputStream(); + $os2->expects($this->at(0)) + ->method('read') + ->will($this->returnValue('xyz')); + $os2->expects($this->at(1)) + ->method('read') + ->will($this->returnValue('uvw')); + $os2->expects($this->at(2)) + ->method('read') + ->will($this->returnValue(false)); + + $is = $this->_createKeyCacheInputStream(true); + + $cache = $this->_createCache($is); + + $cache->importFromByteStream( + $this->_key1, 'foo', $os1, Swift_KeyCache::MODE_APPEND + ); + $cache->importFromByteStream( + $this->_key1, 'foo', $os2, Swift_KeyCache::MODE_APPEND + ); + + $this->assertEquals('abcdefxyzuvw', $cache->getString($this->_key1, 'foo')); + } + + public function testByteStreamAndStringCanBeAppended() + { + $os = $this->_createOutputStream(); + $os->expects($this->at(0)) + ->method('read') + ->will($this->returnValue('abc')); + $os->expects($this->at(1)) + ->method('read') + ->will($this->returnValue('def')); + $os->expects($this->at(2)) + ->method('read') + ->will($this->returnValue(false)); + + $is = $this->_createKeyCacheInputStream(true); + + $cache = $this->_createCache($is); + + $cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_APPEND + ); + $cache->importFromByteStream( + $this->_key1, 'foo', $os, Swift_KeyCache::MODE_APPEND + ); + $this->assertEquals('testabcdef', $cache->getString($this->_key1, 'foo')); + } + + public function testDataCanBeExportedToByteStream() + { + //See acceptance test for more detail + $is = $this->_createInputStream(); + $is->expects($this->atLeastOnce()) + ->method('write'); + + $kcis = $this->_createKeyCacheInputStream(true); + + $cache = $this->_createCache($kcis); + + $cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + + $cache->exportToByteStream($this->_key1, 'foo', $is); + } + + public function testKeyCanBeCleared() + { + $is = $this->_createKeyCacheInputStream(); + $cache = $this->_createCache($is); + + $cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->assertTrue($cache->hasKey($this->_key1, 'foo')); + $cache->clearKey($this->_key1, 'foo'); + $this->assertFalse($cache->hasKey($this->_key1, 'foo')); + } + + public function testNsKeyCanBeCleared() + { + $is = $this->_createKeyCacheInputStream(); + $cache = $this->_createCache($is); + + $cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $cache->setString( + $this->_key1, 'bar', 'xyz', Swift_KeyCache::MODE_WRITE + ); + $this->assertTrue($cache->hasKey($this->_key1, 'foo')); + $this->assertTrue($cache->hasKey($this->_key1, 'bar')); + $cache->clearAll($this->_key1); + $this->assertFalse($cache->hasKey($this->_key1, 'foo')); + $this->assertFalse($cache->hasKey($this->_key1, 'bar')); + } + + // -- Creation methods + + private function _createCache($is) + { + return new Swift_KeyCache_ArrayKeyCache($is); + } + + private function _createKeyCacheInputStream() + { + return $this->getMockBuilder('Swift_KeyCache_KeyCacheInputStream')->getMock(); + } + + private function _createOutputStream() + { + return $this->getMockBuilder('Swift_OutputByteStream')->getMock(); + } + + private function _createInputStream() + { + return $this->getMockBuilder('Swift_InputByteStream')->getMock(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/KeyCache/SimpleKeyCacheInputStreamTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/KeyCache/SimpleKeyCacheInputStreamTest.php new file mode 100644 index 0000000000..38fbc0df66 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/KeyCache/SimpleKeyCacheInputStreamTest.php @@ -0,0 +1,73 @@ +getMockBuilder('Swift_KeyCache')->getMock(); + $cache->expects($this->at(0)) + ->method('setString') + ->with($this->_nsKey, 'foo', 'a', Swift_KeyCache::MODE_APPEND); + $cache->expects($this->at(1)) + ->method('setString') + ->with($this->_nsKey, 'foo', 'b', Swift_KeyCache::MODE_APPEND); + $cache->expects($this->at(2)) + ->method('setString') + ->with($this->_nsKey, 'foo', 'c', Swift_KeyCache::MODE_APPEND); + + $stream = new Swift_KeyCache_SimpleKeyCacheInputStream(); + $stream->setKeyCache($cache); + $stream->setNsKey($this->_nsKey); + $stream->setItemKey('foo'); + + $stream->write('a'); + $stream->write('b'); + $stream->write('c'); + } + + public function testFlushContentClearsKey() + { + $cache = $this->getMockBuilder('Swift_KeyCache')->getMock(); + $cache->expects($this->once()) + ->method('clearKey') + ->with($this->_nsKey, 'foo'); + + $stream = new Swift_KeyCache_SimpleKeyCacheInputStream(); + $stream->setKeyCache($cache); + $stream->setNsKey($this->_nsKey); + $stream->setItemKey('foo'); + + $stream->flushBuffers(); + } + + public function testClonedStreamStillReferencesSameCache() + { + $cache = $this->getMockBuilder('Swift_KeyCache')->getMock(); + $cache->expects($this->at(0)) + ->method('setString') + ->with($this->_nsKey, 'foo', 'a', Swift_KeyCache::MODE_APPEND); + $cache->expects($this->at(1)) + ->method('setString') + ->with($this->_nsKey, 'foo', 'b', Swift_KeyCache::MODE_APPEND); + $cache->expects($this->at(2)) + ->method('setString') + ->with('test', 'bar', 'x', Swift_KeyCache::MODE_APPEND); + + $stream = new Swift_KeyCache_SimpleKeyCacheInputStream(); + $stream->setKeyCache($cache); + $stream->setNsKey($this->_nsKey); + $stream->setItemKey('foo'); + + $stream->write('a'); + $stream->write('b'); + + $newStream = clone $stream; + $newStream->setKeyCache($cache); + $newStream->setNsKey('test'); + $newStream->setItemKey('bar'); + + $newStream->write('x'); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mailer/ArrayRecipientIteratorTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mailer/ArrayRecipientIteratorTest.php new file mode 100644 index 0000000000..ff0bce4fa3 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mailer/ArrayRecipientIteratorTest.php @@ -0,0 +1,42 @@ +assertFalse($it->hasNext()); + } + + public function testHasNextReturnsTrueIfItemsLeft() + { + $it = new Swift_Mailer_ArrayRecipientIterator(array('foo@bar' => 'Foo')); + $this->assertTrue($it->hasNext()); + } + + public function testReadingToEndOfListCausesHasNextToReturnFalse() + { + $it = new Swift_Mailer_ArrayRecipientIterator(array('foo@bar' => 'Foo')); + $this->assertTrue($it->hasNext()); + $it->nextRecipient(); + $this->assertFalse($it->hasNext()); + } + + public function testReturnedValueHasPreservedKeyValuePair() + { + $it = new Swift_Mailer_ArrayRecipientIterator(array('foo@bar' => 'Foo')); + $this->assertEquals(array('foo@bar' => 'Foo'), $it->nextRecipient()); + } + + public function testIteratorMovesNextAfterEachIteration() + { + $it = new Swift_Mailer_ArrayRecipientIterator(array( + 'foo@bar' => 'Foo', + 'zip@button' => 'Zip thing', + 'test@test' => null, + )); + $this->assertEquals(array('foo@bar' => 'Foo'), $it->nextRecipient()); + $this->assertEquals(array('zip@button' => 'Zip thing'), $it->nextRecipient()); + $this->assertEquals(array('test@test' => null), $it->nextRecipient()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/MailerTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/MailerTest.php new file mode 100644 index 0000000000..389264fb0f --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/MailerTest.php @@ -0,0 +1,147 @@ +_createTransport(); + $message = $this->_createMessage(); + + $started = false; + $transport->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$started) { + return $started; + }); + $transport->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$started) { + $started = true; + + return; + }); + + $mailer = $this->_createMailer($transport); + $mailer->send($message); + } + + public function testTransportIsOnlyStartedOnce() + { + $transport = $this->_createTransport(); + $message = $this->_createMessage(); + + $started = false; + $transport->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$started) { + return $started; + }); + $transport->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$started) { + $started = true; + + return; + }); + + $mailer = $this->_createMailer($transport); + for ($i = 0; $i < 10; ++$i) { + $mailer->send($message); + } + } + + public function testMessageIsPassedToTransport() + { + $transport = $this->_createTransport(); + $message = $this->_createMessage(); + $transport->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()); + + $mailer = $this->_createMailer($transport); + $mailer->send($message); + } + + public function testSendReturnsCountFromTransport() + { + $transport = $this->_createTransport(); + $message = $this->_createMessage(); + $transport->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturn(57); + + $mailer = $this->_createMailer($transport); + $this->assertEquals(57, $mailer->send($message)); + } + + public function testFailedRecipientReferenceIsPassedToTransport() + { + $failures = array(); + + $transport = $this->_createTransport(); + $message = $this->_createMessage(); + $transport->shouldReceive('send') + ->once() + ->with($message, $failures) + ->andReturn(57); + + $mailer = $this->_createMailer($transport); + $mailer->send($message, $failures); + } + + public function testSendRecordsRfcComplianceExceptionAsEntireSendFailure() + { + $failures = array(); + + $rfcException = new Swift_RfcComplianceException('test'); + $transport = $this->_createTransport(); + $message = $this->_createMessage(); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array('foo&invalid' => 'Foo', 'bar@valid.tld' => 'Bar')); + $transport->shouldReceive('send') + ->once() + ->with($message, $failures) + ->andThrow($rfcException); + + $mailer = $this->_createMailer($transport); + $this->assertEquals(0, $mailer->send($message, $failures), '%s: Should return 0'); + $this->assertEquals(array('foo&invalid', 'bar@valid.tld'), $failures, '%s: Failures should contain all addresses since the entire message failed to compile'); + } + + public function testRegisterPluginDelegatesToTransport() + { + $plugin = $this->_createPlugin(); + $transport = $this->_createTransport(); + $mailer = $this->_createMailer($transport); + + $transport->shouldReceive('registerPlugin') + ->once() + ->with($plugin); + + $mailer->registerPlugin($plugin); + } + + // -- Creation methods + + private function _createPlugin() + { + return $this->getMockery('Swift_Events_EventListener')->shouldIgnoreMissing(); + } + + private function _createTransport() + { + return $this->getMockery('Swift_Transport')->shouldIgnoreMissing(); + } + + private function _createMessage() + { + return $this->getMockery('Swift_Mime_Message')->shouldIgnoreMissing(); + } + + private function _createMailer(Swift_Transport $transport) + { + return new Swift_Mailer($transport); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/MessageTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/MessageTest.php new file mode 100644 index 0000000000..00039f10ca --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/MessageTest.php @@ -0,0 +1,130 @@ +_recursiveObjectCloningCheck($message1, $message2, $message1_clone); + } + + public function testCloningWithSigners() + { + $message1 = new Swift_Message('subj', 'body', 'ctype'); + $signer = new Swift_Signers_DKIMSigner(dirname(dirname(__DIR__)).'/_samples/dkim/dkim.test.priv', 'test.example', 'example'); + $message1->attachSigner($signer); + $message2 = new Swift_Message('subj', 'body', 'ctype'); + $signer = new Swift_Signers_DKIMSigner(dirname(dirname(__DIR__)).'/_samples/dkim/dkim.test.priv', 'test.example', 'example'); + $message2->attachSigner($signer); + $message1_clone = clone $message1; + + $this->_recursiveObjectCloningCheck($message1, $message2, $message1_clone); + } + + public function testBodySwap() + { + $message1 = new Swift_Message('Test'); + $html = Swift_MimePart::newInstance('', 'text/html'); + $html->getHeaders()->addTextHeader('X-Test-Remove', 'Test-Value'); + $html->getHeaders()->addTextHeader('X-Test-Alter', 'Test-Value'); + $message1->attach($html); + $source = $message1->toString(); + $message2 = clone $message1; + $message2->setSubject('Message2'); + foreach ($message2->getChildren() as $child) { + $child->setBody('Test'); + $child->getHeaders()->removeAll('X-Test-Remove'); + $child->getHeaders()->get('X-Test-Alter')->setValue('Altered'); + } + $final = $message1->toString(); + if ($source != $final) { + $this->fail("Difference although object cloned \n [".$source."]\n[".$final."]\n"); + } + $final = $message2->toString(); + if ($final == $source) { + $this->fail('Two body matches although they should differ'."\n [".$source."]\n[".$final."]\n"); + } + $id_1 = $message1->getId(); + $id_2 = $message2->getId(); + $this->assertEquals($id_1, $id_2, 'Message Ids differ'); + $id_2 = $message2->generateId(); + $this->assertNotEquals($id_1, $id_2, 'Message Ids are the same'); + } + + // -- Private helpers + protected function _recursiveObjectCloningCheck($obj1, $obj2, $obj1_clone) + { + $obj1_properties = (array) $obj1; + $obj2_properties = (array) $obj2; + $obj1_clone_properties = (array) $obj1_clone; + + foreach ($obj1_properties as $property => $value) { + if (is_object($value)) { + $obj1_value = $obj1_properties[$property]; + $obj2_value = $obj2_properties[$property]; + $obj1_clone_value = $obj1_clone_properties[$property]; + + if ($obj1_value !== $obj2_value) { + // two separetely instanciated objects property not referencing same object + $this->assertFalse( + // but object's clone does - not everything copied + $obj1_value === $obj1_clone_value, + "Property `$property` cloning error: source and cloned objects property is referencing same object" + ); + } else { + // two separetely instanciated objects have same reference + $this->assertFalse( + // but object's clone doesn't - overdone making copies + $obj1_value !== $obj1_clone_value, + "Property `$property` not properly cloned: it should reference same object as cloning source (overdone copping)" + ); + } + // recurse + $this->_recursiveObjectCloningCheck($obj1_value, $obj2_value, $obj1_clone_value); + } elseif (is_array($value)) { + $obj1_value = $obj1_properties[$property]; + $obj2_value = $obj2_properties[$property]; + $obj1_clone_value = $obj1_clone_properties[$property]; + + return $this->_recursiveArrayCloningCheck($obj1_value, $obj2_value, $obj1_clone_value); + } + } + } + + protected function _recursiveArrayCloningCheck($array1, $array2, $array1_clone) + { + foreach ($array1 as $key => $value) { + if (is_object($value)) { + $arr1_value = $array1[$key]; + $arr2_value = $array2[$key]; + $arr1_clone_value = $array1_clone[$key]; + if ($arr1_value !== $arr2_value) { + // two separetely instanciated objects property not referencing same object + $this->assertFalse( + // but object's clone does - not everything copied + $arr1_value === $arr1_clone_value, + "Key `$key` cloning error: source and cloned objects property is referencing same object" + ); + } else { + // two separetely instanciated objects have same reference + $this->assertFalse( + // but object's clone doesn't - overdone making copies + $arr1_value !== $arr1_clone_value, + "Key `$key` not properly cloned: it should reference same object as cloning source (overdone copping)" + ); + } + // recurse + $this->_recursiveObjectCloningCheck($arr1_value, $arr2_value, $arr1_clone_value); + } elseif (is_array($value)) { + $arr1_value = $array1[$key]; + $arr2_value = $array2[$key]; + $arr1_clone_value = $array1_clone[$key]; + + return $this->_recursiveArrayCloningCheck($arr1_value, $arr2_value, $arr1_clone_value); + } + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/AbstractMimeEntityTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/AbstractMimeEntityTest.php new file mode 100644 index 0000000000..ffe9cd8d66 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/AbstractMimeEntityTest.php @@ -0,0 +1,1054 @@ +_createHeaderSet(); + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $this->assertSame($headers, $entity->getHeaders()); + } + + public function testContentTypeIsReturnedFromHeader() + { + $ctype = $this->_createHeader('Content-Type', 'image/jpeg-test'); + $headers = $this->_createHeaderSet(array('Content-Type' => $ctype)); + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $this->assertEquals('image/jpeg-test', $entity->getContentType()); + } + + public function testContentTypeIsSetInHeader() + { + $ctype = $this->_createHeader('Content-Type', 'text/plain', array(), false); + $headers = $this->_createHeaderSet(array('Content-Type' => $ctype)); + + $ctype->shouldReceive('setFieldBodyModel') + ->once() + ->with('image/jpeg'); + $ctype->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes() + ->with(\Mockery::not('image/jpeg')); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $entity->setContentType('image/jpeg'); + } + + public function testContentTypeHeaderIsAddedIfNoneSet() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addParameterizedHeader') + ->once() + ->with('Content-Type', 'image/jpeg'); + $headers->shouldReceive('addParameterizedHeader') + ->zeroOrMoreTimes(); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $entity->setContentType('image/jpeg'); + } + + public function testContentTypeCanBeSetViaSetBody() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addParameterizedHeader') + ->once() + ->with('Content-Type', 'text/html'); + $headers->shouldReceive('addParameterizedHeader') + ->zeroOrMoreTimes(); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $entity->setBody('foo', 'text/html'); + } + + public function testGetEncoderFromConstructor() + { + $encoder = $this->_createEncoder('base64'); + $entity = $this->_createEntity($this->_createHeaderSet(), $encoder, + $this->_createCache() + ); + $this->assertSame($encoder, $entity->getEncoder()); + } + + public function testSetAndGetEncoder() + { + $encoder = $this->_createEncoder('base64'); + $headers = $this->_createHeaderSet(); + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $entity->setEncoder($encoder); + $this->assertSame($encoder, $entity->getEncoder()); + } + + public function testSettingEncoderUpdatesTransferEncoding() + { + $encoder = $this->_createEncoder('base64'); + $encoding = $this->_createHeader( + 'Content-Transfer-Encoding', '8bit', array(), false + ); + $headers = $this->_createHeaderSet(array( + 'Content-Transfer-Encoding' => $encoding, + )); + $encoding->shouldReceive('setFieldBodyModel') + ->once() + ->with('base64'); + $encoding->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes(); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $entity->setEncoder($encoder); + } + + public function testSettingEncoderAddsEncodingHeaderIfNonePresent() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addTextHeader') + ->once() + ->with('Content-Transfer-Encoding', 'something'); + $headers->shouldReceive('addTextHeader') + ->zeroOrMoreTimes(); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $entity->setEncoder($this->_createEncoder('something')); + } + + public function testIdIsReturnedFromHeader() + { + /* -- RFC 2045, 7. + In constructing a high-level user agent, it may be desirable to allow + one body to make reference to another. Accordingly, bodies may be + labelled using the "Content-ID" header field, which is syntactically + identical to the "Message-ID" header field + */ + + $cid = $this->_createHeader('Content-ID', 'zip@button'); + $headers = $this->_createHeaderSet(array('Content-ID' => $cid)); + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $this->assertEquals('zip@button', $entity->getId()); + } + + public function testIdIsSetInHeader() + { + $cid = $this->_createHeader('Content-ID', 'zip@button', array(), false); + $headers = $this->_createHeaderSet(array('Content-ID' => $cid)); + + $cid->shouldReceive('setFieldBodyModel') + ->once() + ->with('foo@bar'); + $cid->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes(); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $entity->setId('foo@bar'); + } + + public function testIdIsAutoGenerated() + { + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertRegExp('/^.*?@.*?$/D', $entity->getId()); + } + + public function testGenerateIdCreatesNewId() + { + $headers = $this->_createHeaderSet(); + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $id1 = $entity->generateId(); + $id2 = $entity->generateId(); + $this->assertNotEquals($id1, $id2); + } + + public function testGenerateIdSetsNewId() + { + $headers = $this->_createHeaderSet(); + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $id = $entity->generateId(); + $this->assertEquals($id, $entity->getId()); + } + + public function testDescriptionIsReadFromHeader() + { + /* -- RFC 2045, 8. + The ability to associate some descriptive information with a given + body is often desirable. For example, it may be useful to mark an + "image" body as "a picture of the Space Shuttle Endeavor." Such text + may be placed in the Content-Description header field. This header + field is always optional. + */ + + $desc = $this->_createHeader('Content-Description', 'something'); + $headers = $this->_createHeaderSet(array('Content-Description' => $desc)); + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $this->assertEquals('something', $entity->getDescription()); + } + + public function testDescriptionIsSetInHeader() + { + $desc = $this->_createHeader('Content-Description', '', array(), false); + $desc->shouldReceive('setFieldBodyModel')->once()->with('whatever'); + + $headers = $this->_createHeaderSet(array('Content-Description' => $desc)); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $entity->setDescription('whatever'); + } + + public function testDescriptionHeaderIsAddedIfNotPresent() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addTextHeader') + ->once() + ->with('Content-Description', 'whatever'); + $headers->shouldReceive('addTextHeader') + ->zeroOrMoreTimes(); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $entity->setDescription('whatever'); + } + + public function testSetAndGetMaxLineLength() + { + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $entity->setMaxLineLength(60); + $this->assertEquals(60, $entity->getMaxLineLength()); + } + + public function testEncoderIsUsedForStringGeneration() + { + $encoder = $this->_createEncoder('base64', false); + $encoder->expects($this->once()) + ->method('encodeString') + ->with('blah'); + + $entity = $this->_createEntity($this->_createHeaderSet(), + $encoder, $this->_createCache() + ); + $entity->setBody('blah'); + $entity->toString(); + } + + public function testMaxLineLengthIsProvidedWhenEncoding() + { + $encoder = $this->_createEncoder('base64', false); + $encoder->expects($this->once()) + ->method('encodeString') + ->with('blah', 0, 65); + + $entity = $this->_createEntity($this->_createHeaderSet(), + $encoder, $this->_createCache() + ); + $entity->setBody('blah'); + $entity->setMaxLineLength(65); + $entity->toString(); + } + + public function testHeadersAppearInString() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('toString') + ->once() + ->andReturn( + "Content-Type: text/plain; charset=utf-8\r\n". + "X-MyHeader: foobar\r\n" + ); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $this->assertEquals( + "Content-Type: text/plain; charset=utf-8\r\n". + "X-MyHeader: foobar\r\n", + $entity->toString() + ); + } + + public function testSetAndGetBody() + { + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $entity->setBody("blah\r\nblah!"); + $this->assertEquals("blah\r\nblah!", $entity->getBody()); + } + + public function testBodyIsAppended() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('toString') + ->once() + ->andReturn("Content-Type: text/plain; charset=utf-8\r\n"); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $entity->setBody("blah\r\nblah!"); + $this->assertEquals( + "Content-Type: text/plain; charset=utf-8\r\n". + "\r\n". + "blah\r\nblah!", + $entity->toString() + ); + } + + public function testGetBodyReturnsStringFromByteStream() + { + $os = $this->_createOutputStream('byte stream string'); + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $entity->setBody($os); + $this->assertEquals('byte stream string', $entity->getBody()); + } + + public function testByteStreamBodyIsAppended() + { + $headers = $this->_createHeaderSet(array(), false); + $os = $this->_createOutputStream('streamed'); + $headers->shouldReceive('toString') + ->once() + ->andReturn("Content-Type: text/plain; charset=utf-8\r\n"); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $entity->setBody($os); + $this->assertEquals( + "Content-Type: text/plain; charset=utf-8\r\n". + "\r\n". + 'streamed', + $entity->toString() + ); + } + + public function testBoundaryCanBeRetrieved() + { + /* -- RFC 2046, 5.1.1. + boundary := 0*69 bcharsnospace + + bchars := bcharsnospace / " " + + bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" / + "+" / "_" / "," / "-" / "." / + "/" / ":" / "=" / "?" + */ + + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertRegExp( + '/^[a-zA-Z0-9\'\(\)\+_\-,\.\/:=\?\ ]{0,69}[a-zA-Z0-9\'\(\)\+_\-,\.\/:=\?]$/D', + $entity->getBoundary() + ); + } + + public function testBoundaryNeverChanges() + { + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $firstBoundary = $entity->getBoundary(); + for ($i = 0; $i < 10; ++$i) { + $this->assertEquals($firstBoundary, $entity->getBoundary()); + } + } + + public function testBoundaryCanBeSet() + { + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $entity->setBoundary('foobar'); + $this->assertEquals('foobar', $entity->getBoundary()); + } + + public function testAddingChildrenGeneratesBoundaryInHeaders() + { + $child = $this->_createChild(); + $cType = $this->_createHeader('Content-Type', 'text/plain', array(), false); + $cType->shouldReceive('setParameter') + ->once() + ->with('boundary', \Mockery::any()); + $cType->shouldReceive('setParameter') + ->zeroOrMoreTimes(); + + $entity = $this->_createEntity($this->_createHeaderSet(array( + 'Content-Type' => $cType, + )), + $this->_createEncoder(), $this->_createCache() + ); + $entity->setChildren(array($child)); + } + + public function testChildrenOfLevelAttachmentAndLessCauseMultipartMixed() + { + for ($level = Swift_Mime_MimeEntity::LEVEL_MIXED; + $level > Swift_Mime_MimeEntity::LEVEL_TOP; $level /= 2) { + $child = $this->_createChild($level); + $cType = $this->_createHeader( + 'Content-Type', 'text/plain', array(), false + ); + $cType->shouldReceive('setFieldBodyModel') + ->once() + ->with('multipart/mixed'); + $cType->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes(); + + $entity = $this->_createEntity($this->_createHeaderSet(array( + 'Content-Type' => $cType, )), + $this->_createEncoder(), $this->_createCache() + ); + $entity->setChildren(array($child)); + } + } + + public function testChildrenOfLevelAlternativeAndLessCauseMultipartAlternative() + { + for ($level = Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE; + $level > Swift_Mime_MimeEntity::LEVEL_MIXED; $level /= 2) { + $child = $this->_createChild($level); + $cType = $this->_createHeader( + 'Content-Type', 'text/plain', array(), false + ); + $cType->shouldReceive('setFieldBodyModel') + ->once() + ->with('multipart/alternative'); + $cType->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes(); + + $entity = $this->_createEntity($this->_createHeaderSet(array( + 'Content-Type' => $cType, )), + $this->_createEncoder(), $this->_createCache() + ); + $entity->setChildren(array($child)); + } + } + + public function testChildrenOfLevelRelatedAndLessCauseMultipartRelated() + { + for ($level = Swift_Mime_MimeEntity::LEVEL_RELATED; + $level > Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE; $level /= 2) { + $child = $this->_createChild($level); + $cType = $this->_createHeader( + 'Content-Type', 'text/plain', array(), false + ); + $cType->shouldReceive('setFieldBodyModel') + ->once() + ->with('multipart/related'); + $cType->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes(); + + $entity = $this->_createEntity($this->_createHeaderSet(array( + 'Content-Type' => $cType, )), + $this->_createEncoder(), $this->_createCache() + ); + $entity->setChildren(array($child)); + } + } + + public function testHighestLevelChildDeterminesContentType() + { + $combinations = array( + array('levels' => array(Swift_Mime_MimeEntity::LEVEL_MIXED, + Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE, + Swift_Mime_MimeEntity::LEVEL_RELATED, + ), + 'type' => 'multipart/mixed', + ), + array('levels' => array(Swift_Mime_MimeEntity::LEVEL_MIXED, + Swift_Mime_MimeEntity::LEVEL_RELATED, + ), + 'type' => 'multipart/mixed', + ), + array('levels' => array(Swift_Mime_MimeEntity::LEVEL_MIXED, + Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE, + ), + 'type' => 'multipart/mixed', + ), + array('levels' => array(Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE, + Swift_Mime_MimeEntity::LEVEL_RELATED, + ), + 'type' => 'multipart/alternative', + ), + ); + + foreach ($combinations as $combination) { + $children = array(); + foreach ($combination['levels'] as $level) { + $children[] = $this->_createChild($level); + } + + $cType = $this->_createHeader( + 'Content-Type', 'text/plain', array(), false + ); + $cType->shouldReceive('setFieldBodyModel') + ->once() + ->with($combination['type']); + + $headerSet = $this->_createHeaderSet(array('Content-Type' => $cType)); + $headerSet->shouldReceive('newInstance') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use ($headerSet) { + return $headerSet; + }); + $entity = $this->_createEntity($headerSet, + $this->_createEncoder(), $this->_createCache() + ); + $entity->setChildren($children); + } + } + + public function testChildrenAppearNestedInString() + { + /* -- RFC 2046, 5.1.1. + (excerpt too verbose to paste here) + */ + + $headers = $this->_createHeaderSet(array(), false); + + $child1 = new MimeEntityFixture(Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE, + "Content-Type: text/plain\r\n". + "\r\n". + 'foobar', 'text/plain' + ); + + $child2 = new MimeEntityFixture(Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE, + "Content-Type: text/html\r\n". + "\r\n". + 'foobar', 'text/html' + ); + + $headers->shouldReceive('toString') + ->zeroOrMoreTimes() + ->andReturn("Content-Type: multipart/alternative; boundary=\"xxx\"\r\n"); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $entity->setBoundary('xxx'); + $entity->setChildren(array($child1, $child2)); + + $this->assertEquals( + "Content-Type: multipart/alternative; boundary=\"xxx\"\r\n". + "\r\n". + "\r\n--xxx\r\n". + "Content-Type: text/plain\r\n". + "\r\n". + "foobar\r\n". + "\r\n--xxx\r\n". + "Content-Type: text/html\r\n". + "\r\n". + "foobar\r\n". + "\r\n--xxx--\r\n", + $entity->toString() + ); + } + + public function testMixingLevelsIsHierarchical() + { + $headers = $this->_createHeaderSet(array(), false); + $newHeaders = $this->_createHeaderSet(array(), false); + + $part = $this->_createChild(Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE, + "Content-Type: text/plain\r\n". + "\r\n". + 'foobar' + ); + + $attachment = $this->_createChild(Swift_Mime_MimeEntity::LEVEL_MIXED, + "Content-Type: application/octet-stream\r\n". + "\r\n". + 'data' + ); + + $headers->shouldReceive('toString') + ->zeroOrMoreTimes() + ->andReturn("Content-Type: multipart/mixed; boundary=\"xxx\"\r\n"); + $headers->shouldReceive('newInstance') + ->zeroOrMoreTimes() + ->andReturn($newHeaders); + $newHeaders->shouldReceive('toString') + ->zeroOrMoreTimes() + ->andReturn("Content-Type: multipart/alternative; boundary=\"yyy\"\r\n"); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $entity->setBoundary('xxx'); + $entity->setChildren(array($part, $attachment)); + + $this->assertRegExp( + '~^'. + "Content-Type: multipart/mixed; boundary=\"xxx\"\r\n". + "\r\n\r\n--xxx\r\n". + "Content-Type: multipart/alternative; boundary=\"yyy\"\r\n". + "\r\n\r\n--(.*?)\r\n". + "Content-Type: text/plain\r\n". + "\r\n". + 'foobar'. + "\r\n\r\n--\\1--\r\n". + "\r\n\r\n--xxx\r\n". + "Content-Type: application/octet-stream\r\n". + "\r\n". + 'data'. + "\r\n\r\n--xxx--\r\n". + '$~', + $entity->toString() + ); + } + + public function testSettingEncoderNotifiesChildren() + { + $child = $this->_createChild(0, '', false); + $encoder = $this->_createEncoder('base64'); + + $child->shouldReceive('encoderChanged') + ->once() + ->with($encoder); + + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $entity->setChildren(array($child)); + $entity->setEncoder($encoder); + } + + public function testReceiptOfEncoderChangeNotifiesChildren() + { + $child = $this->_createChild(0, '', false); + $encoder = $this->_createEncoder('base64'); + + $child->shouldReceive('encoderChanged') + ->once() + ->with($encoder); + + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $entity->setChildren(array($child)); + $entity->encoderChanged($encoder); + } + + public function testReceiptOfCharsetChangeNotifiesChildren() + { + $child = $this->_createChild(0, '', false); + $child->shouldReceive('charsetChanged') + ->once() + ->with('windows-874'); + + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $entity->setChildren(array($child)); + $entity->charsetChanged('windows-874'); + } + + public function testEntityIsWrittenToByteStream() + { + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $is = $this->_createInputStream(false); + $is->expects($this->atLeastOnce()) + ->method('write'); + + $entity->toByteStream($is); + } + + public function testEntityHeadersAreComittedToByteStream() + { + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $is = $this->_createInputStream(false); + $is->expects($this->atLeastOnce()) + ->method('write'); + $is->expects($this->atLeastOnce()) + ->method('commit'); + + $entity->toByteStream($is); + } + + public function testOrderingTextBeforeHtml() + { + $htmlChild = new MimeEntityFixture(Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE, + "Content-Type: text/html\r\n". + "\r\n". + 'HTML PART', + 'text/html' + ); + $textChild = new MimeEntityFixture(Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE, + "Content-Type: text/plain\r\n". + "\r\n". + 'TEXT PART', + 'text/plain' + ); + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('toString') + ->zeroOrMoreTimes() + ->andReturn("Content-Type: multipart/alternative; boundary=\"xxx\"\r\n"); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $entity->setBoundary('xxx'); + $entity->setChildren(array($htmlChild, $textChild)); + + $this->assertEquals( + "Content-Type: multipart/alternative; boundary=\"xxx\"\r\n". + "\r\n\r\n--xxx\r\n". + "Content-Type: text/plain\r\n". + "\r\n". + 'TEXT PART'. + "\r\n\r\n--xxx\r\n". + "Content-Type: text/html\r\n". + "\r\n". + 'HTML PART'. + "\r\n\r\n--xxx--\r\n", + $entity->toString() + ); + } + + public function testUnsettingChildrenRestoresContentType() + { + $cType = $this->_createHeader('Content-Type', 'text/plain', array(), false); + $child = $this->_createChild(Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE); + + $cType->shouldReceive('setFieldBodyModel') + ->twice() + ->with('image/jpeg'); + $cType->shouldReceive('setFieldBodyModel') + ->once() + ->with('multipart/alternative'); + $cType->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes() + ->with(\Mockery::not('multipart/alternative', 'image/jpeg')); + + $entity = $this->_createEntity($this->_createHeaderSet(array( + 'Content-Type' => $cType, + )), + $this->_createEncoder(), $this->_createCache() + ); + + $entity->setContentType('image/jpeg'); + $entity->setChildren(array($child)); + $entity->setChildren(array()); + } + + public function testBodyIsReadFromCacheWhenUsingToStringIfPresent() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('toString') + ->zeroOrMoreTimes() + ->andReturn("Content-Type: text/plain; charset=utf-8\r\n"); + + $cache = $this->_createCache(false); + $cache->shouldReceive('hasKey') + ->once() + ->with(\Mockery::any(), 'body') + ->andReturn(true); + $cache->shouldReceive('getString') + ->once() + ->with(\Mockery::any(), 'body') + ->andReturn("\r\ncache\r\ncache!"); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $cache + ); + + $entity->setBody("blah\r\nblah!"); + $this->assertEquals( + "Content-Type: text/plain; charset=utf-8\r\n". + "\r\n". + "cache\r\ncache!", + $entity->toString() + ); + } + + public function testBodyIsAddedToCacheWhenUsingToString() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('toString') + ->zeroOrMoreTimes() + ->andReturn("Content-Type: text/plain; charset=utf-8\r\n"); + + $cache = $this->_createCache(false); + $cache->shouldReceive('hasKey') + ->once() + ->with(\Mockery::any(), 'body') + ->andReturn(false); + $cache->shouldReceive('setString') + ->once() + ->with(\Mockery::any(), 'body', "\r\nblah\r\nblah!", Swift_KeyCache::MODE_WRITE); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $cache + ); + + $entity->setBody("blah\r\nblah!"); + $entity->toString(); + } + + public function testBodyIsClearedFromCacheIfNewBodySet() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('toString') + ->zeroOrMoreTimes() + ->andReturn("Content-Type: text/plain; charset=utf-8\r\n"); + + $cache = $this->_createCache(false); + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $cache + ); + + $entity->setBody("blah\r\nblah!"); + $entity->toString(); + + // We set the expectation at this point because we only care what happens when calling setBody() + $cache->shouldReceive('clearKey') + ->once() + ->with(\Mockery::any(), 'body'); + + $entity->setBody("new\r\nnew!"); + } + + public function testBodyIsNotClearedFromCacheIfSameBodySet() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('toString') + ->zeroOrMoreTimes() + ->andReturn("Content-Type: text/plain; charset=utf-8\r\n"); + + $cache = $this->_createCache(false); + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $cache + ); + + $entity->setBody("blah\r\nblah!"); + $entity->toString(); + + // We set the expectation at this point because we only care what happens when calling setBody() + $cache->shouldReceive('clearKey') + ->never(); + + $entity->setBody("blah\r\nblah!"); + } + + public function testBodyIsClearedFromCacheIfNewEncoderSet() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('toString') + ->zeroOrMoreTimes() + ->andReturn("Content-Type: text/plain; charset=utf-8\r\n"); + + $cache = $this->_createCache(false); + $otherEncoder = $this->_createEncoder(); + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $cache + ); + + $entity->setBody("blah\r\nblah!"); + $entity->toString(); + + // We set the expectation at this point because we only care what happens when calling setEncoder() + $cache->shouldReceive('clearKey') + ->once() + ->with(\Mockery::any(), 'body'); + + $entity->setEncoder($otherEncoder); + } + + public function testBodyIsReadFromCacheWhenUsingToByteStreamIfPresent() + { + $is = $this->_createInputStream(); + $cache = $this->_createCache(false); + $cache->shouldReceive('hasKey') + ->once() + ->with(\Mockery::any(), 'body') + ->andReturn(true); + $cache->shouldReceive('exportToByteStream') + ->once() + ->with(\Mockery::any(), 'body', $is); + + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $cache + ); + $entity->setBody('foo'); + + $entity->toByteStream($is); + } + + public function testBodyIsAddedToCacheWhenUsingToByteStream() + { + $is = $this->_createInputStream(); + $cache = $this->_createCache(false); + $cache->shouldReceive('hasKey') + ->once() + ->with(\Mockery::any(), 'body') + ->andReturn(false); + $cache->shouldReceive('getInputByteStream') + ->once() + ->with(\Mockery::any(), 'body'); + + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $cache + ); + $entity->setBody('foo'); + + $entity->toByteStream($is); + } + + public function testFluidInterface() + { + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + + $this->assertSame($entity, + $entity + ->setContentType('text/plain') + ->setEncoder($this->_createEncoder()) + ->setId('foo@bar') + ->setDescription('my description') + ->setMaxLineLength(998) + ->setBody('xx') + ->setBoundary('xyz') + ->setChildren(array()) + ); + } + + // -- Private helpers + + abstract protected function _createEntity($headers, $encoder, $cache); + + protected function _createChild($level = null, $string = '', $stub = true) + { + $child = $this->getMockery('Swift_Mime_MimeEntity')->shouldIgnoreMissing(); + if (isset($level)) { + $child->shouldReceive('getNestingLevel') + ->zeroOrMoreTimes() + ->andReturn($level); + } + $child->shouldReceive('toString') + ->zeroOrMoreTimes() + ->andReturn($string); + + return $child; + } + + protected function _createEncoder($name = 'quoted-printable', $stub = true) + { + $encoder = $this->getMockBuilder('Swift_Mime_ContentEncoder')->getMock(); + $encoder->expects($this->any()) + ->method('getName') + ->will($this->returnValue($name)); + $encoder->expects($this->any()) + ->method('encodeString') + ->will($this->returnCallback(function () { + $args = func_get_args(); + + return array_shift($args); + })); + + return $encoder; + } + + protected function _createCache($stub = true) + { + return $this->getMockery('Swift_KeyCache')->shouldIgnoreMissing(); + } + + protected function _createHeaderSet($headers = array(), $stub = true) + { + $set = $this->getMockery('Swift_Mime_HeaderSet')->shouldIgnoreMissing(); + $set->shouldReceive('get') + ->zeroOrMoreTimes() + ->andReturnUsing(function ($key) use ($headers) { + return $headers[$key]; + }); + $set->shouldReceive('has') + ->zeroOrMoreTimes() + ->andReturnUsing(function ($key) use ($headers) { + return array_key_exists($key, $headers); + }); + + return $set; + } + + protected function _createHeader($name, $model = null, $params = array(), $stub = true) + { + $header = $this->getMockery('Swift_Mime_ParameterizedHeader')->shouldIgnoreMissing(); + $header->shouldReceive('getFieldName') + ->zeroOrMoreTimes() + ->andReturn($name); + $header->shouldReceive('getFieldBodyModel') + ->zeroOrMoreTimes() + ->andReturn($model); + $header->shouldReceive('getParameter') + ->zeroOrMoreTimes() + ->andReturnUsing(function ($key) use ($params) { + return $params[$key]; + }); + + return $header; + } + + protected function _createOutputStream($data = null, $stub = true) + { + $os = $this->getMockery('Swift_OutputByteStream'); + if (isset($data)) { + $os->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use ($data) { + static $first = true; + if (!$first) { + return false; + } + + $first = false; + + return $data; + }); + $os->shouldReceive('setReadPointer') + ->zeroOrMoreTimes(); + } + + return $os; + } + + protected function _createInputStream($stub = true) + { + return $this->getMockBuilder('Swift_InputByteStream')->getMock(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/AttachmentTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/AttachmentTest.php new file mode 100644 index 0000000000..bd2499c0cd --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/AttachmentTest.php @@ -0,0 +1,320 @@ +_createAttachment($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals( + Swift_Mime_MimeEntity::LEVEL_MIXED, $attachment->getNestingLevel() + ); + } + + public function testDispositionIsReturnedFromHeader() + { + /* -- RFC 2183, 2.1, 2.2. + */ + + $disposition = $this->_createHeader('Content-Disposition', 'attachment'); + $attachment = $this->_createAttachment($this->_createHeaderSet(array( + 'Content-Disposition' => $disposition, )), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals('attachment', $attachment->getDisposition()); + } + + public function testDispositionIsSetInHeader() + { + $disposition = $this->_createHeader('Content-Disposition', 'attachment', + array(), false + ); + $disposition->shouldReceive('setFieldBodyModel') + ->once() + ->with('inline'); + $disposition->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes(); + + $attachment = $this->_createAttachment($this->_createHeaderSet(array( + 'Content-Disposition' => $disposition, )), + $this->_createEncoder(), $this->_createCache() + ); + $attachment->setDisposition('inline'); + } + + public function testDispositionIsAddedIfNonePresent() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addParameterizedHeader') + ->once() + ->with('Content-Disposition', 'inline'); + $headers->shouldReceive('addParameterizedHeader') + ->zeroOrMoreTimes(); + + $attachment = $this->_createAttachment($headers, $this->_createEncoder(), + $this->_createCache() + ); + $attachment->setDisposition('inline'); + } + + public function testDispositionIsAutoDefaultedToAttachment() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addParameterizedHeader') + ->once() + ->with('Content-Disposition', 'attachment'); + $headers->shouldReceive('addParameterizedHeader') + ->zeroOrMoreTimes(); + + $attachment = $this->_createAttachment($headers, $this->_createEncoder(), + $this->_createCache() + ); + } + + public function testDefaultContentTypeInitializedToOctetStream() + { + $cType = $this->_createHeader('Content-Type', '', + array(), false + ); + $cType->shouldReceive('setFieldBodyModel') + ->once() + ->with('application/octet-stream'); + $cType->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes(); + + $attachment = $this->_createAttachment($this->_createHeaderSet(array( + 'Content-Type' => $cType, )), + $this->_createEncoder(), $this->_createCache() + ); + } + + public function testFilenameIsReturnedFromHeader() + { + /* -- RFC 2183, 2.3. + */ + + $disposition = $this->_createHeader('Content-Disposition', 'attachment', + array('filename' => 'foo.txt') + ); + $attachment = $this->_createAttachment($this->_createHeaderSet(array( + 'Content-Disposition' => $disposition, )), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals('foo.txt', $attachment->getFilename()); + } + + public function testFilenameIsSetInHeader() + { + $disposition = $this->_createHeader('Content-Disposition', 'attachment', + array('filename' => 'foo.txt'), false + ); + $disposition->shouldReceive('setParameter') + ->once() + ->with('filename', 'bar.txt'); + $disposition->shouldReceive('setParameter') + ->zeroOrMoreTimes(); + + $attachment = $this->_createAttachment($this->_createHeaderSet(array( + 'Content-Disposition' => $disposition, )), + $this->_createEncoder(), $this->_createCache() + ); + $attachment->setFilename('bar.txt'); + } + + public function testSettingFilenameSetsNameInContentType() + { + /* + This is a legacy requirement which isn't covered by up-to-date RFCs. + */ + + $cType = $this->_createHeader('Content-Type', 'text/plain', + array(), false + ); + $cType->shouldReceive('setParameter') + ->once() + ->with('name', 'bar.txt'); + $cType->shouldReceive('setParameter') + ->zeroOrMoreTimes(); + + $attachment = $this->_createAttachment($this->_createHeaderSet(array( + 'Content-Type' => $cType, )), + $this->_createEncoder(), $this->_createCache() + ); + $attachment->setFilename('bar.txt'); + } + + public function testSizeIsReturnedFromHeader() + { + /* -- RFC 2183, 2.7. + */ + + $disposition = $this->_createHeader('Content-Disposition', 'attachment', + array('size' => 1234) + ); + $attachment = $this->_createAttachment($this->_createHeaderSet(array( + 'Content-Disposition' => $disposition, )), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals(1234, $attachment->getSize()); + } + + public function testSizeIsSetInHeader() + { + $disposition = $this->_createHeader('Content-Disposition', 'attachment', + array(), false + ); + $disposition->shouldReceive('setParameter') + ->once() + ->with('size', 12345); + $disposition->shouldReceive('setParameter') + ->zeroOrMoreTimes(); + + $attachment = $this->_createAttachment($this->_createHeaderSet(array( + 'Content-Disposition' => $disposition, )), + $this->_createEncoder(), $this->_createCache() + ); + $attachment->setSize(12345); + } + + public function testFilnameCanBeReadFromFileStream() + { + $file = $this->_createFileStream('/bar/file.ext', ''); + $disposition = $this->_createHeader('Content-Disposition', 'attachment', + array('filename' => 'foo.txt'), false + ); + $disposition->shouldReceive('setParameter') + ->once() + ->with('filename', 'file.ext'); + + $attachment = $this->_createAttachment($this->_createHeaderSet(array( + 'Content-Disposition' => $disposition, )), + $this->_createEncoder(), $this->_createCache() + ); + $attachment->setFile($file); + } + + public function testContentTypeCanBeSetViaSetFile() + { + $file = $this->_createFileStream('/bar/file.ext', ''); + $disposition = $this->_createHeader('Content-Disposition', 'attachment', + array('filename' => 'foo.txt'), false + ); + $disposition->shouldReceive('setParameter') + ->once() + ->with('filename', 'file.ext'); + + $ctype = $this->_createHeader('Content-Type', 'text/plain', array(), false); + $ctype->shouldReceive('setFieldBodyModel') + ->once() + ->with('text/html'); + $ctype->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes(); + + $headers = $this->_createHeaderSet(array( + 'Content-Disposition' => $disposition, + 'Content-Type' => $ctype, + )); + + $attachment = $this->_createAttachment($headers, $this->_createEncoder(), + $this->_createCache() + ); + $attachment->setFile($file, 'text/html'); + } + + public function XtestContentTypeCanBeLookedUpFromCommonListIfNotProvided() + { + $file = $this->_createFileStream('/bar/file.zip', ''); + $disposition = $this->_createHeader('Content-Disposition', 'attachment', + array('filename' => 'foo.zip'), false + ); + $disposition->shouldReceive('setParameter') + ->once() + ->with('filename', 'file.zip'); + + $ctype = $this->_createHeader('Content-Type', 'text/plain', array(), false); + $ctype->shouldReceive('setFieldBodyModel') + ->once() + ->with('application/zip'); + $ctype->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes(); + + $headers = $this->_createHeaderSet(array( + 'Content-Disposition' => $disposition, + 'Content-Type' => $ctype, + )); + + $attachment = $this->_createAttachment($headers, $this->_createEncoder(), + $this->_createCache(), array('zip' => 'application/zip', 'txt' => 'text/plain') + ); + $attachment->setFile($file); + } + + public function testDataCanBeReadFromFile() + { + $file = $this->_createFileStream('/foo/file.ext', ''); + $attachment = $this->_createAttachment($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $attachment->setFile($file); + $this->assertEquals('', $attachment->getBody()); + } + + public function testFluidInterface() + { + $attachment = $this->_createAttachment($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertSame($attachment, + $attachment + ->setContentType('application/pdf') + ->setEncoder($this->_createEncoder()) + ->setId('foo@bar') + ->setDescription('my pdf') + ->setMaxLineLength(998) + ->setBody('xx') + ->setBoundary('xyz') + ->setChildren(array()) + ->setDisposition('inline') + ->setFilename('afile.txt') + ->setSize(123) + ->setFile($this->_createFileStream('foo.txt', '')) + ); + } + + // -- Private helpers + + protected function _createEntity($headers, $encoder, $cache) + { + return $this->_createAttachment($headers, $encoder, $cache); + } + + protected function _createAttachment($headers, $encoder, $cache, $mimeTypes = array()) + { + return new Swift_Mime_Attachment($headers, $encoder, $cache, new Swift_Mime_Grammar(), $mimeTypes); + } + + protected function _createFileStream($path, $data, $stub = true) + { + $file = $this->getMockery('Swift_FileStream'); + $file->shouldReceive('getPath') + ->zeroOrMoreTimes() + ->andReturn($path); + $file->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use ($data) { + static $first = true; + if (!$first) { + return false; + } + + $first = false; + + return $data; + }); + $file->shouldReceive('setReadPointer') + ->zeroOrMoreTimes(); + + return $file; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/ContentEncoder/Base64ContentEncoderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/ContentEncoder/Base64ContentEncoderTest.php new file mode 100644 index 0000000000..0442af33fd --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/ContentEncoder/Base64ContentEncoderTest.php @@ -0,0 +1,323 @@ +_encoder = new Swift_Mime_ContentEncoder_Base64ContentEncoder(); + } + + public function testNameIsBase64() + { + $this->assertEquals('base64', $this->_encoder->getName()); + } + + /* + There's really no point in testing the entire base64 encoding to the + level QP encoding has been tested. base64_encode() has been in PHP for + years. + */ + + public function testInputOutputRatioIs3to4Bytes() + { + /* + RFC 2045, 6.8 + + The encoding process represents 24-bit groups of input bits as output + strings of 4 encoded characters. Proceeding from left to right, a + 24-bit input group is formed by concatenating 3 8bit input groups. + These 24 bits are then treated as 4 concatenated 6-bit groups, each + of which is translated into a single digit in the base64 alphabet. + */ + + $os = $this->_createOutputByteStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $os->shouldReceive('read') + ->once() + ->andReturn('123'); + $os->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturn(false); + + $this->_encoder->encodeByteStream($os, $is); + $this->assertEquals('MTIz', $collection->content); + } + + public function testPadLength() + { + /* + RFC 2045, 6.8 + + Special processing is performed if fewer than 24 bits are available + at the end of the data being encoded. A full encoding quantum is + always completed at the end of a body. When fewer than 24 input bits + are available in an input group, zero bits are added (on the right) + to form an integral number of 6-bit groups. Padding at the end of + the data is performed using the "=" character. Since all base64 + input is an integral number of octets, only the following cases can + arise: (1) the final quantum of encoding input is an integral + multiple of 24 bits; here, the final unit of encoded output will be + an integral multiple of 4 characters with no "=" padding, (2) the + final quantum of encoding input is exactly 8 bits; here, the final + unit of encoded output will be two characters followed by two "=" + padding characters, or (3) the final quantum of encoding input is + exactly 16 bits; here, the final unit of encoded output will be three + characters followed by one "=" padding character. + */ + + for ($i = 0; $i < 30; ++$i) { + $os = $this->_createOutputByteStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $os->shouldReceive('read') + ->once() + ->andReturn(pack('C', rand(0, 255))); + $os->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturn(false); + + $this->_encoder->encodeByteStream($os, $is); + $this->assertRegExp('~^[a-zA-Z0-9/\+]{2}==$~', $collection->content, + '%s: A single byte should have 2 bytes of padding' + ); + } + + for ($i = 0; $i < 30; ++$i) { + $os = $this->_createOutputByteStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $os->shouldReceive('read') + ->once() + ->andReturn(pack('C*', rand(0, 255), rand(0, 255))); + $os->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturn(false); + + $this->_encoder->encodeByteStream($os, $is); + $this->assertRegExp('~^[a-zA-Z0-9/\+]{3}=$~', $collection->content, + '%s: Two bytes should have 1 byte of padding' + ); + } + + for ($i = 0; $i < 30; ++$i) { + $os = $this->_createOutputByteStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $os->shouldReceive('read') + ->once() + ->andReturn(pack('C*', rand(0, 255), rand(0, 255), rand(0, 255))); + $os->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturn(false); + + $this->_encoder->encodeByteStream($os, $is); + $this->assertRegExp('~^[a-zA-Z0-9/\+]{4}$~', $collection->content, + '%s: Three bytes should have no padding' + ); + } + } + + public function testMaximumLineLengthIs76Characters() + { + /* + The encoded output stream must be represented in lines of no more + than 76 characters each. All line breaks or other characters not + found in Table 1 must be ignored by decoding software. + */ + + $os = $this->_createOutputByteStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $os->shouldReceive('read') + ->once() + ->andReturn('abcdefghijkl'); //12 + $os->shouldReceive('read') + ->once() + ->andReturn('mnopqrstuvwx'); //24 + $os->shouldReceive('read') + ->once() + ->andReturn('yzabc1234567'); //36 + $os->shouldReceive('read') + ->once() + ->andReturn('890ABCDEFGHI'); //48 + $os->shouldReceive('read') + ->once() + ->andReturn('JKLMNOPQRSTU'); //60 + $os->shouldReceive('read') + ->once() + ->andReturn('VWXYZ1234567'); //72 + $os->shouldReceive('read') + ->once() + ->andReturn('abcdefghijkl'); //84 + $os->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturn(false); + + $this->_encoder->encodeByteStream($os, $is); + $this->assertEquals( + "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXphYmMxMjM0NTY3ODkwQUJDREVGR0hJSktMTU5PUFFS\r\n". + 'U1RVVldYWVoxMjM0NTY3YWJjZGVmZ2hpamts', + $collection->content + ); + } + + public function testMaximumLineLengthCanBeDifferent() + { + $os = $this->_createOutputByteStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $os->shouldReceive('read') + ->once() + ->andReturn('abcdefghijkl'); //12 + $os->shouldReceive('read') + ->once() + ->andReturn('mnopqrstuvwx'); //24 + $os->shouldReceive('read') + ->once() + ->andReturn('yzabc1234567'); //36 + $os->shouldReceive('read') + ->once() + ->andReturn('890ABCDEFGHI'); //48 + $os->shouldReceive('read') + ->once() + ->andReturn('JKLMNOPQRSTU'); //60 + $os->shouldReceive('read') + ->once() + ->andReturn('VWXYZ1234567'); //72 + $os->shouldReceive('read') + ->once() + ->andReturn('abcdefghijkl'); //84 + $os->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturn(false); + + $this->_encoder->encodeByteStream($os, $is, 0, 50); + $this->assertEquals( + "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXphYmMxMjM0NTY3OD\r\n". + "kwQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVoxMjM0NTY3YWJj\r\n". + 'ZGVmZ2hpamts', + $collection->content + ); + } + + public function testMaximumLineLengthIsNeverMoreThan76Chars() + { + $os = $this->_createOutputByteStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $os->shouldReceive('read') + ->once() + ->andReturn('abcdefghijkl'); //12 + $os->shouldReceive('read') + ->once() + ->andReturn('mnopqrstuvwx'); //24 + $os->shouldReceive('read') + ->once() + ->andReturn('yzabc1234567'); //36 + $os->shouldReceive('read') + ->once() + ->andReturn('890ABCDEFGHI'); //48 + $os->shouldReceive('read') + ->once() + ->andReturn('JKLMNOPQRSTU'); //60 + $os->shouldReceive('read') + ->once() + ->andReturn('VWXYZ1234567'); //72 + $os->shouldReceive('read') + ->once() + ->andReturn('abcdefghijkl'); //84 + $os->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturn(false); + + $this->_encoder->encodeByteStream($os, $is, 0, 100); + $this->assertEquals( + "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXphYmMxMjM0NTY3ODkwQUJDREVGR0hJSktMTU5PUFFS\r\n". + 'U1RVVldYWVoxMjM0NTY3YWJjZGVmZ2hpamts', + $collection->content + ); + } + + public function testFirstLineLengthCanBeDifferent() + { + $os = $this->_createOutputByteStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $os->shouldReceive('read') + ->once() + ->andReturn('abcdefghijkl'); //12 + $os->shouldReceive('read') + ->once() + ->andReturn('mnopqrstuvwx'); //24 + $os->shouldReceive('read') + ->once() + ->andReturn('yzabc1234567'); //36 + $os->shouldReceive('read') + ->once() + ->andReturn('890ABCDEFGHI'); //48 + $os->shouldReceive('read') + ->once() + ->andReturn('JKLMNOPQRSTU'); //60 + $os->shouldReceive('read') + ->once() + ->andReturn('VWXYZ1234567'); //72 + $os->shouldReceive('read') + ->once() + ->andReturn('abcdefghijkl'); //84 + $os->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturn(false); + + $this->_encoder->encodeByteStream($os, $is, 19); + $this->assertEquals( + "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXphYmMxMjM0NTY3ODkwQUJDR\r\n". + 'EVGR0hJSktMTU5PUFFSU1RVVldYWVoxMjM0NTY3YWJjZGVmZ2hpamts', + $collection->content + ); + } + + private function _createOutputByteStream($stub = false) + { + return $this->getMockery('Swift_OutputByteStream')->shouldIgnoreMissing(); + } + + private function _createInputByteStream($stub = false) + { + return $this->getMockery('Swift_InputByteStream')->shouldIgnoreMissing(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/ContentEncoder/PlainContentEncoderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/ContentEncoder/PlainContentEncoderTest.php new file mode 100644 index 0000000000..b523a64c6b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/ContentEncoder/PlainContentEncoderTest.php @@ -0,0 +1,173 @@ +_getEncoder('7bit'); + $this->assertEquals('7bit', $encoder->getName()); + + $encoder = $this->_getEncoder('8bit'); + $this->assertEquals('8bit', $encoder->getName()); + } + + public function testNoOctetsAreModifiedInString() + { + $encoder = $this->_getEncoder('7bit'); + foreach (range(0x00, 0xFF) as $octet) { + $byte = pack('C', $octet); + $this->assertIdenticalBinary($byte, $encoder->encodeString($byte)); + } + } + + public function testNoOctetsAreModifiedInByteStream() + { + $encoder = $this->_getEncoder('7bit'); + foreach (range(0x00, 0xFF) as $octet) { + $byte = pack('C', $octet); + + $os = $this->_createOutputByteStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $os->shouldReceive('read') + ->once() + ->andReturn($byte); + $os->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder->encodeByteStream($os, $is); + $this->assertIdenticalBinary($byte, $collection->content); + } + } + + public function testLineLengthCanBeSpecified() + { + $encoder = $this->_getEncoder('7bit'); + + $chars = array(); + for ($i = 0; $i < 50; ++$i) { + $chars[] = 'a'; + } + $input = implode(' ', $chars); //99 chars long + + $this->assertEquals( + 'a a a a a a a a a a a a a a a a a a a a a a a a a '."\r\n".//50 * + 'a a a a a a a a a a a a a a a a a a a a a a a a a', //99 + $encoder->encodeString($input, 0, 50), + '%s: Lines should be wrapped at 50 chars' + ); + } + + public function testLineLengthCanBeSpecifiedInByteStream() + { + $encoder = $this->_getEncoder('7bit'); + + $os = $this->_createOutputByteStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + + for ($i = 0; $i < 50; ++$i) { + $os->shouldReceive('read') + ->once() + ->andReturn('a '); + } + + $os->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder->encodeByteStream($os, $is, 0, 50); + $this->assertEquals( + str_repeat('a ', 25)."\r\n".str_repeat('a ', 25), + $collection->content + ); + } + + public function testencodeStringGeneratesCorrectCrlf() + { + $encoder = $this->_getEncoder('7bit', true); + $this->assertEquals("a\r\nb", $encoder->encodeString("a\rb"), + '%s: Line endings should be standardized' + ); + $this->assertEquals("a\r\nb", $encoder->encodeString("a\nb"), + '%s: Line endings should be standardized' + ); + $this->assertEquals("a\r\n\r\nb", $encoder->encodeString("a\n\rb"), + '%s: Line endings should be standardized' + ); + $this->assertEquals("a\r\n\r\nb", $encoder->encodeString("a\r\rb"), + '%s: Line endings should be standardized' + ); + $this->assertEquals("a\r\n\r\nb", $encoder->encodeString("a\n\nb"), + '%s: Line endings should be standardized' + ); + } + + public function crlfProvider() + { + return array( + array("\r", "a\r\nb"), + array("\n", "a\r\nb"), + array("\n\r", "a\r\n\r\nb"), + array("\n\n", "a\r\n\r\nb"), + array("\r\r", "a\r\n\r\nb"), + ); + } + + /** + * @dataProvider crlfProvider + */ + public function testCanonicEncodeByteStreamGeneratesCorrectCrlf($test, $expected) + { + $encoder = $this->_getEncoder('7bit', true); + + $os = $this->_createOutputByteStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $os->shouldReceive('read') + ->once() + ->andReturn('a'); + $os->shouldReceive('read') + ->once() + ->andReturn($test); + $os->shouldReceive('read') + ->once() + ->andReturn('b'); + $os->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder->encodeByteStream($os, $is); + $this->assertEquals($expected, $collection->content); + } + + // -- Private helpers + + private function _getEncoder($name, $canonical = false) + { + return new Swift_Mime_ContentEncoder_PlainContentEncoder($name, $canonical); + } + + private function _createOutputByteStream($stub = false) + { + return $this->getMockery('Swift_OutputByteStream')->shouldIgnoreMissing(); + } + + private function _createInputByteStream($stub = false) + { + return $this->getMockery('Swift_InputByteStream')->shouldIgnoreMissing(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/ContentEncoder/QpContentEncoderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/ContentEncoder/QpContentEncoderTest.php new file mode 100644 index 0000000000..8199289104 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/ContentEncoder/QpContentEncoderTest.php @@ -0,0 +1,518 @@ +_createCharacterStream(true) + ); + $this->assertEquals('quoted-printable', $encoder->getName()); + } + + /* -- RFC 2045, 6.7 -- + (1) (General 8bit representation) Any octet, except a CR or + LF that is part of a CRLF line break of the canonical + (standard) form of the data being encoded, may be + represented by an "=" followed by a two digit + hexadecimal representation of the octet's value. The + digits of the hexadecimal alphabet, for this purpose, + are "0123456789ABCDEF". Uppercase letters must be + used; lowercase letters are not allowed. Thus, for + example, the decimal value 12 (US-ASCII form feed) can + be represented by "=0C", and the decimal value 61 (US- + ASCII EQUAL SIGN) can be represented by "=3D". This + rule must be followed except when the following rules + allow an alternative encoding. + */ + + public function testPermittedCharactersAreNotEncoded() + { + /* -- RFC 2045, 6.7 -- + (2) (Literal representation) Octets with decimal values of + 33 through 60 inclusive, and 62 through 126, inclusive, + MAY be represented as the US-ASCII characters which + correspond to those octets (EXCLAMATION POINT through + LESS THAN, and GREATER THAN through TILDE, + respectively). + */ + + foreach (array_merge(range(33, 60), range(62, 126)) as $ordinal) { + $char = chr($ordinal); + + $os = $this->_createOutputByteStream(true); + $charStream = $this->_createCharacterStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importByteStream') + ->once() + ->with($os); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array($ordinal)); + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = new Swift_Mime_ContentEncoder_QpContentEncoder($charStream); + $encoder->encodeByteStream($os, $is); + $this->assertIdenticalBinary($char, $collection->content); + } + } + + public function testLinearWhiteSpaceAtLineEndingIsEncoded() + { + /* -- RFC 2045, 6.7 -- + (3) (White Space) Octets with values of 9 and 32 MAY be + represented as US-ASCII TAB (HT) and SPACE characters, + respectively, but MUST NOT be so represented at the end + of an encoded line. Any TAB (HT) or SPACE characters + on an encoded line MUST thus be followed on that line + by a printable character. In particular, an "=" at the + end of an encoded line, indicating a soft line break + (see rule #5) may follow one or more TAB (HT) or SPACE + characters. It follows that an octet with decimal + value 9 or 32 appearing at the end of an encoded line + must be represented according to Rule #1. This rule is + necessary because some MTAs (Message Transport Agents, + programs which transport messages from one user to + another, or perform a portion of such transfers) are + known to pad lines of text with SPACEs, and others are + known to remove "white space" characters from the end + of a line. Therefore, when decoding a Quoted-Printable + body, any trailing white space on a line must be + deleted, as it will necessarily have been added by + intermediate transport agents. + */ + + $HT = chr(0x09); //9 + $SPACE = chr(0x20); //32 + + //HT + $os = $this->_createOutputByteStream(true); + $charStream = $this->_createCharacterStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importByteStream') + ->once() + ->with($os); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('a'))); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x09)); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x09)); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x0D)); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x0A)); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('b'))); + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = new Swift_Mime_ContentEncoder_QpContentEncoder($charStream); + $encoder->encodeByteStream($os, $is); + + $this->assertEquals("a\t=09\r\nb", $collection->content); + + //SPACE + $os = $this->_createOutputByteStream(true); + $charStream = $this->_createCharacterStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importByteStream') + ->once() + ->with($os); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('a'))); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x20)); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x20)); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x0D)); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x0A)); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('b'))); + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = new Swift_Mime_ContentEncoder_QpContentEncoder($charStream); + $encoder->encodeByteStream($os, $is); + + $this->assertEquals("a =20\r\nb", $collection->content); + } + + public function testCRLFIsLeftAlone() + { + /* + (4) (Line Breaks) A line break in a text body, represented + as a CRLF sequence in the text canonical form, must be + represented by a (RFC 822) line break, which is also a + CRLF sequence, in the Quoted-Printable encoding. Since + the canonical representation of media types other than + text do not generally include the representation of + line breaks as CRLF sequences, no hard line breaks + (i.e. line breaks that are intended to be meaningful + and to be displayed to the user) can occur in the + quoted-printable encoding of such types. Sequences + like "=0D", "=0A", "=0A=0D" and "=0D=0A" will routinely + appear in non-text data represented in quoted- + printable, of course. + + Note that many implementations may elect to encode the + local representation of various content types directly + rather than converting to canonical form first, + encoding, and then converting back to local + representation. In particular, this may apply to plain + text material on systems that use newline conventions + other than a CRLF terminator sequence. Such an + implementation optimization is permissible, but only + when the combined canonicalization-encoding step is + equivalent to performing the three steps separately. + */ + + $os = $this->_createOutputByteStream(true); + $charStream = $this->_createCharacterStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importByteStream') + ->once() + ->with($os); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('a'))); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x0D)); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x0A)); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('b'))); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x0D)); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x0A)); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('c'))); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x0D)); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x0A)); + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = new Swift_Mime_ContentEncoder_QpContentEncoder($charStream); + $encoder->encodeByteStream($os, $is); + $this->assertEquals("a\r\nb\r\nc\r\n", $collection->content); + } + + public function testLinesLongerThan76CharactersAreSoftBroken() + { + /* + (5) (Soft Line Breaks) The Quoted-Printable encoding + REQUIRES that encoded lines be no more than 76 + characters long. If longer lines are to be encoded + with the Quoted-Printable encoding, "soft" line breaks + must be used. An equal sign as the last character on a + encoded line indicates such a non-significant ("soft") + line break in the encoded text. + */ + + $os = $this->_createOutputByteStream(true); + $charStream = $this->_createCharacterStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importByteStream') + ->once() + ->with($os); + + for ($seq = 0; $seq <= 140; ++$seq) { + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('a'))); + } + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = new Swift_Mime_ContentEncoder_QpContentEncoder($charStream); + $encoder->encodeByteStream($os, $is); + $this->assertEquals(str_repeat('a', 75)."=\r\n".str_repeat('a', 66), $collection->content); + } + + public function testMaxLineLengthCanBeSpecified() + { + $os = $this->_createOutputByteStream(true); + $charStream = $this->_createCharacterStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importByteStream') + ->once() + ->with($os); + + for ($seq = 0; $seq <= 100; ++$seq) { + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('a'))); + } + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = new Swift_Mime_ContentEncoder_QpContentEncoder($charStream); + $encoder->encodeByteStream($os, $is, 0, 54); + $this->assertEquals(str_repeat('a', 53)."=\r\n".str_repeat('a', 48), $collection->content); + } + + public function testBytesBelowPermittedRangeAreEncoded() + { + /* + According to Rule (1 & 2) + */ + + foreach (range(0, 32) as $ordinal) { + $char = chr($ordinal); + + $os = $this->_createOutputByteStream(true); + $charStream = $this->_createCharacterStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importByteStream') + ->once() + ->with($os); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array($ordinal)); + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = new Swift_Mime_ContentEncoder_QpContentEncoder($charStream); + $encoder->encodeByteStream($os, $is); + $this->assertEquals(sprintf('=%02X', $ordinal), $collection->content); + } + } + + public function testDecimalByte61IsEncoded() + { + /* + According to Rule (1 & 2) + */ + + $char = chr(61); + + $os = $this->_createOutputByteStream(true); + $charStream = $this->_createCharacterStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importByteStream') + ->once() + ->with($os); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(61)); + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = new Swift_Mime_ContentEncoder_QpContentEncoder($charStream); + $encoder->encodeByteStream($os, $is); + $this->assertEquals(sprintf('=%02X', 61), $collection->content); + } + + public function testBytesAbovePermittedRangeAreEncoded() + { + /* + According to Rule (1 & 2) + */ + + foreach (range(127, 255) as $ordinal) { + $char = chr($ordinal); + + $os = $this->_createOutputByteStream(true); + $charStream = $this->_createCharacterStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importByteStream') + ->once() + ->with($os); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array($ordinal)); + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = new Swift_Mime_ContentEncoder_QpContentEncoder($charStream); + $encoder->encodeByteStream($os, $is); + $this->assertEquals(sprintf('=%02X', $ordinal), $collection->content); + } + } + + public function testFirstLineLengthCanBeDifferent() + { + $os = $this->_createOutputByteStream(true); + $charStream = $this->_createCharacterStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importByteStream') + ->once() + ->with($os); + + for ($seq = 0; $seq <= 140; ++$seq) { + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('a'))); + } + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = new Swift_Mime_ContentEncoder_QpContentEncoder($charStream); + $encoder->encodeByteStream($os, $is, 22); + $this->assertEquals( + str_repeat('a', 53)."=\r\n".str_repeat('a', 75)."=\r\n".str_repeat('a', 13), + $collection->content + ); + } + + public function testObserverInterfaceCanChangeCharset() + { + $stream = $this->_createCharacterStream(); + $stream->shouldReceive('setCharacterSet') + ->once() + ->with('windows-1252'); + + $encoder = new Swift_Mime_ContentEncoder_QpContentEncoder($stream); + $encoder->charsetChanged('windows-1252'); + } + + public function testTextIsPreWrapped() + { + $encoder = $this->createEncoder(); + + $input = str_repeat('a', 70)."\r\n". + str_repeat('a', 70)."\r\n". + str_repeat('a', 70); + + $os = new Swift_ByteStream_ArrayByteStream(); + $is = new Swift_ByteStream_ArrayByteStream(); + $is->write($input); + + $encoder->encodeByteStream($is, $os); + + $this->assertEquals( + $input, $os->read(PHP_INT_MAX) + ); + } + + // -- Creation Methods + + private function _createCharacterStream($stub = false) + { + return $this->getMockery('Swift_CharacterStream')->shouldIgnoreMissing(); + } + + private function createEncoder() + { + $factory = new Swift_CharacterReaderFactory_SimpleCharacterReaderFactory(); + $charStream = new Swift_CharacterStream_NgCharacterStream($factory, 'utf-8'); + + return new Swift_Mime_ContentEncoder_QpContentEncoder($charStream); + } + + private function _createOutputByteStream($stub = false) + { + return $this->getMockery('Swift_OutputByteStream')->shouldIgnoreMissing(); + } + + private function _createInputByteStream($stub = false) + { + return $this->getMockery('Swift_InputByteStream')->shouldIgnoreMissing(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/EmbeddedFileTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/EmbeddedFileTest.php new file mode 100644 index 0000000000..f4c3ac8984 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/EmbeddedFileTest.php @@ -0,0 +1,57 @@ +_createEmbeddedFile($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals( + Swift_Mime_MimeEntity::LEVEL_RELATED, $file->getNestingLevel() + ); + } + + public function testIdIsAutoGenerated() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addIdHeader') + ->once() + ->with('Content-ID', '/^.*?@.*?$/D'); + + $file = $this->_createEmbeddedFile($headers, $this->_createEncoder(), + $this->_createCache() + ); + } + + public function testDefaultDispositionIsInline() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addParameterizedHeader') + ->once() + ->with('Content-Disposition', 'inline'); + $headers->shouldReceive('addParameterizedHeader') + ->zeroOrMoreTimes(); + + $file = $this->_createEmbeddedFile($headers, $this->_createEncoder(), + $this->_createCache() + ); + } + + // -- Private helpers + + protected function _createAttachment($headers, $encoder, $cache, $mimeTypes = array()) + { + return $this->_createEmbeddedFile($headers, $encoder, $cache, $mimeTypes); + } + + private function _createEmbeddedFile($headers, $encoder, $cache) + { + return new Swift_Mime_EmbeddedFile($headers, $encoder, $cache, new Swift_Mime_Grammar()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/HeaderEncoder/Base64HeaderEncoderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/HeaderEncoder/Base64HeaderEncoderTest.php new file mode 100644 index 0000000000..35801556e6 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/HeaderEncoder/Base64HeaderEncoderTest.php @@ -0,0 +1,13 @@ +assertEquals('B', $encoder->getName()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/HeaderEncoder/QpHeaderEncoderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/HeaderEncoder/QpHeaderEncoderTest.php new file mode 100644 index 0000000000..54a792a67d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/HeaderEncoder/QpHeaderEncoderTest.php @@ -0,0 +1,223 @@ +_createEncoder( + $this->_createCharacterStream(true) + ); + $this->assertEquals('Q', $encoder->getName()); + } + + public function testSpaceAndTabNeverAppear() + { + /* -- RFC 2047, 4. + Only a subset of the printable ASCII characters may be used in + 'encoded-text'. Space and tab characters are not allowed, so that + the beginning and end of an 'encoded-word' are obvious. + */ + + $charStream = $this->_createCharacterStream(); + $charStream->shouldReceive('readBytes') + ->atLeast()->times(6) + ->andReturn(array(ord('a')), array(0x20), array(0x09), array(0x20), array(ord('b')), false); + + $encoder = $this->_createEncoder($charStream); + $this->assertNotRegExp('~[ \t]~', $encoder->encodeString("a \t b"), + '%s: encoded-words in headers cannot contain LWSP as per RFC 2047.' + ); + } + + public function testSpaceIsRepresentedByUnderscore() + { + /* -- RFC 2047, 4.2. + (2) The 8-bit hexadecimal value 20 (e.g., ISO-8859-1 SPACE) may be + represented as "_" (underscore, ASCII 95.). (This character may + not pass through some internetwork mail gateways, but its use + will greatly enhance readability of "Q" encoded data with mail + readers that do not support this encoding.) Note that the "_" + always represents hexadecimal 20, even if the SPACE character + occupies a different code position in the character set in use. + */ + $charStream = $this->_createCharacterStream(); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('a'))); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x20)); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('b'))); + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = $this->_createEncoder($charStream); + $this->assertEquals('a_b', $encoder->encodeString('a b'), + '%s: Spaces can be represented by more readable underscores as per RFC 2047.' + ); + } + + public function testEqualsAndQuestionAndUnderscoreAreEncoded() + { + /* -- RFC 2047, 4.2. + (3) 8-bit values which correspond to printable ASCII characters other + than "=", "?", and "_" (underscore), MAY be represented as those + characters. (But see section 5 for restrictions.) In + particular, SPACE and TAB MUST NOT be represented as themselves + within encoded words. + */ + $charStream = $this->_createCharacterStream(); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('='))); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('?'))); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('_'))); + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = $this->_createEncoder($charStream); + $this->assertEquals('=3D=3F=5F', $encoder->encodeString('=?_'), + '%s: Chars =, ? and _ (underscore) may not appear as per RFC 2047.' + ); + } + + public function testParensAndQuotesAreEncoded() + { + /* -- RFC 2047, 5 (2). + A "Q"-encoded 'encoded-word' which appears in a 'comment' MUST NOT + contain the characters "(", ")" or " + */ + + $charStream = $this->_createCharacterStream(); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('('))); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('"'))); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord(')'))); + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = $this->_createEncoder($charStream); + $this->assertEquals('=28=22=29', $encoder->encodeString('(")'), + '%s: Chars (, " (DQUOTE) and ) may not appear as per RFC 2047.' + ); + } + + public function testOnlyCharactersAllowedInPhrasesAreUsed() + { + /* -- RFC 2047, 5. + (3) As a replacement for a 'word' entity within a 'phrase', for example, + one that precedes an address in a From, To, or Cc header. The ABNF + definition for 'phrase' from RFC 822 thus becomes: + + phrase = 1*( encoded-word / word ) + + In this case the set of characters that may be used in a "Q"-encoded + 'encoded-word' is restricted to: . An 'encoded-word' that appears within a + 'phrase' MUST be separated from any adjacent 'word', 'text' or + 'special' by 'linear-white-space'. + */ + + $allowedBytes = array_merge( + range(ord('a'), ord('z')), range(ord('A'), ord('Z')), + range(ord('0'), ord('9')), + array(ord('!'), ord('*'), ord('+'), ord('-'), ord('/')) + ); + + foreach (range(0x00, 0xFF) as $byte) { + $char = pack('C', $byte); + + $charStream = $this->_createCharacterStream(); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array($byte)); + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = $this->_createEncoder($charStream); + $encodedChar = $encoder->encodeString($char); + + if (in_array($byte, $allowedBytes)) { + $this->assertEquals($char, $encodedChar, + '%s: Character '.$char.' should not be encoded.' + ); + } elseif (0x20 == $byte) { + //Special case + $this->assertEquals('_', $encodedChar, + '%s: Space character should be replaced.' + ); + } else { + $this->assertEquals(sprintf('=%02X', $byte), $encodedChar, + '%s: Byte '.$byte.' should be encoded.' + ); + } + } + } + + public function testEqualsNeverAppearsAtEndOfLine() + { + /* -- RFC 2047, 5 (3). + The 'encoded-text' in an 'encoded-word' must be self-contained; + 'encoded-text' MUST NOT be continued from one 'encoded-word' to + another. This implies that the 'encoded-text' portion of a "B" + 'encoded-word' will be a multiple of 4 characters long; for a "Q" + 'encoded-word', any "=" character that appears in the 'encoded-text' + portion will be followed by two hexadecimal characters. + */ + + $input = str_repeat('a', 140); + + $charStream = $this->_createCharacterStream(); + + $output = ''; + $seq = 0; + for (; $seq < 140; ++$seq) { + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('a'))); + + if (75 == $seq) { + $output .= "\r\n"; // =\r\n + } + $output .= 'a'; + } + + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = $this->_createEncoder($charStream); + $this->assertEquals($output, $encoder->encodeString($input)); + } + + // -- Creation Methods + + private function _createEncoder($charStream) + { + return new Swift_Mime_HeaderEncoder_QpHeaderEncoder($charStream); + } + + private function _createCharacterStream($stub = false) + { + return $this->getMockery('Swift_CharacterStream')->shouldIgnoreMissing(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/DateHeaderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/DateHeaderTest.php new file mode 100644 index 0000000000..1822ea688b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/DateHeaderTest.php @@ -0,0 +1,69 @@ +_getHeader('Date'); + $this->assertEquals(Swift_Mime_Header::TYPE_DATE, $header->getFieldType()); + } + + public function testGetTimestamp() + { + $timestamp = time(); + $header = $this->_getHeader('Date'); + $header->setTimestamp($timestamp); + $this->assertSame($timestamp, $header->getTimestamp()); + } + + public function testTimestampCanBeSetBySetter() + { + $timestamp = time(); + $header = $this->_getHeader('Date'); + $header->setTimestamp($timestamp); + $this->assertSame($timestamp, $header->getTimestamp()); + } + + public function testIntegerTimestampIsConvertedToRfc2822Date() + { + $timestamp = time(); + $header = $this->_getHeader('Date'); + $header->setTimestamp($timestamp); + $this->assertEquals(date('r', $timestamp), $header->getFieldBody()); + } + + public function testSetBodyModel() + { + $timestamp = time(); + $header = $this->_getHeader('Date'); + $header->setFieldBodyModel($timestamp); + $this->assertEquals(date('r', $timestamp), $header->getFieldBody()); + } + + public function testGetBodyModel() + { + $timestamp = time(); + $header = $this->_getHeader('Date'); + $header->setTimestamp($timestamp); + $this->assertEquals($timestamp, $header->getFieldBodyModel()); + } + + public function testToString() + { + $timestamp = time(); + $header = $this->_getHeader('Date'); + $header->setTimestamp($timestamp); + $this->assertEquals('Date: '.date('r', $timestamp)."\r\n", + $header->toString() + ); + } + + private function _getHeader($name) + { + return new Swift_Mime_Headers_DateHeader($name, new Swift_Mime_Grammar()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/IdentificationHeaderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/IdentificationHeaderTest.php new file mode 100644 index 0000000000..93b3f60921 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/IdentificationHeaderTest.php @@ -0,0 +1,189 @@ +_getHeader('Message-ID'); + $this->assertEquals(Swift_Mime_Header::TYPE_ID, $header->getFieldType()); + } + + public function testValueMatchesMsgIdSpec() + { + /* -- RFC 2822, 3.6.4. + message-id = "Message-ID:" msg-id CRLF + + in-reply-to = "In-Reply-To:" 1*msg-id CRLF + + references = "References:" 1*msg-id CRLF + + msg-id = [CFWS] "<" id-left "@" id-right ">" [CFWS] + + id-left = dot-atom-text / no-fold-quote / obs-id-left + + id-right = dot-atom-text / no-fold-literal / obs-id-right + + no-fold-quote = DQUOTE *(qtext / quoted-pair) DQUOTE + + no-fold-literal = "[" *(dtext / quoted-pair) "]" + */ + + $header = $this->_getHeader('Message-ID'); + $header->setId('id-left@id-right'); + $this->assertEquals('', $header->getFieldBody()); + } + + public function testIdCanBeRetrievedVerbatim() + { + $header = $this->_getHeader('Message-ID'); + $header->setId('id-left@id-right'); + $this->assertEquals('id-left@id-right', $header->getId()); + } + + public function testMultipleIdsCanBeSet() + { + $header = $this->_getHeader('References'); + $header->setIds(array('a@b', 'x@y')); + $this->assertEquals(array('a@b', 'x@y'), $header->getIds()); + } + + public function testSettingMultipleIdsProducesAListValue() + { + /* -- RFC 2822, 3.6.4. + The "References:" and "In-Reply-To:" field each contain one or more + unique message identifiers, optionally separated by CFWS. + + .. SNIP .. + + in-reply-to = "In-Reply-To:" 1*msg-id CRLF + + references = "References:" 1*msg-id CRLF + */ + + $header = $this->_getHeader('References'); + $header->setIds(array('a@b', 'x@y')); + $this->assertEquals(' ', $header->getFieldBody()); + } + + public function testIdLeftCanBeQuoted() + { + /* -- RFC 2822, 3.6.4. + id-left = dot-atom-text / no-fold-quote / obs-id-left + */ + + $header = $this->_getHeader('References'); + $header->setId('"ab"@c'); + $this->assertEquals('"ab"@c', $header->getId()); + $this->assertEquals('<"ab"@c>', $header->getFieldBody()); + } + + public function testIdLeftCanContainAnglesAsQuotedPairs() + { + /* -- RFC 2822, 3.6.4. + no-fold-quote = DQUOTE *(qtext / quoted-pair) DQUOTE + */ + + $header = $this->_getHeader('References'); + $header->setId('"a\\<\\>b"@c'); + $this->assertEquals('"a\\<\\>b"@c', $header->getId()); + $this->assertEquals('<"a\\<\\>b"@c>', $header->getFieldBody()); + } + + public function testIdLeftCanBeDotAtom() + { + $header = $this->_getHeader('References'); + $header->setId('a.b+&%$.c@d'); + $this->assertEquals('a.b+&%$.c@d', $header->getId()); + $this->assertEquals('', $header->getFieldBody()); + } + + public function testInvalidIdLeftThrowsException() + { + try { + $header = $this->_getHeader('References'); + $header->setId('a b c@d'); + $this->fail( + 'Exception should be thrown since "a b c" is not valid id-left.' + ); + } catch (Exception $e) { + } + } + + public function testIdRightCanBeDotAtom() + { + /* -- RFC 2822, 3.6.4. + id-right = dot-atom-text / no-fold-literal / obs-id-right + */ + + $header = $this->_getHeader('References'); + $header->setId('a@b.c+&%$.d'); + $this->assertEquals('a@b.c+&%$.d', $header->getId()); + $this->assertEquals('', $header->getFieldBody()); + } + + public function testIdRightCanBeLiteral() + { + /* -- RFC 2822, 3.6.4. + no-fold-literal = "[" *(dtext / quoted-pair) "]" + */ + + $header = $this->_getHeader('References'); + $header->setId('a@[1.2.3.4]'); + $this->assertEquals('a@[1.2.3.4]', $header->getId()); + $this->assertEquals('', $header->getFieldBody()); + } + + public function testInvalidIdRightThrowsException() + { + try { + $header = $this->_getHeader('References'); + $header->setId('a@b c d'); + $this->fail( + 'Exception should be thrown since "b c d" is not valid id-right.' + ); + } catch (Exception $e) { + } + } + + public function testMissingAtSignThrowsException() + { + /* -- RFC 2822, 3.6.4. + msg-id = [CFWS] "<" id-left "@" id-right ">" [CFWS] + */ + + try { + $header = $this->_getHeader('References'); + $header->setId('abc'); + $this->fail( + 'Exception should be thrown since "abc" is does not contain @.' + ); + } catch (Exception $e) { + } + } + + public function testSetBodyModel() + { + $header = $this->_getHeader('Message-ID'); + $header->setFieldBodyModel('a@b'); + $this->assertEquals(array('a@b'), $header->getIds()); + } + + public function testGetBodyModel() + { + $header = $this->_getHeader('Message-ID'); + $header->setId('a@b'); + $this->assertEquals(array('a@b'), $header->getFieldBodyModel()); + } + + public function testStringValue() + { + $header = $this->_getHeader('References'); + $header->setIds(array('a@b', 'x@y')); + $this->assertEquals('References: '."\r\n", $header->toString()); + } + + private function _getHeader($name) + { + return new Swift_Mime_Headers_IdentificationHeader($name, new Swift_Mime_Grammar()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/MailboxHeaderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/MailboxHeaderTest.php new file mode 100644 index 0000000000..0713ff4e78 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/MailboxHeaderTest.php @@ -0,0 +1,327 @@ +_getHeader('To', $this->_getEncoder('Q', true)); + $this->assertEquals(Swift_Mime_Header::TYPE_MAILBOX, $header->getFieldType()); + } + + public function testMailboxIsSetForAddress() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setAddresses('chris@swiftmailer.org'); + $this->assertEquals(array('chris@swiftmailer.org'), + $header->getNameAddressStrings() + ); + } + + public function testMailboxIsRenderedForNameAddress() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setNameAddresses(array('chris@swiftmailer.org' => 'Chris Corbyn')); + $this->assertEquals( + array('Chris Corbyn '), $header->getNameAddressStrings() + ); + } + + public function testAddressCanBeReturnedForAddress() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setAddresses('chris@swiftmailer.org'); + $this->assertEquals(array('chris@swiftmailer.org'), $header->getAddresses()); + } + + public function testAddressCanBeReturnedForNameAddress() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setNameAddresses(array('chris@swiftmailer.org' => 'Chris Corbyn')); + $this->assertEquals(array('chris@swiftmailer.org'), $header->getAddresses()); + } + + public function testQuotesInNameAreQuoted() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setNameAddresses(array( + 'chris@swiftmailer.org' => 'Chris Corbyn, "DHE"', + )); + $this->assertEquals( + array('"Chris Corbyn, \"DHE\"" '), + $header->getNameAddressStrings() + ); + } + + public function testEscapeCharsInNameAreQuoted() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setNameAddresses(array( + 'chris@swiftmailer.org' => 'Chris Corbyn, \\escaped\\', + )); + $this->assertEquals( + array('"Chris Corbyn, \\\\escaped\\\\" '), + $header->getNameAddressStrings() + ); + } + + public function testGetMailboxesReturnsNameValuePairs() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setNameAddresses(array( + 'chris@swiftmailer.org' => 'Chris Corbyn, DHE', + )); + $this->assertEquals( + array('chris@swiftmailer.org' => 'Chris Corbyn, DHE'), $header->getNameAddresses() + ); + } + + public function testMultipleAddressesCanBeSetAndFetched() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setAddresses(array( + 'chris@swiftmailer.org', 'mark@swiftmailer.org', + )); + $this->assertEquals( + array('chris@swiftmailer.org', 'mark@swiftmailer.org'), + $header->getAddresses() + ); + } + + public function testMultipleAddressesAsMailboxes() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setAddresses(array( + 'chris@swiftmailer.org', 'mark@swiftmailer.org', + )); + $this->assertEquals( + array('chris@swiftmailer.org' => null, 'mark@swiftmailer.org' => null), + $header->getNameAddresses() + ); + } + + public function testMultipleAddressesAsMailboxStrings() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setAddresses(array( + 'chris@swiftmailer.org', 'mark@swiftmailer.org', + )); + $this->assertEquals( + array('chris@swiftmailer.org', 'mark@swiftmailer.org'), + $header->getNameAddressStrings() + ); + } + + public function testMultipleNamedMailboxesReturnsMultipleAddresses() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setNameAddresses(array( + 'chris@swiftmailer.org' => 'Chris Corbyn', + 'mark@swiftmailer.org' => 'Mark Corbyn', + )); + $this->assertEquals( + array('chris@swiftmailer.org', 'mark@swiftmailer.org'), + $header->getAddresses() + ); + } + + public function testMultipleNamedMailboxesReturnsMultipleMailboxes() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setNameAddresses(array( + 'chris@swiftmailer.org' => 'Chris Corbyn', + 'mark@swiftmailer.org' => 'Mark Corbyn', + )); + $this->assertEquals(array( + 'chris@swiftmailer.org' => 'Chris Corbyn', + 'mark@swiftmailer.org' => 'Mark Corbyn', + ), + $header->getNameAddresses() + ); + } + + public function testMultipleMailboxesProducesMultipleMailboxStrings() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setNameAddresses(array( + 'chris@swiftmailer.org' => 'Chris Corbyn', + 'mark@swiftmailer.org' => 'Mark Corbyn', + )); + $this->assertEquals(array( + 'Chris Corbyn ', + 'Mark Corbyn ', + ), + $header->getNameAddressStrings() + ); + } + + public function testSetAddressesOverwritesAnyMailboxes() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setNameAddresses(array( + 'chris@swiftmailer.org' => 'Chris Corbyn', + 'mark@swiftmailer.org' => 'Mark Corbyn', + )); + $this->assertEquals( + array('chris@swiftmailer.org' => 'Chris Corbyn', + 'mark@swiftmailer.org' => 'Mark Corbyn', ), + $header->getNameAddresses() + ); + $this->assertEquals( + array('chris@swiftmailer.org', 'mark@swiftmailer.org'), + $header->getAddresses() + ); + + $header->setAddresses(array('chris@swiftmailer.org', 'mark@swiftmailer.org')); + + $this->assertEquals( + array('chris@swiftmailer.org' => null, 'mark@swiftmailer.org' => null), + $header->getNameAddresses() + ); + $this->assertEquals( + array('chris@swiftmailer.org', 'mark@swiftmailer.org'), + $header->getAddresses() + ); + } + + public function testNameIsEncodedIfNonAscii() + { + $name = 'C'.pack('C', 0x8F).'rbyn'; + + $encoder = $this->_getEncoder('Q'); + $encoder->shouldReceive('encodeString') + ->once() + ->with($name, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn('C=8Frbyn'); + + $header = $this->_getHeader('From', $encoder); + $header->setNameAddresses(array('chris@swiftmailer.org' => 'Chris '.$name)); + + $addresses = $header->getNameAddressStrings(); + $this->assertEquals( + 'Chris =?'.$this->_charset.'?Q?C=8Frbyn?= ', + array_shift($addresses) + ); + } + + public function testEncodingLineLengthCalculations() + { + /* -- RFC 2047, 2. + An 'encoded-word' may not be more than 75 characters long, including + 'charset', 'encoding', 'encoded-text', and delimiters. + */ + + $name = 'C'.pack('C', 0x8F).'rbyn'; + + $encoder = $this->_getEncoder('Q'); + $encoder->shouldReceive('encodeString') + ->once() + ->with($name, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn('C=8Frbyn'); + + $header = $this->_getHeader('From', $encoder); + $header->setNameAddresses(array('chris@swiftmailer.org' => 'Chris '.$name)); + + $header->getNameAddressStrings(); + } + + public function testGetValueReturnsMailboxStringValue() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setNameAddresses(array( + 'chris@swiftmailer.org' => 'Chris Corbyn', + )); + $this->assertEquals( + 'Chris Corbyn ', $header->getFieldBody() + ); + } + + public function testGetValueReturnsMailboxStringValueForMultipleMailboxes() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setNameAddresses(array( + 'chris@swiftmailer.org' => 'Chris Corbyn', + 'mark@swiftmailer.org' => 'Mark Corbyn', + )); + $this->assertEquals( + 'Chris Corbyn , Mark Corbyn ', + $header->getFieldBody() + ); + } + + public function testRemoveAddressesWithSingleValue() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setNameAddresses(array( + 'chris@swiftmailer.org' => 'Chris Corbyn', + 'mark@swiftmailer.org' => 'Mark Corbyn', + )); + $header->removeAddresses('chris@swiftmailer.org'); + $this->assertEquals(array('mark@swiftmailer.org'), + $header->getAddresses() + ); + } + + public function testRemoveAddressesWithList() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setNameAddresses(array( + 'chris@swiftmailer.org' => 'Chris Corbyn', + 'mark@swiftmailer.org' => 'Mark Corbyn', + )); + $header->removeAddresses( + array('chris@swiftmailer.org', 'mark@swiftmailer.org') + ); + $this->assertEquals(array(), $header->getAddresses()); + } + + public function testSetBodyModel() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setFieldBodyModel('chris@swiftmailer.org'); + $this->assertEquals(array('chris@swiftmailer.org' => null), $header->getNameAddresses()); + } + + public function testGetBodyModel() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setAddresses(array('chris@swiftmailer.org')); + $this->assertEquals(array('chris@swiftmailer.org' => null), $header->getFieldBodyModel()); + } + + public function testToString() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setNameAddresses(array( + 'chris@swiftmailer.org' => 'Chris Corbyn', + 'mark@swiftmailer.org' => 'Mark Corbyn', + )); + $this->assertEquals( + 'From: Chris Corbyn , '. + 'Mark Corbyn '."\r\n", + $header->toString() + ); + } + + private function _getHeader($name, $encoder) + { + $header = new Swift_Mime_Headers_MailboxHeader($name, $encoder, new Swift_Mime_Grammar()); + $header->setCharset($this->_charset); + + return $header; + } + + private function _getEncoder($type, $stub = false) + { + $encoder = $this->getMockery('Swift_Mime_HeaderEncoder')->shouldIgnoreMissing(); + $encoder->shouldReceive('getName') + ->zeroOrMoreTimes() + ->andReturn($type); + + return $encoder; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/ParameterizedHeaderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/ParameterizedHeaderTest.php new file mode 100644 index 0000000000..0f3fe14540 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/ParameterizedHeaderTest.php @@ -0,0 +1,400 @@ +_getHeader('Content-Type', + $this->_getHeaderEncoder('Q', true), $this->_getParameterEncoder(true) + ); + $this->assertEquals(Swift_Mime_Header::TYPE_PARAMETERIZED, $header->getFieldType()); + } + + public function testValueIsReturnedVerbatim() + { + $header = $this->_getHeader('Content-Type', + $this->_getHeaderEncoder('Q', true), $this->_getParameterEncoder(true) + ); + $header->setValue('text/plain'); + $this->assertEquals('text/plain', $header->getValue()); + } + + public function testParametersAreAppended() + { + /* -- RFC 2045, 5.1 + parameter := attribute "=" value + + attribute := token + ; Matching of attributes + ; is ALWAYS case-insensitive. + + value := token / quoted-string + + token := 1* + + tspecials := "(" / ")" / "<" / ">" / "@" / + "," / ";" / ":" / "\" / <"> + "/" / "[" / "]" / "?" / "=" + ; Must be in quoted-string, + ; to use within parameter values + */ + + $header = $this->_getHeader('Content-Type', + $this->_getHeaderEncoder('Q', true), $this->_getParameterEncoder(true) + ); + $header->setValue('text/plain'); + $header->setParameters(array('charset' => 'utf-8')); + $this->assertEquals('text/plain; charset=utf-8', $header->getFieldBody()); + } + + public function testSpaceInParamResultsInQuotedString() + { + $header = $this->_getHeader('Content-Disposition', + $this->_getHeaderEncoder('Q', true), $this->_getParameterEncoder(true) + ); + $header->setValue('attachment'); + $header->setParameters(array('filename' => 'my file.txt')); + $this->assertEquals('attachment; filename="my file.txt"', + $header->getFieldBody() + ); + } + + public function testLongParamsAreBrokenIntoMultipleAttributeStrings() + { + /* -- RFC 2231, 3. + The asterisk character ("*") followed + by a decimal count is employed to indicate that multiple parameters + are being used to encapsulate a single parameter value. The count + starts at 0 and increments by 1 for each subsequent section of the + parameter value. Decimal values are used and neither leading zeroes + nor gaps in the sequence are allowed. + + The original parameter value is recovered by concatenating the + various sections of the parameter, in order. For example, the + content-type field + + Content-Type: message/external-body; access-type=URL; + URL*0="ftp://"; + URL*1="cs.utk.edu/pub/moore/bulk-mailer/bulk-mailer.tar" + + is semantically identical to + + Content-Type: message/external-body; access-type=URL; + URL="ftp://cs.utk.edu/pub/moore/bulk-mailer/bulk-mailer.tar" + + Note that quotes around parameter values are part of the value + syntax; they are NOT part of the value itself. Furthermore, it is + explicitly permitted to have a mixture of quoted and unquoted + continuation fields. + */ + + $value = str_repeat('a', 180); + + $encoder = $this->_getParameterEncoder(); + $encoder->shouldReceive('encodeString') + ->once() + ->with($value, \Mockery::any(), 63, \Mockery::any()) + ->andReturn(str_repeat('a', 63)."\r\n". + str_repeat('a', 63)."\r\n".str_repeat('a', 54)); + + $header = $this->_getHeader('Content-Disposition', + $this->_getHeaderEncoder('Q', true), $encoder + ); + $header->setValue('attachment'); + $header->setParameters(array('filename' => $value)); + $header->setMaxLineLength(78); + $this->assertEquals( + 'attachment; '. + 'filename*0*=utf-8\'\''.str_repeat('a', 63).";\r\n ". + 'filename*1*='.str_repeat('a', 63).";\r\n ". + 'filename*2*='.str_repeat('a', 54), + $header->getFieldBody() + ); + } + + public function testEncodedParamDataIncludesCharsetAndLanguage() + { + /* -- RFC 2231, 4. + Asterisks ("*") are reused to provide the indicator that language and + character set information is present and encoding is being used. A + single quote ("'") is used to delimit the character set and language + information at the beginning of the parameter value. Percent signs + ("%") are used as the encoding flag, which agrees with RFC 2047. + + Specifically, an asterisk at the end of a parameter name acts as an + indicator that character set and language information may appear at + the beginning of the parameter value. A single quote is used to + separate the character set, language, and actual value information in + the parameter value string, and an percent sign is used to flag + octets encoded in hexadecimal. For example: + + Content-Type: application/x-stuff; + title*=us-ascii'en-us'This%20is%20%2A%2A%2Afun%2A%2A%2A + + Note that it is perfectly permissible to leave either the character + set or language field blank. Note also that the single quote + delimiters MUST be present even when one of the field values is + omitted. + */ + + $value = str_repeat('a', 20).pack('C', 0x8F).str_repeat('a', 10); + + $encoder = $this->_getParameterEncoder(); + $encoder->shouldReceive('encodeString') + ->once() + ->with($value, 12, 62, \Mockery::any()) + ->andReturn(str_repeat('a', 20).'%8F'.str_repeat('a', 10)); + + $header = $this->_getHeader('Content-Disposition', + $this->_getHeaderEncoder('Q', true), $encoder + ); + $header->setValue('attachment'); + $header->setParameters(array('filename' => $value)); + $header->setMaxLineLength(78); + $header->setLanguage($this->_lang); + $this->assertEquals( + 'attachment; filename*='.$this->_charset."'".$this->_lang."'". + str_repeat('a', 20).'%8F'.str_repeat('a', 10), + $header->getFieldBody() + ); + } + + public function testMultipleEncodedParamLinesAreFormattedCorrectly() + { + /* -- RFC 2231, 4.1. + Character set and language information may be combined with the + parameter continuation mechanism. For example: + + Content-Type: application/x-stuff + title*0*=us-ascii'en'This%20is%20even%20more%20 + title*1*=%2A%2A%2Afun%2A%2A%2A%20 + title*2="isn't it!" + + Note that: + + (1) Language and character set information only appear at + the beginning of a given parameter value. + + (2) Continuations do not provide a facility for using more + than one character set or language in the same + parameter value. + + (3) A value presented using multiple continuations may + contain a mixture of encoded and unencoded segments. + + (4) The first segment of a continuation MUST be encoded if + language and character set information are given. + + (5) If the first segment of a continued parameter value is + encoded the language and character set field delimiters + MUST be present even when the fields are left blank. + */ + + $value = str_repeat('a', 20).pack('C', 0x8F).str_repeat('a', 60); + + $encoder = $this->_getParameterEncoder(); + $encoder->shouldReceive('encodeString') + ->once() + ->with($value, 12, 62, \Mockery::any()) + ->andReturn(str_repeat('a', 20).'%8F'.str_repeat('a', 28)."\r\n". + str_repeat('a', 32)); + + $header = $this->_getHeader('Content-Disposition', + $this->_getHeaderEncoder('Q', true), $encoder + ); + $header->setValue('attachment'); + $header->setParameters(array('filename' => $value)); + $header->setMaxLineLength(78); + $header->setLanguage($this->_lang); + $this->assertEquals( + 'attachment; filename*0*='.$this->_charset."'".$this->_lang."'". + str_repeat('a', 20).'%8F'.str_repeat('a', 28).";\r\n ". + 'filename*1*='.str_repeat('a', 32), + $header->getFieldBody() + ); + } + + public function testToString() + { + $header = $this->_getHeader('Content-Type', + $this->_getHeaderEncoder('Q', true), $this->_getParameterEncoder(true) + ); + $header->setValue('text/html'); + $header->setParameters(array('charset' => 'utf-8')); + $this->assertEquals('Content-Type: text/html; charset=utf-8'."\r\n", + $header->toString() + ); + } + + public function testValueCanBeEncodedIfNonAscii() + { + $value = 'fo'.pack('C', 0x8F).'bar'; + + $encoder = $this->_getHeaderEncoder('Q'); + $encoder->shouldReceive('encodeString') + ->once() + ->with($value, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn('fo=8Fbar'); + + $header = $this->_getHeader('X-Foo', $encoder, $this->_getParameterEncoder(true)); + $header->setValue($value); + $header->setParameters(array('lookslike' => 'foobar')); + $this->assertEquals('X-Foo: =?utf-8?Q?fo=8Fbar?=; lookslike=foobar'."\r\n", + $header->toString() + ); + } + + public function testValueAndParamCanBeEncodedIfNonAscii() + { + $value = 'fo'.pack('C', 0x8F).'bar'; + + $encoder = $this->_getHeaderEncoder('Q'); + $encoder->shouldReceive('encodeString') + ->once() + ->with($value, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn('fo=8Fbar'); + + $paramEncoder = $this->_getParameterEncoder(); + $paramEncoder->shouldReceive('encodeString') + ->once() + ->with($value, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn('fo%8Fbar'); + + $header = $this->_getHeader('X-Foo', $encoder, $paramEncoder); + $header->setValue($value); + $header->setParameters(array('says' => $value)); + $this->assertEquals("X-Foo: =?utf-8?Q?fo=8Fbar?=; says*=utf-8''fo%8Fbar\r\n", + $header->toString() + ); + } + + public function testParamsAreEncodedWithEncodedWordsIfNoParamEncoderSet() + { + $value = 'fo'.pack('C', 0x8F).'bar'; + + $encoder = $this->_getHeaderEncoder('Q'); + $encoder->shouldReceive('encodeString') + ->once() + ->with($value, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn('fo=8Fbar'); + + $header = $this->_getHeader('X-Foo', $encoder, null); + $header->setValue('bar'); + $header->setParameters(array('says' => $value)); + $this->assertEquals("X-Foo: bar; says=\"=?utf-8?Q?fo=8Fbar?=\"\r\n", + $header->toString() + ); + } + + public function testLanguageInformationAppearsInEncodedWords() + { + /* -- RFC 2231, 5. + 5. Language specification in Encoded Words + + RFC 2047 provides support for non-US-ASCII character sets in RFC 822 + message header comments, phrases, and any unstructured text field. + This is done by defining an encoded word construct which can appear + in any of these places. Given that these are fields intended for + display, it is sometimes necessary to associate language information + with encoded words as well as just the character set. This + specification extends the definition of an encoded word to allow the + inclusion of such information. This is simply done by suffixing the + character set specification with an asterisk followed by the language + tag. For example: + + From: =?US-ASCII*EN?Q?Keith_Moore?= + */ + + $value = 'fo'.pack('C', 0x8F).'bar'; + + $encoder = $this->_getHeaderEncoder('Q'); + $encoder->shouldReceive('encodeString') + ->once() + ->with($value, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn('fo=8Fbar'); + + $paramEncoder = $this->_getParameterEncoder(); + $paramEncoder->shouldReceive('encodeString') + ->once() + ->with($value, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn('fo%8Fbar'); + + $header = $this->_getHeader('X-Foo', $encoder, $paramEncoder); + $header->setLanguage('en'); + $header->setValue($value); + $header->setParameters(array('says' => $value)); + $this->assertEquals("X-Foo: =?utf-8*en?Q?fo=8Fbar?=; says*=utf-8'en'fo%8Fbar\r\n", + $header->toString() + ); + } + + public function testSetBodyModel() + { + $header = $this->_getHeader('Content-Type', + $this->_getHeaderEncoder('Q', true), $this->_getParameterEncoder(true) + ); + $header->setFieldBodyModel('text/html'); + $this->assertEquals('text/html', $header->getValue()); + } + + public function testGetBodyModel() + { + $header = $this->_getHeader('Content-Type', + $this->_getHeaderEncoder('Q', true), $this->_getParameterEncoder(true) + ); + $header->setValue('text/plain'); + $this->assertEquals('text/plain', $header->getFieldBodyModel()); + } + + public function testSetParameter() + { + $header = $this->_getHeader('Content-Type', + $this->_getHeaderEncoder('Q', true), $this->_getParameterEncoder(true) + ); + $header->setParameters(array('charset' => 'utf-8', 'delsp' => 'yes')); + $header->setParameter('delsp', 'no'); + $this->assertEquals(array('charset' => 'utf-8', 'delsp' => 'no'), + $header->getParameters() + ); + } + + public function testGetParameter() + { + $header = $this->_getHeader('Content-Type', + $this->_getHeaderEncoder('Q', true), $this->_getParameterEncoder(true) + ); + $header->setParameters(array('charset' => 'utf-8', 'delsp' => 'yes')); + $this->assertEquals('utf-8', $header->getParameter('charset')); + } + + // -- Private helper + + private function _getHeader($name, $encoder, $paramEncoder) + { + $header = new Swift_Mime_Headers_ParameterizedHeader($name, $encoder, + $paramEncoder, new Swift_Mime_Grammar() + ); + $header->setCharset($this->_charset); + + return $header; + } + + private function _getHeaderEncoder($type, $stub = false) + { + $encoder = $this->getMockery('Swift_Mime_HeaderEncoder')->shouldIgnoreMissing(); + $encoder->shouldReceive('getName') + ->zeroOrMoreTimes() + ->andReturn($type); + + return $encoder; + } + + private function _getParameterEncoder($stub = false) + { + return $this->getMockery('Swift_Encoder')->shouldIgnoreMissing(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/PathHeaderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/PathHeaderTest.php new file mode 100644 index 0000000000..a9f35e9de7 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/PathHeaderTest.php @@ -0,0 +1,77 @@ +_getHeader('Return-Path'); + $this->assertEquals(Swift_Mime_Header::TYPE_PATH, $header->getFieldType()); + } + + public function testSingleAddressCanBeSetAndFetched() + { + $header = $this->_getHeader('Return-Path'); + $header->setAddress('chris@swiftmailer.org'); + $this->assertEquals('chris@swiftmailer.org', $header->getAddress()); + } + + public function testAddressMustComplyWithRfc2822() + { + try { + $header = $this->_getHeader('Return-Path'); + $header->setAddress('chr is@swiftmailer.org'); + $this->fail('Addresses not valid according to RFC 2822 addr-spec grammar must be rejected.'); + } catch (Exception $e) { + } + } + + public function testValueIsAngleAddrWithValidAddress() + { + /* -- RFC 2822, 3.6.7. + + return = "Return-Path:" path CRLF + + path = ([CFWS] "<" ([CFWS] / addr-spec) ">" [CFWS]) / + obs-path + */ + + $header = $this->_getHeader('Return-Path'); + $header->setAddress('chris@swiftmailer.org'); + $this->assertEquals('', $header->getFieldBody()); + } + + public function testValueIsEmptyAngleBracketsIfEmptyAddressSet() + { + $header = $this->_getHeader('Return-Path'); + $header->setAddress(''); + $this->assertEquals('<>', $header->getFieldBody()); + } + + public function testSetBodyModel() + { + $header = $this->_getHeader('Return-Path'); + $header->setFieldBodyModel('foo@bar.tld'); + $this->assertEquals('foo@bar.tld', $header->getAddress()); + } + + public function testGetBodyModel() + { + $header = $this->_getHeader('Return-Path'); + $header->setAddress('foo@bar.tld'); + $this->assertEquals('foo@bar.tld', $header->getFieldBodyModel()); + } + + public function testToString() + { + $header = $this->_getHeader('Return-Path'); + $header->setAddress('chris@swiftmailer.org'); + $this->assertEquals('Return-Path: '."\r\n", + $header->toString() + ); + } + + private function _getHeader($name) + { + return new Swift_Mime_Headers_PathHeader($name, new Swift_Mime_Grammar()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/UnstructuredHeaderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/UnstructuredHeaderTest.php new file mode 100644 index 0000000000..2e1dc8cabc --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/UnstructuredHeaderTest.php @@ -0,0 +1,355 @@ +_getHeader('Subject', $this->_getEncoder('Q', true)); + $this->assertEquals(Swift_Mime_Header::TYPE_TEXT, $header->getFieldType()); + } + + public function testGetNameReturnsNameVerbatim() + { + $header = $this->_getHeader('Subject', $this->_getEncoder('Q', true)); + $this->assertEquals('Subject', $header->getFieldName()); + } + + public function testGetValueReturnsValueVerbatim() + { + $header = $this->_getHeader('Subject', $this->_getEncoder('Q', true)); + $header->setValue('Test'); + $this->assertEquals('Test', $header->getValue()); + } + + public function testBasicStructureIsKeyValuePair() + { + /* -- RFC 2822, 2.2 + Header fields are lines composed of a field name, followed by a colon + (":"), followed by a field body, and terminated by CRLF. + */ + $header = $this->_getHeader('Subject', $this->_getEncoder('Q', true)); + $header->setValue('Test'); + $this->assertEquals('Subject: Test'."\r\n", $header->toString()); + } + + public function testLongHeadersAreFoldedAtWordBoundary() + { + /* -- RFC 2822, 2.2.3 + Each header field is logically a single line of characters comprising + the field name, the colon, and the field body. For convenience + however, and to deal with the 998/78 character limitations per line, + the field body portion of a header field can be split into a multiple + line representation; this is called "folding". The general rule is + that wherever this standard allows for folding white space (not + simply WSP characters), a CRLF may be inserted before any WSP. + */ + + $value = 'The quick brown fox jumped over the fence, he was a very very '. + 'scary brown fox with a bushy tail'; + $header = $this->_getHeader('X-Custom-Header', + $this->_getEncoder('Q', true) + ); + $header->setValue($value); + $header->setMaxLineLength(78); //A safe [RFC 2822, 2.2.3] default + /* + X-Custom-Header: The quick brown fox jumped over the fence, he was a very very + scary brown fox with a bushy tail + */ + $this->assertEquals( + 'X-Custom-Header: The quick brown fox jumped over the fence, he was a'. + ' very very'."\r\n".//Folding + ' scary brown fox with a bushy tail'."\r\n", + $header->toString(), '%s: The header should have been folded at 78th char' + ); + } + + public function testPrintableAsciiOnlyAppearsInHeaders() + { + /* -- RFC 2822, 2.2. + A field name MUST be composed of printable US-ASCII characters (i.e., + characters that have values between 33 and 126, inclusive), except + colon. A field body may be composed of any US-ASCII characters, + except for CR and LF. + */ + + $nonAsciiChar = pack('C', 0x8F); + $header = $this->_getHeader('X-Test', $this->_getEncoder('Q', true)); + $header->setValue($nonAsciiChar); + $this->assertRegExp( + '~^[^:\x00-\x20\x80-\xFF]+: [^\x80-\xFF\r\n]+\r\n$~s', + $header->toString() + ); + } + + public function testEncodedWordsFollowGeneralStructure() + { + /* -- RFC 2047, 1. + Generally, an "encoded-word" is a sequence of printable ASCII + characters that begins with "=?", ends with "?=", and has two "?"s in + between. + */ + + $nonAsciiChar = pack('C', 0x8F); + $header = $this->_getHeader('X-Test', $this->_getEncoder('Q', true)); + $header->setValue($nonAsciiChar); + $this->assertRegExp( + '~^X-Test: \=?.*?\?.*?\?.*?\?=\r\n$~s', + $header->toString() + ); + } + + public function testEncodedWordIncludesCharsetAndEncodingMethodAndText() + { + /* -- RFC 2047, 2. + An 'encoded-word' is defined by the following ABNF grammar. The + notation of RFC 822 is used, with the exception that white space + characters MUST NOT appear between components of an 'encoded-word'. + + encoded-word = "=?" charset "?" encoding "?" encoded-text "?=" + */ + + $nonAsciiChar = pack('C', 0x8F); + + $encoder = $this->_getEncoder('Q'); + $encoder->shouldReceive('encodeString') + ->once() + ->with($nonAsciiChar, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn('=8F'); + + $header = $this->_getHeader('X-Test', $encoder); + $header->setValue($nonAsciiChar); + $this->assertEquals( + 'X-Test: =?'.$this->_charset.'?Q?=8F?='."\r\n", + $header->toString() + ); + } + + public function testEncodedWordsAreUsedToEncodedNonPrintableAscii() + { + //SPACE and TAB permitted + $nonPrintableBytes = array_merge( + range(0x00, 0x08), range(0x10, 0x19), array(0x7F) + ); + + foreach ($nonPrintableBytes as $byte) { + $char = pack('C', $byte); + $encodedChar = sprintf('=%02X', $byte); + + $encoder = $this->_getEncoder('Q'); + $encoder->shouldReceive('encodeString') + ->once() + ->with($char, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn($encodedChar); + + $header = $this->_getHeader('X-A', $encoder); + $header->setValue($char); + + $this->assertEquals( + 'X-A: =?'.$this->_charset.'?Q?'.$encodedChar.'?='."\r\n", + $header->toString(), '%s: Non-printable ascii should be encoded' + ); + } + } + + public function testEncodedWordsAreUsedToEncode8BitOctets() + { + $_8BitBytes = range(0x80, 0xFF); + + foreach ($_8BitBytes as $byte) { + $char = pack('C', $byte); + $encodedChar = sprintf('=%02X', $byte); + + $encoder = $this->_getEncoder('Q'); + $encoder->shouldReceive('encodeString') + ->once() + ->with($char, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn($encodedChar); + + $header = $this->_getHeader('X-A', $encoder); + $header->setValue($char); + + $this->assertEquals( + 'X-A: =?'.$this->_charset.'?Q?'.$encodedChar.'?='."\r\n", + $header->toString(), '%s: 8-bit octets should be encoded' + ); + } + } + + public function testEncodedWordsAreNoMoreThan75CharsPerLine() + { + /* -- RFC 2047, 2. + An 'encoded-word' may not be more than 75 characters long, including + 'charset', 'encoding', 'encoded-text', and delimiters. + + ... SNIP ... + + While there is no limit to the length of a multiple-line header + field, each line of a header field that contains one or more + 'encoded-word's is limited to 76 characters. + */ + + $nonAsciiChar = pack('C', 0x8F); + + $encoder = $this->_getEncoder('Q'); + $encoder->shouldReceive('encodeString') + ->once() + ->with($nonAsciiChar, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn('=8F'); + //Note that multi-line headers begin with LWSP which makes 75 + 1 = 76 + //Note also that =?utf-8?q??= is 12 chars which makes 75 - 12 = 63 + + //* X-Test: is 8 chars + $header = $this->_getHeader('X-Test', $encoder); + $header->setValue($nonAsciiChar); + + $this->assertEquals( + 'X-Test: =?'.$this->_charset.'?Q?=8F?='."\r\n", + $header->toString() + ); + } + + public function testFWSPIsUsedWhenEncoderReturnsMultipleLines() + { + /* --RFC 2047, 2. + If it is desirable to encode more text than will fit in an 'encoded-word' of + 75 characters, multiple 'encoded-word's (separated by CRLF SPACE) may + be used. + */ + + //Note the Mock does NOT return 8F encoded, the 8F merely triggers + // encoding for the sake of testing + $nonAsciiChar = pack('C', 0x8F); + + $encoder = $this->_getEncoder('Q'); + $encoder->shouldReceive('encodeString') + ->once() + ->with($nonAsciiChar, 8, 63, \Mockery::any()) + ->andReturn('line_one_here'."\r\n".'line_two_here'); + + //Note that multi-line headers begin with LWSP which makes 75 + 1 = 76 + //Note also that =?utf-8?q??= is 12 chars which makes 75 - 12 = 63 + + //* X-Test: is 8 chars + $header = $this->_getHeader('X-Test', $encoder); + $header->setValue($nonAsciiChar); + + $this->assertEquals( + 'X-Test: =?'.$this->_charset.'?Q?line_one_here?='."\r\n". + ' =?'.$this->_charset.'?Q?line_two_here?='."\r\n", + $header->toString() + ); + } + + public function testAdjacentWordsAreEncodedTogether() + { + /* -- RFC 2047, 5 (1) + Ordinary ASCII text and 'encoded-word's may appear together in the + same header field. However, an 'encoded-word' that appears in a + header field defined as '*text' MUST be separated from any adjacent + 'encoded-word' or 'text' by 'linear-white-space'. + + -- RFC 2047, 2. + IMPORTANT: 'encoded-word's are designed to be recognized as 'atom's + by an RFC 822 parser. As a consequence, unencoded white space + characters (such as SPACE and HTAB) are FORBIDDEN within an + 'encoded-word'. + */ + + //It would be valid to encode all words needed, however it's probably + // easiest to encode the longest amount required at a time + + $word = 'w'.pack('C', 0x8F).'rd'; + $text = 'start '.$word.' '.$word.' then end '.$word; + // 'start', ' word word', ' and end', ' word' + + $encoder = $this->_getEncoder('Q'); + $encoder->shouldReceive('encodeString') + ->once() + ->with($word.' '.$word, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn('w=8Frd_w=8Frd'); + $encoder->shouldReceive('encodeString') + ->once() + ->with($word, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn('w=8Frd'); + + $header = $this->_getHeader('X-Test', $encoder); + $header->setValue($text); + + $headerString = $header->toString(); + + $this->assertEquals('X-Test: start =?'.$this->_charset.'?Q?'. + 'w=8Frd_w=8Frd?= then end =?'.$this->_charset.'?Q?'. + 'w=8Frd?='."\r\n", $headerString, + '%s: Adjacent encoded words should appear grouped with WSP encoded' + ); + } + + public function testLanguageInformationAppearsInEncodedWords() + { + /* -- RFC 2231, 5. + 5. Language specification in Encoded Words + + RFC 2047 provides support for non-US-ASCII character sets in RFC 822 + message header comments, phrases, and any unstructured text field. + This is done by defining an encoded word construct which can appear + in any of these places. Given that these are fields intended for + display, it is sometimes necessary to associate language information + with encoded words as well as just the character set. This + specification extends the definition of an encoded word to allow the + inclusion of such information. This is simply done by suffixing the + character set specification with an asterisk followed by the language + tag. For example: + + From: =?US-ASCII*EN?Q?Keith_Moore?= + */ + + $value = 'fo'.pack('C', 0x8F).'bar'; + + $encoder = $this->_getEncoder('Q'); + $encoder->shouldReceive('encodeString') + ->once() + ->with($value, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn('fo=8Fbar'); + + $header = $this->_getHeader('Subject', $encoder); + $header->setLanguage('en'); + $header->setValue($value); + $this->assertEquals("Subject: =?utf-8*en?Q?fo=8Fbar?=\r\n", + $header->toString() + ); + } + + public function testSetBodyModel() + { + $header = $this->_getHeader('Subject', $this->_getEncoder('Q', true)); + $header->setFieldBodyModel('test'); + $this->assertEquals('test', $header->getValue()); + } + + public function testGetBodyModel() + { + $header = $this->_getHeader('Subject', $this->_getEncoder('Q', true)); + $header->setValue('test'); + $this->assertEquals('test', $header->getFieldBodyModel()); + } + + private function _getHeader($name, $encoder) + { + $header = new Swift_Mime_Headers_UnstructuredHeader($name, $encoder, new Swift_Mime_Grammar()); + $header->setCharset($this->_charset); + + return $header; + } + + private function _getEncoder($type, $stub = false) + { + $encoder = $this->getMockery('Swift_Mime_HeaderEncoder')->shouldIgnoreMissing(); + $encoder->shouldReceive('getName') + ->zeroOrMoreTimes() + ->andReturn($type); + + return $encoder; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/MimePartTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/MimePartTest.php new file mode 100644 index 0000000000..7e91134f3a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/MimePartTest.php @@ -0,0 +1,233 @@ +_createMimePart($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals( + Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE, $part->getNestingLevel() + ); + } + + public function testCharsetIsReturnedFromHeader() + { + /* -- RFC 2046, 4.1.2. + A critical parameter that may be specified in the Content-Type field + for "text/plain" data is the character set. This is specified with a + "charset" parameter, as in: + + Content-type: text/plain; charset=iso-8859-1 + + Unlike some other parameter values, the values of the charset + parameter are NOT case sensitive. The default character set, which + must be assumed in the absence of a charset parameter, is US-ASCII. + */ + + $cType = $this->_createHeader('Content-Type', 'text/plain', + array('charset' => 'iso-8859-1') + ); + $part = $this->_createMimePart($this->_createHeaderSet(array( + 'Content-Type' => $cType, )), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals('iso-8859-1', $part->getCharset()); + } + + public function testCharsetIsSetInHeader() + { + $cType = $this->_createHeader('Content-Type', 'text/plain', + array('charset' => 'iso-8859-1'), false + ); + $cType->shouldReceive('setParameter')->once()->with('charset', 'utf-8'); + + $part = $this->_createMimePart($this->_createHeaderSet(array( + 'Content-Type' => $cType, )), + $this->_createEncoder(), $this->_createCache() + ); + $part->setCharset('utf-8'); + } + + public function testCharsetIsSetInHeaderIfPassedToSetBody() + { + $cType = $this->_createHeader('Content-Type', 'text/plain', + array('charset' => 'iso-8859-1'), false + ); + $cType->shouldReceive('setParameter')->once()->with('charset', 'utf-8'); + + $part = $this->_createMimePart($this->_createHeaderSet(array( + 'Content-Type' => $cType, )), + $this->_createEncoder(), $this->_createCache() + ); + $part->setBody('', 'text/plian', 'utf-8'); + } + + public function testSettingCharsetNotifiesEncoder() + { + $encoder = $this->_createEncoder('quoted-printable', false); + $encoder->expects($this->once()) + ->method('charsetChanged') + ->with('utf-8'); + + $part = $this->_createMimePart($this->_createHeaderSet(), + $encoder, $this->_createCache() + ); + $part->setCharset('utf-8'); + } + + public function testSettingCharsetNotifiesHeaders() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('charsetChanged') + ->zeroOrMoreTimes() + ->with('utf-8'); + + $part = $this->_createMimePart($headers, $this->_createEncoder(), + $this->_createCache() + ); + $part->setCharset('utf-8'); + } + + public function testSettingCharsetNotifiesChildren() + { + $child = $this->_createChild(0, '', false); + $child->shouldReceive('charsetChanged') + ->once() + ->with('windows-874'); + + $part = $this->_createMimePart($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $part->setChildren(array($child)); + $part->setCharset('windows-874'); + } + + public function testCharsetChangeUpdatesCharset() + { + $cType = $this->_createHeader('Content-Type', 'text/plain', + array('charset' => 'iso-8859-1'), false + ); + $cType->shouldReceive('setParameter')->once()->with('charset', 'utf-8'); + + $part = $this->_createMimePart($this->_createHeaderSet(array( + 'Content-Type' => $cType, )), + $this->_createEncoder(), $this->_createCache() + ); + $part->charsetChanged('utf-8'); + } + + public function testSettingCharsetClearsCache() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('toString') + ->zeroOrMoreTimes() + ->andReturn("Content-Type: text/plain; charset=utf-8\r\n"); + + $cache = $this->_createCache(false); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $cache + ); + + $entity->setBody("blah\r\nblah!"); + $entity->toString(); + + // Initialize the expectation here because we only care about what happens in setCharset() + $cache->shouldReceive('clearKey') + ->once() + ->with(\Mockery::any(), 'body'); + + $entity->setCharset('iso-2022'); + } + + public function testFormatIsReturnedFromHeader() + { + /* -- RFC 3676. + */ + + $cType = $this->_createHeader('Content-Type', 'text/plain', + array('format' => 'flowed') + ); + $part = $this->_createMimePart($this->_createHeaderSet(array( + 'Content-Type' => $cType, )), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals('flowed', $part->getFormat()); + } + + public function testFormatIsSetInHeader() + { + $cType = $this->_createHeader('Content-Type', 'text/plain', array(), false); + $cType->shouldReceive('setParameter')->once()->with('format', 'fixed'); + + $part = $this->_createMimePart($this->_createHeaderSet(array( + 'Content-Type' => $cType, )), + $this->_createEncoder(), $this->_createCache() + ); + $part->setFormat('fixed'); + } + + public function testDelSpIsReturnedFromHeader() + { + /* -- RFC 3676. + */ + + $cType = $this->_createHeader('Content-Type', 'text/plain', + array('delsp' => 'no') + ); + $part = $this->_createMimePart($this->_createHeaderSet(array( + 'Content-Type' => $cType, )), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertFalse($part->getDelSp()); + } + + public function testDelSpIsSetInHeader() + { + $cType = $this->_createHeader('Content-Type', 'text/plain', array(), false); + $cType->shouldReceive('setParameter')->once()->with('delsp', 'yes'); + + $part = $this->_createMimePart($this->_createHeaderSet(array( + 'Content-Type' => $cType, )), + $this->_createEncoder(), $this->_createCache() + ); + $part->setDelSp(true); + } + + public function testFluidInterface() + { + $part = $this->_createMimePart($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + + $this->assertSame($part, + $part + ->setContentType('text/plain') + ->setEncoder($this->_createEncoder()) + ->setId('foo@bar') + ->setDescription('my description') + ->setMaxLineLength(998) + ->setBody('xx') + ->setBoundary('xyz') + ->setChildren(array()) + ->setCharset('utf-8') + ->setFormat('flowed') + ->setDelSp(true) + ); + } + + // -- Private helpers + + //abstract + protected function _createEntity($headers, $encoder, $cache) + { + return $this->_createMimePart($headers, $encoder, $cache); + } + + protected function _createMimePart($headers, $encoder, $cache) + { + return new Swift_Mime_MimePart($headers, $encoder, $cache, new Swift_Mime_Grammar()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleHeaderFactoryTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleHeaderFactoryTest.php new file mode 100644 index 0000000000..4ee62963ee --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleHeaderFactoryTest.php @@ -0,0 +1,168 @@ +_factory = $this->_createFactory(); + } + + public function testMailboxHeaderIsCorrectType() + { + $header = $this->_factory->createMailboxHeader('X-Foo'); + $this->assertInstanceof('Swift_Mime_Headers_MailboxHeader', $header); + } + + public function testMailboxHeaderHasCorrectName() + { + $header = $this->_factory->createMailboxHeader('X-Foo'); + $this->assertEquals('X-Foo', $header->getFieldName()); + } + + public function testMailboxHeaderHasCorrectModel() + { + $header = $this->_factory->createMailboxHeader('X-Foo', + array('foo@bar' => 'FooBar') + ); + $this->assertEquals(array('foo@bar' => 'FooBar'), $header->getFieldBodyModel()); + } + + public function testDateHeaderHasCorrectType() + { + $header = $this->_factory->createDateHeader('X-Date'); + $this->assertInstanceof('Swift_Mime_Headers_DateHeader', $header); + } + + public function testDateHeaderHasCorrectName() + { + $header = $this->_factory->createDateHeader('X-Date'); + $this->assertEquals('X-Date', $header->getFieldName()); + } + + public function testDateHeaderHasCorrectModel() + { + $header = $this->_factory->createDateHeader('X-Date', 123); + $this->assertEquals(123, $header->getFieldBodyModel()); + } + + public function testTextHeaderHasCorrectType() + { + $header = $this->_factory->createTextHeader('X-Foo'); + $this->assertInstanceof('Swift_Mime_Headers_UnstructuredHeader', $header); + } + + public function testTextHeaderHasCorrectName() + { + $header = $this->_factory->createTextHeader('X-Foo'); + $this->assertEquals('X-Foo', $header->getFieldName()); + } + + public function testTextHeaderHasCorrectModel() + { + $header = $this->_factory->createTextHeader('X-Foo', 'bar'); + $this->assertEquals('bar', $header->getFieldBodyModel()); + } + + public function testParameterizedHeaderHasCorrectType() + { + $header = $this->_factory->createParameterizedHeader('X-Foo'); + $this->assertInstanceof('Swift_Mime_Headers_ParameterizedHeader', $header); + } + + public function testParameterizedHeaderHasCorrectName() + { + $header = $this->_factory->createParameterizedHeader('X-Foo'); + $this->assertEquals('X-Foo', $header->getFieldName()); + } + + public function testParameterizedHeaderHasCorrectModel() + { + $header = $this->_factory->createParameterizedHeader('X-Foo', 'bar'); + $this->assertEquals('bar', $header->getFieldBodyModel()); + } + + public function testParameterizedHeaderHasCorrectParams() + { + $header = $this->_factory->createParameterizedHeader('X-Foo', 'bar', + array('zip' => 'button') + ); + $this->assertEquals(array('zip' => 'button'), $header->getParameters()); + } + + public function testIdHeaderHasCorrectType() + { + $header = $this->_factory->createIdHeader('X-ID'); + $this->assertInstanceof('Swift_Mime_Headers_IdentificationHeader', $header); + } + + public function testIdHeaderHasCorrectName() + { + $header = $this->_factory->createIdHeader('X-ID'); + $this->assertEquals('X-ID', $header->getFieldName()); + } + + public function testIdHeaderHasCorrectModel() + { + $header = $this->_factory->createIdHeader('X-ID', 'xyz@abc'); + $this->assertEquals(array('xyz@abc'), $header->getFieldBodyModel()); + } + + public function testPathHeaderHasCorrectType() + { + $header = $this->_factory->createPathHeader('X-Path'); + $this->assertInstanceof('Swift_Mime_Headers_PathHeader', $header); + } + + public function testPathHeaderHasCorrectName() + { + $header = $this->_factory->createPathHeader('X-Path'); + $this->assertEquals('X-Path', $header->getFieldName()); + } + + public function testPathHeaderHasCorrectModel() + { + $header = $this->_factory->createPathHeader('X-Path', 'foo@bar'); + $this->assertEquals('foo@bar', $header->getFieldBodyModel()); + } + + public function testCharsetChangeNotificationNotifiesEncoders() + { + $encoder = $this->_createHeaderEncoder(); + $encoder->expects($this->once()) + ->method('charsetChanged') + ->with('utf-8'); + $paramEncoder = $this->_createParamEncoder(); + $paramEncoder->expects($this->once()) + ->method('charsetChanged') + ->with('utf-8'); + + $factory = $this->_createFactory($encoder, $paramEncoder); + + $factory->charsetChanged('utf-8'); + } + + // -- Creation methods + + private function _createFactory($encoder = null, $paramEncoder = null) + { + return new Swift_Mime_SimpleHeaderFactory( + $encoder + ? $encoder : $this->_createHeaderEncoder(), + $paramEncoder + ? $paramEncoder : $this->_createParamEncoder(), + new Swift_Mime_Grammar() + ); + } + + private function _createHeaderEncoder() + { + return $this->getMockBuilder('Swift_Mime_HeaderEncoder')->getMock(); + } + + private function _createParamEncoder() + { + return $this->getMockBuilder('Swift_Encoder')->getMock(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleHeaderSetTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleHeaderSetTest.php new file mode 100644 index 0000000000..9682f37ee6 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleHeaderSetTest.php @@ -0,0 +1,739 @@ +_createFactory(); + $factory->expects($this->once()) + ->method('createMailboxHeader') + ->with('From', array('person@domain' => 'Person')) + ->will($this->returnValue($this->_createHeader('From'))); + + $set = $this->_createSet($factory); + $set->addMailboxHeader('From', array('person@domain' => 'Person')); + } + + public function testAddDateHeaderDelegatesToFactory() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createDateHeader') + ->with('Date', 1234) + ->will($this->returnValue($this->_createHeader('Date'))); + + $set = $this->_createSet($factory); + $set->addDateHeader('Date', 1234); + } + + public function testAddTextHeaderDelegatesToFactory() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createTextHeader') + ->with('Subject', 'some text') + ->will($this->returnValue($this->_createHeader('Subject'))); + + $set = $this->_createSet($factory); + $set->addTextHeader('Subject', 'some text'); + } + + public function testAddParameterizedHeaderDelegatesToFactory() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createParameterizedHeader') + ->with('Content-Type', 'text/plain', array('charset' => 'utf-8')) + ->will($this->returnValue($this->_createHeader('Content-Type'))); + + $set = $this->_createSet($factory); + $set->addParameterizedHeader('Content-Type', 'text/plain', + array('charset' => 'utf-8') + ); + } + + public function testAddIdHeaderDelegatesToFactory() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($this->_createHeader('Message-ID'))); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + } + + public function testAddPathHeaderDelegatesToFactory() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createPathHeader') + ->with('Return-Path', 'some@path') + ->will($this->returnValue($this->_createHeader('Return-Path'))); + + $set = $this->_createSet($factory); + $set->addPathHeader('Return-Path', 'some@path'); + } + + public function testHasReturnsFalseWhenNoHeaders() + { + $set = $this->_createSet($this->_createFactory()); + $this->assertFalse($set->has('Some-Header')); + } + + public function testAddedMailboxHeaderIsSeenByHas() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createMailboxHeader') + ->with('From', array('person@domain' => 'Person')) + ->will($this->returnValue($this->_createHeader('From'))); + + $set = $this->_createSet($factory); + $set->addMailboxHeader('From', array('person@domain' => 'Person')); + $this->assertTrue($set->has('From')); + } + + public function testAddedDateHeaderIsSeenByHas() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createDateHeader') + ->with('Date', 1234) + ->will($this->returnValue($this->_createHeader('Date'))); + + $set = $this->_createSet($factory); + $set->addDateHeader('Date', 1234); + $this->assertTrue($set->has('Date')); + } + + public function testAddedTextHeaderIsSeenByHas() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createTextHeader') + ->with('Subject', 'some text') + ->will($this->returnValue($this->_createHeader('Subject'))); + + $set = $this->_createSet($factory); + $set->addTextHeader('Subject', 'some text'); + $this->assertTrue($set->has('Subject')); + } + + public function testAddedParameterizedHeaderIsSeenByHas() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createParameterizedHeader') + ->with('Content-Type', 'text/plain', array('charset' => 'utf-8')) + ->will($this->returnValue($this->_createHeader('Content-Type'))); + + $set = $this->_createSet($factory); + $set->addParameterizedHeader('Content-Type', 'text/plain', + array('charset' => 'utf-8') + ); + $this->assertTrue($set->has('Content-Type')); + } + + public function testAddedIdHeaderIsSeenByHas() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($this->_createHeader('Message-ID'))); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $this->assertTrue($set->has('Message-ID')); + } + + public function testAddedPathHeaderIsSeenByHas() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createPathHeader') + ->with('Return-Path', 'some@path') + ->will($this->returnValue($this->_createHeader('Return-Path'))); + + $set = $this->_createSet($factory); + $set->addPathHeader('Return-Path', 'some@path'); + $this->assertTrue($set->has('Return-Path')); + } + + public function testNewlySetHeaderIsSeenByHas() + { + $factory = $this->_createFactory(); + $header = $this->_createHeader('X-Foo', 'bar'); + $set = $this->_createSet($factory); + $set->set($header); + $this->assertTrue($set->has('X-Foo')); + } + + public function testHasCanAcceptOffset() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($this->_createHeader('Message-ID'))); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $this->assertTrue($set->has('Message-ID', 0)); + } + + public function testHasWithIllegalOffsetReturnsFalse() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($this->_createHeader('Message-ID'))); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $this->assertFalse($set->has('Message-ID', 1)); + } + + public function testHasCanDistinguishMultipleHeaders() + { + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($this->_createHeader('Message-ID'))); + $factory->expects($this->at(1)) + ->method('createIdHeader') + ->with('Message-ID', 'other@id') + ->will($this->returnValue($this->_createHeader('Message-ID'))); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $set->addIdHeader('Message-ID', 'other@id'); + $this->assertTrue($set->has('Message-ID', 1)); + } + + public function testGetWithUnspecifiedOffset() + { + $header = $this->_createHeader('Message-ID'); + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($header)); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $this->assertSame($header, $set->get('Message-ID')); + } + + public function testGetWithSpeiciedOffset() + { + $header0 = $this->_createHeader('Message-ID'); + $header1 = $this->_createHeader('Message-ID'); + $header2 = $this->_createHeader('Message-ID'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($header0)); + $factory->expects($this->at(1)) + ->method('createIdHeader') + ->with('Message-ID', 'other@id') + ->will($this->returnValue($header1)); + $factory->expects($this->at(2)) + ->method('createIdHeader') + ->with('Message-ID', 'more@id') + ->will($this->returnValue($header2)); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $set->addIdHeader('Message-ID', 'other@id'); + $set->addIdHeader('Message-ID', 'more@id'); + $this->assertSame($header1, $set->get('Message-ID', 1)); + } + + public function testGetReturnsNullIfHeaderNotSet() + { + $set = $this->_createSet($this->_createFactory()); + $this->assertNull($set->get('Message-ID', 99)); + } + + public function testGetAllReturnsAllHeadersMatchingName() + { + $header0 = $this->_createHeader('Message-ID'); + $header1 = $this->_createHeader('Message-ID'); + $header2 = $this->_createHeader('Message-ID'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($header0)); + $factory->expects($this->at(1)) + ->method('createIdHeader') + ->with('Message-ID', 'other@id') + ->will($this->returnValue($header1)); + $factory->expects($this->at(2)) + ->method('createIdHeader') + ->with('Message-ID', 'more@id') + ->will($this->returnValue($header2)); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $set->addIdHeader('Message-ID', 'other@id'); + $set->addIdHeader('Message-ID', 'more@id'); + + $this->assertEquals(array($header0, $header1, $header2), + $set->getAll('Message-ID') + ); + } + + public function testGetAllReturnsAllHeadersIfNoArguments() + { + $header0 = $this->_createHeader('Message-ID'); + $header1 = $this->_createHeader('Subject'); + $header2 = $this->_createHeader('To'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($header0)); + $factory->expects($this->at(1)) + ->method('createIdHeader') + ->with('Subject', 'thing') + ->will($this->returnValue($header1)); + $factory->expects($this->at(2)) + ->method('createIdHeader') + ->with('To', 'person@example.org') + ->will($this->returnValue($header2)); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $set->addIdHeader('Subject', 'thing'); + $set->addIdHeader('To', 'person@example.org'); + + $this->assertEquals(array($header0, $header1, $header2), + $set->getAll() + ); + } + + public function testGetAllReturnsEmptyArrayIfNoneSet() + { + $set = $this->_createSet($this->_createFactory()); + $this->assertEquals(array(), $set->getAll('Received')); + } + + public function testRemoveWithUnspecifiedOffset() + { + $header = $this->_createHeader('Message-ID'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($header)); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $set->remove('Message-ID'); + $this->assertFalse($set->has('Message-ID')); + } + + public function testRemoveWithSpecifiedIndexRemovesHeader() + { + $header0 = $this->_createHeader('Message-ID'); + $header1 = $this->_createHeader('Message-ID'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($header0)); + $factory->expects($this->at(1)) + ->method('createIdHeader') + ->with('Message-ID', 'other@id') + ->will($this->returnValue($header1)); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $set->addIdHeader('Message-ID', 'other@id'); + $set->remove('Message-ID', 0); + $this->assertFalse($set->has('Message-ID', 0)); + $this->assertTrue($set->has('Message-ID', 1)); + $this->assertTrue($set->has('Message-ID')); + $set->remove('Message-ID', 1); + $this->assertFalse($set->has('Message-ID', 1)); + $this->assertFalse($set->has('Message-ID')); + } + + public function testRemoveWithSpecifiedIndexLeavesOtherHeaders() + { + $header0 = $this->_createHeader('Message-ID'); + $header1 = $this->_createHeader('Message-ID'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($header0)); + $factory->expects($this->at(1)) + ->method('createIdHeader') + ->with('Message-ID', 'other@id') + ->will($this->returnValue($header1)); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $set->addIdHeader('Message-ID', 'other@id'); + $set->remove('Message-ID', 1); + $this->assertTrue($set->has('Message-ID', 0)); + } + + public function testRemoveWithInvalidOffsetDoesNothing() + { + $header = $this->_createHeader('Message-ID'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($header)); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $set->remove('Message-ID', 50); + $this->assertTrue($set->has('Message-ID')); + } + + public function testRemoveAllRemovesAllHeadersWithName() + { + $header0 = $this->_createHeader('Message-ID'); + $header1 = $this->_createHeader('Message-ID'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($header0)); + $factory->expects($this->at(1)) + ->method('createIdHeader') + ->with('Message-ID', 'other@id') + ->will($this->returnValue($header1)); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $set->addIdHeader('Message-ID', 'other@id'); + $set->removeAll('Message-ID'); + $this->assertFalse($set->has('Message-ID', 0)); + $this->assertFalse($set->has('Message-ID', 1)); + } + + public function testHasIsNotCaseSensitive() + { + $header = $this->_createHeader('Message-ID'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($header)); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $this->assertTrue($set->has('message-id')); + } + + public function testGetIsNotCaseSensitive() + { + $header = $this->_createHeader('Message-ID'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($header)); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $this->assertSame($header, $set->get('message-id')); + } + + public function testGetAllIsNotCaseSensitive() + { + $header = $this->_createHeader('Message-ID'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($header)); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $this->assertEquals(array($header), $set->getAll('message-id')); + } + + public function testRemoveIsNotCaseSensitive() + { + $header = $this->_createHeader('Message-ID'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($header)); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $set->remove('message-id'); + $this->assertFalse($set->has('Message-ID')); + } + + public function testRemoveAllIsNotCaseSensitive() + { + $header = $this->_createHeader('Message-ID'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($header)); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $set->removeAll('message-id'); + $this->assertFalse($set->has('Message-ID')); + } + + public function testNewInstance() + { + $set = $this->_createSet($this->_createFactory()); + $instance = $set->newInstance(); + $this->assertInstanceof('Swift_Mime_HeaderSet', $instance); + } + + public function testToStringJoinsHeadersTogether() + { + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createTextHeader') + ->with('Foo', 'bar') + ->will($this->returnValue($this->_createHeader('Foo', 'bar'))); + $factory->expects($this->at(1)) + ->method('createTextHeader') + ->with('Zip', 'buttons') + ->will($this->returnValue($this->_createHeader('Zip', 'buttons'))); + + $set = $this->_createSet($factory); + $set->addTextHeader('Foo', 'bar'); + $set->addTextHeader('Zip', 'buttons'); + $this->assertEquals( + "Foo: bar\r\n". + "Zip: buttons\r\n", + $set->toString() + ); + } + + public function testHeadersWithoutBodiesAreNotDisplayed() + { + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createTextHeader') + ->with('Foo', 'bar') + ->will($this->returnValue($this->_createHeader('Foo', 'bar'))); + $factory->expects($this->at(1)) + ->method('createTextHeader') + ->with('Zip', '') + ->will($this->returnValue($this->_createHeader('Zip', ''))); + + $set = $this->_createSet($factory); + $set->addTextHeader('Foo', 'bar'); + $set->addTextHeader('Zip', ''); + $this->assertEquals( + "Foo: bar\r\n", + $set->toString() + ); + } + + public function testHeadersWithoutBodiesCanBeForcedToDisplay() + { + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createTextHeader') + ->with('Foo', '') + ->will($this->returnValue($this->_createHeader('Foo', ''))); + $factory->expects($this->at(1)) + ->method('createTextHeader') + ->with('Zip', '') + ->will($this->returnValue($this->_createHeader('Zip', ''))); + + $set = $this->_createSet($factory); + $set->addTextHeader('Foo', ''); + $set->addTextHeader('Zip', ''); + $set->setAlwaysDisplayed(array('Foo', 'Zip')); + $this->assertEquals( + "Foo: \r\n". + "Zip: \r\n", + $set->toString() + ); + } + + public function testHeaderSequencesCanBeSpecified() + { + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createTextHeader') + ->with('Third', 'three') + ->will($this->returnValue($this->_createHeader('Third', 'three'))); + $factory->expects($this->at(1)) + ->method('createTextHeader') + ->with('First', 'one') + ->will($this->returnValue($this->_createHeader('First', 'one'))); + $factory->expects($this->at(2)) + ->method('createTextHeader') + ->with('Second', 'two') + ->will($this->returnValue($this->_createHeader('Second', 'two'))); + + $set = $this->_createSet($factory); + $set->addTextHeader('Third', 'three'); + $set->addTextHeader('First', 'one'); + $set->addTextHeader('Second', 'two'); + + $set->defineOrdering(array('First', 'Second', 'Third')); + + $this->assertEquals( + "First: one\r\n". + "Second: two\r\n". + "Third: three\r\n", + $set->toString() + ); + } + + public function testUnsortedHeadersAppearAtEnd() + { + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createTextHeader') + ->with('Fourth', 'four') + ->will($this->returnValue($this->_createHeader('Fourth', 'four'))); + $factory->expects($this->at(1)) + ->method('createTextHeader') + ->with('Fifth', 'five') + ->will($this->returnValue($this->_createHeader('Fifth', 'five'))); + $factory->expects($this->at(2)) + ->method('createTextHeader') + ->with('Third', 'three') + ->will($this->returnValue($this->_createHeader('Third', 'three'))); + $factory->expects($this->at(3)) + ->method('createTextHeader') + ->with('First', 'one') + ->will($this->returnValue($this->_createHeader('First', 'one'))); + $factory->expects($this->at(4)) + ->method('createTextHeader') + ->with('Second', 'two') + ->will($this->returnValue($this->_createHeader('Second', 'two'))); + + $set = $this->_createSet($factory); + $set->addTextHeader('Fourth', 'four'); + $set->addTextHeader('Fifth', 'five'); + $set->addTextHeader('Third', 'three'); + $set->addTextHeader('First', 'one'); + $set->addTextHeader('Second', 'two'); + + $set->defineOrdering(array('First', 'Second', 'Third')); + + $this->assertEquals( + "First: one\r\n". + "Second: two\r\n". + "Third: three\r\n". + "Fourth: four\r\n". + "Fifth: five\r\n", + $set->toString() + ); + } + + public function testSettingCharsetNotifiesAlreadyExistingHeaders() + { + $subject = $this->_createHeader('Subject', 'some text'); + $xHeader = $this->_createHeader('X-Header', 'some text'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createTextHeader') + ->with('Subject', 'some text') + ->will($this->returnValue($subject)); + $factory->expects($this->at(1)) + ->method('createTextHeader') + ->with('X-Header', 'some text') + ->will($this->returnValue($xHeader)); + $subject->expects($this->once()) + ->method('setCharset') + ->with('utf-8'); + $xHeader->expects($this->once()) + ->method('setCharset') + ->with('utf-8'); + + $set = $this->_createSet($factory); + $set->addTextHeader('Subject', 'some text'); + $set->addTextHeader('X-Header', 'some text'); + + $set->setCharset('utf-8'); + } + + public function testCharsetChangeNotifiesAlreadyExistingHeaders() + { + $subject = $this->_createHeader('Subject', 'some text'); + $xHeader = $this->_createHeader('X-Header', 'some text'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createTextHeader') + ->with('Subject', 'some text') + ->will($this->returnValue($subject)); + $factory->expects($this->at(1)) + ->method('createTextHeader') + ->with('X-Header', 'some text') + ->will($this->returnValue($xHeader)); + $subject->expects($this->once()) + ->method('setCharset') + ->with('utf-8'); + $xHeader->expects($this->once()) + ->method('setCharset') + ->with('utf-8'); + + $set = $this->_createSet($factory); + $set->addTextHeader('Subject', 'some text'); + $set->addTextHeader('X-Header', 'some text'); + + $set->charsetChanged('utf-8'); + } + + public function testCharsetChangeNotifiesFactory() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('charsetChanged') + ->with('utf-8'); + + $set = $this->_createSet($factory); + + $set->setCharset('utf-8'); + } + + // -- Creation methods + + private function _createSet($factory) + { + return new Swift_Mime_SimpleHeaderSet($factory); + } + + private function _createFactory() + { + return $this->getMockBuilder('Swift_Mime_HeaderFactory')->getMock(); + } + + private function _createHeader($name, $body = '') + { + $header = $this->getMockBuilder('Swift_Mime_Header')->getMock(); + $header->expects($this->any()) + ->method('getFieldName') + ->will($this->returnValue($name)); + $header->expects($this->any()) + ->method('toString') + ->will($this->returnValue(sprintf("%s: %s\r\n", $name, $body))); + $header->expects($this->any()) + ->method('getFieldBody') + ->will($this->returnValue($body)); + + return $header; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleMessageTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleMessageTest.php new file mode 100644 index 0000000000..267d276474 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleMessageTest.php @@ -0,0 +1,829 @@ +_createMessage($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals( + Swift_Mime_MimeEntity::LEVEL_TOP, $message->getNestingLevel() + ); + } + + public function testDateIsReturnedFromHeader() + { + $date = $this->_createHeader('Date', 123); + $message = $this->_createMessage( + $this->_createHeaderSet(array('Date' => $date)), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals(123, $message->getDate()); + } + + public function testDateIsSetInHeader() + { + $date = $this->_createHeader('Date', 123, array(), false); + $date->shouldReceive('setFieldBodyModel') + ->once() + ->with(1234); + $date->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('Date' => $date)), + $this->_createEncoder(), $this->_createCache() + ); + $message->setDate(1234); + } + + public function testDateHeaderIsCreatedIfNonePresent() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addDateHeader') + ->once() + ->with('Date', 1234); + $headers->shouldReceive('addDateHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setDate(1234); + } + + public function testDateHeaderIsAddedDuringConstruction() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addDateHeader') + ->once() + ->with('Date', '/^[0-9]+$/D'); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + } + + public function testIdIsReturnedFromHeader() + { + /* -- RFC 2045, 7. + In constructing a high-level user agent, it may be desirable to allow + one body to make reference to another. Accordingly, bodies may be + labelled using the "Content-ID" header field, which is syntactically + identical to the "Message-ID" header field + */ + + $messageId = $this->_createHeader('Message-ID', 'a@b'); + $message = $this->_createMessage( + $this->_createHeaderSet(array('Message-ID' => $messageId)), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals('a@b', $message->getId()); + } + + public function testIdIsSetInHeader() + { + $messageId = $this->_createHeader('Message-ID', 'a@b', array(), false); + $messageId->shouldReceive('setFieldBodyModel') + ->once() + ->with('x@y'); + $messageId->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('Message-ID' => $messageId)), + $this->_createEncoder(), $this->_createCache() + ); + $message->setId('x@y'); + } + + public function testIdIsAutoGenerated() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addIdHeader') + ->once() + ->with('Message-ID', '/^.*?@.*?$/D'); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + } + + public function testSubjectIsReturnedFromHeader() + { + /* -- RFC 2822, 3.6.5. + */ + + $subject = $this->_createHeader('Subject', 'example subject'); + $message = $this->_createMessage( + $this->_createHeaderSet(array('Subject' => $subject)), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals('example subject', $message->getSubject()); + } + + public function testSubjectIsSetInHeader() + { + $subject = $this->_createHeader('Subject', '', array(), false); + $subject->shouldReceive('setFieldBodyModel') + ->once() + ->with('foo'); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('Subject' => $subject)), + $this->_createEncoder(), $this->_createCache() + ); + $message->setSubject('foo'); + } + + public function testSubjectHeaderIsCreatedIfNotPresent() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addTextHeader') + ->once() + ->with('Subject', 'example subject'); + $headers->shouldReceive('addTextHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setSubject('example subject'); + } + + public function testReturnPathIsReturnedFromHeader() + { + /* -- RFC 2822, 3.6.7. + */ + + $path = $this->_createHeader('Return-Path', 'bounces@domain'); + $message = $this->_createMessage( + $this->_createHeaderSet(array('Return-Path' => $path)), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals('bounces@domain', $message->getReturnPath()); + } + + public function testReturnPathIsSetInHeader() + { + $path = $this->_createHeader('Return-Path', '', array(), false); + $path->shouldReceive('setFieldBodyModel') + ->once() + ->with('bounces@domain'); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('Return-Path' => $path)), + $this->_createEncoder(), $this->_createCache() + ); + $message->setReturnPath('bounces@domain'); + } + + public function testReturnPathHeaderIsAddedIfNoneSet() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addPathHeader') + ->once() + ->with('Return-Path', 'bounces@domain'); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setReturnPath('bounces@domain'); + } + + public function testSenderIsReturnedFromHeader() + { + /* -- RFC 2822, 3.6.2. + */ + + $sender = $this->_createHeader('Sender', array('sender@domain' => 'Name')); + $message = $this->_createMessage( + $this->_createHeaderSet(array('Sender' => $sender)), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals(array('sender@domain' => 'Name'), $message->getSender()); + } + + public function testSenderIsSetInHeader() + { + $sender = $this->_createHeader('Sender', array('sender@domain' => 'Name'), + array(), false + ); + $sender->shouldReceive('setFieldBodyModel') + ->once() + ->with(array('other@domain' => 'Other')); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('Sender' => $sender)), + $this->_createEncoder(), $this->_createCache() + ); + $message->setSender(array('other@domain' => 'Other')); + } + + public function testSenderHeaderIsAddedIfNoneSet() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addMailboxHeader') + ->once() + ->with('Sender', (array) 'sender@domain'); + $headers->shouldReceive('addMailboxHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setSender('sender@domain'); + } + + public function testNameCanBeUsedInSenderHeader() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addMailboxHeader') + ->once() + ->with('Sender', array('sender@domain' => 'Name')); + $headers->shouldReceive('addMailboxHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setSender('sender@domain', 'Name'); + } + + public function testFromIsReturnedFromHeader() + { + /* -- RFC 2822, 3.6.2. + */ + + $from = $this->_createHeader('From', array('from@domain' => 'Name')); + $message = $this->_createMessage( + $this->_createHeaderSet(array('From' => $from)), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals(array('from@domain' => 'Name'), $message->getFrom()); + } + + public function testFromIsSetInHeader() + { + $from = $this->_createHeader('From', array('from@domain' => 'Name'), + array(), false + ); + $from->shouldReceive('setFieldBodyModel') + ->once() + ->with(array('other@domain' => 'Other')); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('From' => $from)), + $this->_createEncoder(), $this->_createCache() + ); + $message->setFrom(array('other@domain' => 'Other')); + } + + public function testFromIsAddedToHeadersDuringAddFrom() + { + $from = $this->_createHeader('From', array('from@domain' => 'Name'), + array(), false + ); + $from->shouldReceive('setFieldBodyModel') + ->once() + ->with(array('from@domain' => 'Name', 'other@domain' => 'Other')); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('From' => $from)), + $this->_createEncoder(), $this->_createCache() + ); + $message->addFrom('other@domain', 'Other'); + } + + public function testFromHeaderIsAddedIfNoneSet() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addMailboxHeader') + ->once() + ->with('From', (array) 'from@domain'); + $headers->shouldReceive('addMailboxHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setFrom('from@domain'); + } + + public function testPersonalNameCanBeUsedInFromAddress() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addMailboxHeader') + ->once() + ->with('From', array('from@domain' => 'Name')); + $headers->shouldReceive('addMailboxHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setFrom('from@domain', 'Name'); + } + + public function testReplyToIsReturnedFromHeader() + { + /* -- RFC 2822, 3.6.2. + */ + + $reply = $this->_createHeader('Reply-To', array('reply@domain' => 'Name')); + $message = $this->_createMessage( + $this->_createHeaderSet(array('Reply-To' => $reply)), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals(array('reply@domain' => 'Name'), $message->getReplyTo()); + } + + public function testReplyToIsSetInHeader() + { + $reply = $this->_createHeader('Reply-To', array('reply@domain' => 'Name'), + array(), false + ); + $reply->shouldReceive('setFieldBodyModel') + ->once() + ->with(array('other@domain' => 'Other')); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('Reply-To' => $reply)), + $this->_createEncoder(), $this->_createCache() + ); + $message->setReplyTo(array('other@domain' => 'Other')); + } + + public function testReplyToIsAddedToHeadersDuringAddReplyTo() + { + $replyTo = $this->_createHeader('Reply-To', array('from@domain' => 'Name'), + array(), false + ); + $replyTo->shouldReceive('setFieldBodyModel') + ->once() + ->with(array('from@domain' => 'Name', 'other@domain' => 'Other')); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('Reply-To' => $replyTo)), + $this->_createEncoder(), $this->_createCache() + ); + $message->addReplyTo('other@domain', 'Other'); + } + + public function testReplyToHeaderIsAddedIfNoneSet() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addMailboxHeader') + ->once() + ->with('Reply-To', (array) 'reply@domain'); + $headers->shouldReceive('addMailboxHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setReplyTo('reply@domain'); + } + + public function testNameCanBeUsedInReplyTo() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addMailboxHeader') + ->once() + ->with('Reply-To', array('reply@domain' => 'Name')); + $headers->shouldReceive('addMailboxHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setReplyTo('reply@domain', 'Name'); + } + + public function testToIsReturnedFromHeader() + { + /* -- RFC 2822, 3.6.3. + */ + + $to = $this->_createHeader('To', array('to@domain' => 'Name')); + $message = $this->_createMessage( + $this->_createHeaderSet(array('To' => $to)), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals(array('to@domain' => 'Name'), $message->getTo()); + } + + public function testToIsSetInHeader() + { + $to = $this->_createHeader('To', array('to@domain' => 'Name'), + array(), false + ); + $to->shouldReceive('setFieldBodyModel') + ->once() + ->with(array('other@domain' => 'Other')); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('To' => $to)), + $this->_createEncoder(), $this->_createCache() + ); + $message->setTo(array('other@domain' => 'Other')); + } + + public function testToIsAddedToHeadersDuringAddTo() + { + $to = $this->_createHeader('To', array('from@domain' => 'Name'), + array(), false + ); + $to->shouldReceive('setFieldBodyModel') + ->once() + ->with(array('from@domain' => 'Name', 'other@domain' => 'Other')); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('To' => $to)), + $this->_createEncoder(), $this->_createCache() + ); + $message->addTo('other@domain', 'Other'); + } + + public function testToHeaderIsAddedIfNoneSet() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addMailboxHeader') + ->once() + ->with('To', (array) 'to@domain'); + $headers->shouldReceive('addMailboxHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setTo('to@domain'); + } + + public function testNameCanBeUsedInToHeader() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addMailboxHeader') + ->once() + ->with('To', array('to@domain' => 'Name')); + $headers->shouldReceive('addMailboxHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setTo('to@domain', 'Name'); + } + + public function testCcIsReturnedFromHeader() + { + /* -- RFC 2822, 3.6.3. + */ + + $cc = $this->_createHeader('Cc', array('cc@domain' => 'Name')); + $message = $this->_createMessage( + $this->_createHeaderSet(array('Cc' => $cc)), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals(array('cc@domain' => 'Name'), $message->getCc()); + } + + public function testCcIsSetInHeader() + { + $cc = $this->_createHeader('Cc', array('cc@domain' => 'Name'), + array(), false + ); + $cc->shouldReceive('setFieldBodyModel') + ->once() + ->with(array('other@domain' => 'Other')); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('Cc' => $cc)), + $this->_createEncoder(), $this->_createCache() + ); + $message->setCc(array('other@domain' => 'Other')); + } + + public function testCcIsAddedToHeadersDuringAddCc() + { + $cc = $this->_createHeader('Cc', array('from@domain' => 'Name'), + array(), false + ); + $cc->shouldReceive('setFieldBodyModel') + ->once() + ->with(array('from@domain' => 'Name', 'other@domain' => 'Other')); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('Cc' => $cc)), + $this->_createEncoder(), $this->_createCache() + ); + $message->addCc('other@domain', 'Other'); + } + + public function testCcHeaderIsAddedIfNoneSet() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addMailboxHeader') + ->once() + ->with('Cc', (array) 'cc@domain'); + $headers->shouldReceive('addMailboxHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setCc('cc@domain'); + } + + public function testNameCanBeUsedInCcHeader() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addMailboxHeader') + ->once() + ->with('Cc', array('cc@domain' => 'Name')); + $headers->shouldReceive('addMailboxHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setCc('cc@domain', 'Name'); + } + + public function testBccIsReturnedFromHeader() + { + /* -- RFC 2822, 3.6.3. + */ + + $bcc = $this->_createHeader('Bcc', array('bcc@domain' => 'Name')); + $message = $this->_createMessage( + $this->_createHeaderSet(array('Bcc' => $bcc)), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals(array('bcc@domain' => 'Name'), $message->getBcc()); + } + + public function testBccIsSetInHeader() + { + $bcc = $this->_createHeader('Bcc', array('bcc@domain' => 'Name'), + array(), false + ); + $bcc->shouldReceive('setFieldBodyModel') + ->once() + ->with(array('other@domain' => 'Other')); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('Bcc' => $bcc)), + $this->_createEncoder(), $this->_createCache() + ); + $message->setBcc(array('other@domain' => 'Other')); + } + + public function testBccIsAddedToHeadersDuringAddBcc() + { + $bcc = $this->_createHeader('Bcc', array('from@domain' => 'Name'), + array(), false + ); + $bcc->shouldReceive('setFieldBodyModel') + ->once() + ->with(array('from@domain' => 'Name', 'other@domain' => 'Other')); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('Bcc' => $bcc)), + $this->_createEncoder(), $this->_createCache() + ); + $message->addBcc('other@domain', 'Other'); + } + + public function testBccHeaderIsAddedIfNoneSet() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addMailboxHeader') + ->once() + ->with('Bcc', (array) 'bcc@domain'); + $headers->shouldReceive('addMailboxHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setBcc('bcc@domain'); + } + + public function testNameCanBeUsedInBcc() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addMailboxHeader') + ->once() + ->with('Bcc', array('bcc@domain' => 'Name')); + $headers->shouldReceive('addMailboxHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setBcc('bcc@domain', 'Name'); + } + + public function testPriorityIsReadFromHeader() + { + $prio = $this->_createHeader('X-Priority', '2 (High)'); + $message = $this->_createMessage( + $this->_createHeaderSet(array('X-Priority' => $prio)), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals(2, $message->getPriority()); + } + + public function testPriorityIsSetInHeader() + { + $prio = $this->_createHeader('X-Priority', '2 (High)', array(), false); + $prio->shouldReceive('setFieldBodyModel') + ->once() + ->with('5 (Lowest)'); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('X-Priority' => $prio)), + $this->_createEncoder(), $this->_createCache() + ); + $message->setPriority($message::PRIORITY_LOWEST); + } + + public function testPriorityHeaderIsAddedIfNoneSet() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addTextHeader') + ->once() + ->with('X-Priority', '4 (Low)'); + $headers->shouldReceive('addTextHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setPriority($message::PRIORITY_LOW); + } + + public function testReadReceiptAddressReadFromHeader() + { + $rcpt = $this->_createHeader('Disposition-Notification-To', + array('chris@swiftmailer.org' => 'Chris') + ); + $message = $this->_createMessage( + $this->_createHeaderSet(array('Disposition-Notification-To' => $rcpt)), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals(array('chris@swiftmailer.org' => 'Chris'), + $message->getReadReceiptTo() + ); + } + + public function testReadReceiptIsSetInHeader() + { + $rcpt = $this->_createHeader('Disposition-Notification-To', array(), array(), false); + $rcpt->shouldReceive('setFieldBodyModel') + ->once() + ->with('mark@swiftmailer.org'); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('Disposition-Notification-To' => $rcpt)), + $this->_createEncoder(), $this->_createCache() + ); + $message->setReadReceiptTo('mark@swiftmailer.org'); + } + + public function testReadReceiptHeaderIsAddedIfNoneSet() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addMailboxHeader') + ->once() + ->with('Disposition-Notification-To', 'mark@swiftmailer.org'); + $headers->shouldReceive('addMailboxHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setReadReceiptTo('mark@swiftmailer.org'); + } + + public function testChildrenCanBeAttached() + { + $child1 = $this->_createChild(); + $child2 = $this->_createChild(); + + $message = $this->_createMessage($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + + $message->attach($child1); + $message->attach($child2); + + $this->assertEquals(array($child1, $child2), $message->getChildren()); + } + + public function testChildrenCanBeDetached() + { + $child1 = $this->_createChild(); + $child2 = $this->_createChild(); + + $message = $this->_createMessage($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + + $message->attach($child1); + $message->attach($child2); + + $message->detach($child1); + + $this->assertEquals(array($child2), $message->getChildren()); + } + + public function testEmbedAttachesChild() + { + $child = $this->_createChild(); + + $message = $this->_createMessage($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + + $message->embed($child); + + $this->assertEquals(array($child), $message->getChildren()); + } + + public function testEmbedReturnsValidCid() + { + $child = $this->_createChild(Swift_Mime_MimeEntity::LEVEL_RELATED, '', + false + ); + $child->shouldReceive('getId') + ->zeroOrMoreTimes() + ->andReturn('foo@bar'); + + $message = $this->_createMessage($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + + $this->assertEquals('cid:foo@bar', $message->embed($child)); + } + + public function testFluidInterface() + { + $child = $this->_createChild(); + $message = $this->_createMessage($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertSame($message, + $message + ->setContentType('text/plain') + ->setEncoder($this->_createEncoder()) + ->setId('foo@bar') + ->setDescription('my description') + ->setMaxLineLength(998) + ->setBody('xx') + ->setBoundary('xyz') + ->setChildren(array()) + ->setCharset('iso-8859-1') + ->setFormat('flowed') + ->setDelSp(false) + ->setSubject('subj') + ->setDate(123) + ->setReturnPath('foo@bar') + ->setSender('foo@bar') + ->setFrom(array('x@y' => 'XY')) + ->setReplyTo(array('ab@cd' => 'ABCD')) + ->setTo(array('chris@site.tld', 'mark@site.tld')) + ->setCc('john@somewhere.tld') + ->setBcc(array('one@site', 'two@site' => 'Two')) + ->setPriority($message::PRIORITY_LOW) + ->setReadReceiptTo('a@b') + ->attach($child) + ->detach($child) + ); + } + + // -- Private helpers + + //abstract + protected function _createEntity($headers, $encoder, $cache) + { + return $this->_createMessage($headers, $encoder, $cache); + } + + protected function _createMimePart($headers, $encoder, $cache) + { + return $this->_createMessage($headers, $encoder, $cache); + } + + private function _createMessage($headers, $encoder, $cache) + { + return new Swift_Mime_SimpleMessage($headers, $encoder, $cache, new Swift_Mime_Grammar()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleMimeEntityTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleMimeEntityTest.php new file mode 100644 index 0000000000..b54d7f874c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleMimeEntityTest.php @@ -0,0 +1,11 @@ +assertEquals(10, $plugin->getThreshold()); + $plugin->setThreshold(100); + $this->assertEquals(100, $plugin->getThreshold()); + } + + public function testSleepTimeCanBeSetAndFetched() + { + $plugin = new Swift_Plugins_AntiFloodPlugin(10, 5); + $this->assertEquals(5, $plugin->getSleepTime()); + $plugin->setSleepTime(1); + $this->assertEquals(1, $plugin->getSleepTime()); + } + + public function testPluginStopsConnectionAfterThreshold() + { + $transport = $this->_createTransport(); + $transport->expects($this->once()) + ->method('start'); + $transport->expects($this->once()) + ->method('stop'); + + $evt = $this->_createSendEvent($transport); + + $plugin = new Swift_Plugins_AntiFloodPlugin(10); + for ($i = 0; $i < 12; ++$i) { + $plugin->sendPerformed($evt); + } + } + + public function testPluginCanStopAndStartMultipleTimes() + { + $transport = $this->_createTransport(); + $transport->expects($this->exactly(5)) + ->method('start'); + $transport->expects($this->exactly(5)) + ->method('stop'); + + $evt = $this->_createSendEvent($transport); + + $plugin = new Swift_Plugins_AntiFloodPlugin(2); + for ($i = 0; $i < 11; ++$i) { + $plugin->sendPerformed($evt); + } + } + + public function testPluginCanSleepDuringRestart() + { + $sleeper = $this->getMockBuilder('Swift_Plugins_Sleeper')->getMock(); + $sleeper->expects($this->once()) + ->method('sleep') + ->with(10); + + $transport = $this->_createTransport(); + $transport->expects($this->once()) + ->method('start'); + $transport->expects($this->once()) + ->method('stop'); + + $evt = $this->_createSendEvent($transport); + + $plugin = new Swift_Plugins_AntiFloodPlugin(99, 10, $sleeper); + for ($i = 0; $i < 101; ++$i) { + $plugin->sendPerformed($evt); + } + } + + // -- Creation Methods + + private function _createTransport() + { + return $this->getMockBuilder('Swift_Transport')->getMock(); + } + + private function _createSendEvent($transport) + { + $evt = $this->getMockBuilder('Swift_Events_SendEvent') + ->disableOriginalConstructor() + ->getMock(); + $evt->expects($this->any()) + ->method('getSource') + ->will($this->returnValue($transport)); + $evt->expects($this->any()) + ->method('getTransport') + ->will($this->returnValue($transport)); + + return $evt; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/BandwidthMonitorPluginTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/BandwidthMonitorPluginTest.php new file mode 100644 index 0000000000..bb01a92c9a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/BandwidthMonitorPluginTest.php @@ -0,0 +1,130 @@ +_monitor = new Swift_Plugins_BandwidthMonitorPlugin(); + } + + public function testBytesOutIncreasesWhenCommandsSent() + { + $evt = $this->_createCommandEvent("RCPT TO:\r\n"); + + $this->assertEquals(0, $this->_monitor->getBytesOut()); + $this->_monitor->commandSent($evt); + $this->assertEquals(23, $this->_monitor->getBytesOut()); + $this->_monitor->commandSent($evt); + $this->assertEquals(46, $this->_monitor->getBytesOut()); + } + + public function testBytesInIncreasesWhenResponsesReceived() + { + $evt = $this->_createResponseEvent("250 Ok\r\n"); + + $this->assertEquals(0, $this->_monitor->getBytesIn()); + $this->_monitor->responseReceived($evt); + $this->assertEquals(8, $this->_monitor->getBytesIn()); + $this->_monitor->responseReceived($evt); + $this->assertEquals(16, $this->_monitor->getBytesIn()); + } + + public function testCountersCanBeReset() + { + $evt = $this->_createResponseEvent("250 Ok\r\n"); + + $this->assertEquals(0, $this->_monitor->getBytesIn()); + $this->_monitor->responseReceived($evt); + $this->assertEquals(8, $this->_monitor->getBytesIn()); + $this->_monitor->responseReceived($evt); + $this->assertEquals(16, $this->_monitor->getBytesIn()); + + $evt = $this->_createCommandEvent("RCPT TO:\r\n"); + + $this->assertEquals(0, $this->_monitor->getBytesOut()); + $this->_monitor->commandSent($evt); + $this->assertEquals(23, $this->_monitor->getBytesOut()); + $this->_monitor->commandSent($evt); + $this->assertEquals(46, $this->_monitor->getBytesOut()); + + $this->_monitor->reset(); + + $this->assertEquals(0, $this->_monitor->getBytesOut()); + $this->assertEquals(0, $this->_monitor->getBytesIn()); + } + + public function testBytesOutIncreasesAccordingToMessageLength() + { + $message = $this->_createMessageWithByteCount(6); + $evt = $this->_createSendEvent($message); + + $this->assertEquals(0, $this->_monitor->getBytesOut()); + $this->_monitor->sendPerformed($evt); + $this->assertEquals(6, $this->_monitor->getBytesOut()); + $this->_monitor->sendPerformed($evt); + $this->assertEquals(12, $this->_monitor->getBytesOut()); + } + + // -- Creation Methods + + private function _createSendEvent($message) + { + $evt = $this->getMockBuilder('Swift_Events_SendEvent') + ->disableOriginalConstructor() + ->getMock(); + $evt->expects($this->any()) + ->method('getMessage') + ->will($this->returnValue($message)); + + return $evt; + } + + private function _createCommandEvent($command) + { + $evt = $this->getMockBuilder('Swift_Events_CommandEvent') + ->disableOriginalConstructor() + ->getMock(); + $evt->expects($this->any()) + ->method('getCommand') + ->will($this->returnValue($command)); + + return $evt; + } + + private function _createResponseEvent($response) + { + $evt = $this->getMockBuilder('Swift_Events_ResponseEvent') + ->disableOriginalConstructor() + ->getMock(); + $evt->expects($this->any()) + ->method('getResponse') + ->will($this->returnValue($response)); + + return $evt; + } + + private function _createMessageWithByteCount($bytes) + { + $this->_bytes = $bytes; + $msg = $this->getMockBuilder('Swift_Mime_Message')->getMock(); + $msg->expects($this->any()) + ->method('toByteStream') + ->will($this->returnCallback(array($this, '_write'))); + /* $this->_checking(Expectations::create() + -> ignoring($msg)->toByteStream(any()) -> calls(array($this, '_write')) + ); */ + + return $msg; + } + + public function _write($is) + { + for ($i = 0; $i < $this->_bytes; ++$i) { + $is->write('x'); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/DecoratorPluginTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/DecoratorPluginTest.php new file mode 100644 index 0000000000..7f4cdef15d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/DecoratorPluginTest.php @@ -0,0 +1,269 @@ +_createMessage( + $this->_createHeaders(), + array('zip@button.tld' => 'Zipathon'), + array('chris.corbyn@swiftmailer.org' => 'Chris'), + 'Subject', + 'Hello {name}, you are customer #{id}' + ); + $message->shouldReceive('setBody') + ->once() + ->with('Hello Zip, you are customer #456'); + $message->shouldReceive('setBody') + ->zeroOrMoreTimes(); + + $plugin = $this->_createPlugin( + array('zip@button.tld' => array('{name}' => 'Zip', '{id}' => '456')) + ); + + $evt = $this->_createSendEvent($message); + + $plugin->beforeSendPerformed($evt); + $plugin->sendPerformed($evt); + } + + public function testReplacementsCanBeAppliedToSameMessageMultipleTimes() + { + $message = $this->_createMessage( + $this->_createHeaders(), + array('zip@button.tld' => 'Zipathon', 'foo@bar.tld' => 'Foo'), + array('chris.corbyn@swiftmailer.org' => 'Chris'), + 'Subject', + 'Hello {name}, you are customer #{id}' + ); + $message->shouldReceive('setBody') + ->once() + ->with('Hello Zip, you are customer #456'); + $message->shouldReceive('setBody') + ->once() + ->with('Hello {name}, you are customer #{id}'); + $message->shouldReceive('setBody') + ->once() + ->with('Hello Foo, you are customer #123'); + $message->shouldReceive('setBody') + ->zeroOrMoreTimes(); + + $plugin = $this->_createPlugin( + array( + 'foo@bar.tld' => array('{name}' => 'Foo', '{id}' => '123'), + 'zip@button.tld' => array('{name}' => 'Zip', '{id}' => '456'), + ) + ); + + $evt = $this->_createSendEvent($message); + + $plugin->beforeSendPerformed($evt); + $plugin->sendPerformed($evt); + $plugin->beforeSendPerformed($evt); + $plugin->sendPerformed($evt); + } + + public function testReplacementsCanBeMadeInHeaders() + { + $headers = $this->_createHeaders(array( + $returnPathHeader = $this->_createHeader('Return-Path', 'foo-{id}@swiftmailer.org'), + $toHeader = $this->_createHeader('Subject', 'A message for {name}!'), + )); + + $message = $this->_createMessage( + $headers, + array('zip@button.tld' => 'Zipathon'), + array('chris.corbyn@swiftmailer.org' => 'Chris'), + 'A message for {name}!', + 'Hello {name}, you are customer #{id}' + ); + + $message->shouldReceive('setBody') + ->once() + ->with('Hello Zip, you are customer #456'); + $toHeader->shouldReceive('setFieldBodyModel') + ->once() + ->with('A message for Zip!'); + $returnPathHeader->shouldReceive('setFieldBodyModel') + ->once() + ->with('foo-456@swiftmailer.org'); + $message->shouldReceive('setBody') + ->zeroOrMoreTimes(); + $toHeader->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes(); + $returnPathHeader->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes(); + + $plugin = $this->_createPlugin( + array('zip@button.tld' => array('{name}' => 'Zip', '{id}' => '456')) + ); + $evt = $this->_createSendEvent($message); + + $plugin->beforeSendPerformed($evt); + $plugin->sendPerformed($evt); + } + + public function testReplacementsAreMadeOnSubparts() + { + $part1 = $this->_createPart('text/plain', 'Your name is {name}?', '1@x'); + $part2 = $this->_createPart('text/html', 'Your name is {name}?', '2@x'); + $message = $this->_createMessage( + $this->_createHeaders(), + array('zip@button.tld' => 'Zipathon'), + array('chris.corbyn@swiftmailer.org' => 'Chris'), + 'A message for {name}!', + 'Subject' + ); + $message->shouldReceive('getChildren') + ->zeroOrMoreTimes() + ->andReturn(array($part1, $part2)); + $part1->shouldReceive('setBody') + ->once() + ->with('Your name is Zip?'); + $part2->shouldReceive('setBody') + ->once() + ->with('Your name is Zip?'); + $part1->shouldReceive('setBody') + ->zeroOrMoreTimes(); + $part2->shouldReceive('setBody') + ->zeroOrMoreTimes(); + + $plugin = $this->_createPlugin( + array('zip@button.tld' => array('{name}' => 'Zip', '{id}' => '456')) + ); + + $evt = $this->_createSendEvent($message); + + $plugin->beforeSendPerformed($evt); + $plugin->sendPerformed($evt); + } + + public function testReplacementsCanBeTakenFromCustomReplacementsObject() + { + $message = $this->_createMessage( + $this->_createHeaders(), + array('foo@bar' => 'Foobar', 'zip@zap' => 'Zip zap'), + array('chris.corbyn@swiftmailer.org' => 'Chris'), + 'Subject', + 'Something {a}' + ); + + $replacements = $this->_createReplacements(); + + $message->shouldReceive('setBody') + ->once() + ->with('Something b'); + $message->shouldReceive('setBody') + ->once() + ->with('Something c'); + $message->shouldReceive('setBody') + ->zeroOrMoreTimes(); + $replacements->shouldReceive('getReplacementsFor') + ->once() + ->with('foo@bar') + ->andReturn(array('{a}' => 'b')); + $replacements->shouldReceive('getReplacementsFor') + ->once() + ->with('zip@zap') + ->andReturn(array('{a}' => 'c')); + + $plugin = $this->_createPlugin($replacements); + + $evt = $this->_createSendEvent($message); + + $plugin->beforeSendPerformed($evt); + $plugin->sendPerformed($evt); + $plugin->beforeSendPerformed($evt); + $plugin->sendPerformed($evt); + } + + // -- Creation methods + + private function _createMessage($headers, $to = array(), $from = null, $subject = null, + $body = null) + { + $message = $this->getMockery('Swift_Mime_Message')->shouldIgnoreMissing(); + foreach ($to as $addr => $name) { + $message->shouldReceive('getTo') + ->once() + ->andReturn(array($addr => $name)); + } + $message->shouldReceive('getHeaders') + ->zeroOrMoreTimes() + ->andReturn($headers); + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn($from); + $message->shouldReceive('getSubject') + ->zeroOrMoreTimes() + ->andReturn($subject); + $message->shouldReceive('getBody') + ->zeroOrMoreTimes() + ->andReturn($body); + + return $message; + } + + private function _createPlugin($replacements) + { + return new Swift_Plugins_DecoratorPlugin($replacements); + } + + private function _createReplacements() + { + return $this->getMockery('Swift_Plugins_Decorator_Replacements')->shouldIgnoreMissing(); + } + + private function _createSendEvent(Swift_Mime_Message $message) + { + $evt = $this->getMockery('Swift_Events_SendEvent')->shouldIgnoreMissing(); + $evt->shouldReceive('getMessage') + ->zeroOrMoreTimes() + ->andReturn($message); + + return $evt; + } + + private function _createPart($type, $body, $id) + { + $part = $this->getMockery('Swift_Mime_MimeEntity')->shouldIgnoreMissing(); + $part->shouldReceive('getContentType') + ->zeroOrMoreTimes() + ->andReturn($type); + $part->shouldReceive('getBody') + ->zeroOrMoreTimes() + ->andReturn($body); + $part->shouldReceive('getId') + ->zeroOrMoreTimes() + ->andReturn($id); + + return $part; + } + + private function _createHeaders($headers = array()) + { + $set = $this->getMockery('Swift_Mime_HeaderSet')->shouldIgnoreMissing(); + $set->shouldReceive('getAll') + ->zeroOrMoreTimes() + ->andReturn($headers); + + foreach ($headers as $header) { + $set->set($header); + } + + return $set; + } + + private function _createHeader($name, $body = '') + { + $header = $this->getMockery('Swift_Mime_Header')->shouldIgnoreMissing(); + $header->shouldReceive('getFieldName') + ->zeroOrMoreTimes() + ->andReturn($name); + $header->shouldReceive('getFieldBodyModel') + ->zeroOrMoreTimes() + ->andReturn($body); + + return $header; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/LoggerPluginTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/LoggerPluginTest.php new file mode 100644 index 0000000000..15d7014b31 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/LoggerPluginTest.php @@ -0,0 +1,190 @@ +_createLogger(); + $logger->expects($this->once()) + ->method('add') + ->with('foo'); + + $plugin = $this->_createPlugin($logger); + $plugin->add('foo'); + } + + public function testLoggerDelegatesDumpingEntries() + { + $logger = $this->_createLogger(); + $logger->expects($this->once()) + ->method('dump') + ->will($this->returnValue('foobar')); + + $plugin = $this->_createPlugin($logger); + $this->assertEquals('foobar', $plugin->dump()); + } + + public function testLoggerDelegatesClearingEntries() + { + $logger = $this->_createLogger(); + $logger->expects($this->once()) + ->method('clear'); + + $plugin = $this->_createPlugin($logger); + $plugin->clear(); + } + + public function testCommandIsSentToLogger() + { + $evt = $this->_createCommandEvent("foo\r\n"); + $logger = $this->_createLogger(); + $logger->expects($this->once()) + ->method('add') + ->with($this->regExp('~foo\r\n~')); + + $plugin = $this->_createPlugin($logger); + $plugin->commandSent($evt); + } + + public function testResponseIsSentToLogger() + { + $evt = $this->_createResponseEvent("354 Go ahead\r\n"); + $logger = $this->_createLogger(); + $logger->expects($this->once()) + ->method('add') + ->with($this->regExp('~354 Go ahead\r\n~')); + + $plugin = $this->_createPlugin($logger); + $plugin->responseReceived($evt); + } + + public function testTransportBeforeStartChangeIsSentToLogger() + { + $evt = $this->_createTransportChangeEvent(); + $logger = $this->_createLogger(); + $logger->expects($this->once()) + ->method('add') + ->with($this->anything()); + + $plugin = $this->_createPlugin($logger); + $plugin->beforeTransportStarted($evt); + } + + public function testTransportStartChangeIsSentToLogger() + { + $evt = $this->_createTransportChangeEvent(); + $logger = $this->_createLogger(); + $logger->expects($this->once()) + ->method('add') + ->with($this->anything()); + + $plugin = $this->_createPlugin($logger); + $plugin->transportStarted($evt); + } + + public function testTransportStopChangeIsSentToLogger() + { + $evt = $this->_createTransportChangeEvent(); + $logger = $this->_createLogger(); + $logger->expects($this->once()) + ->method('add') + ->with($this->anything()); + + $plugin = $this->_createPlugin($logger); + $plugin->transportStopped($evt); + } + + public function testTransportBeforeStopChangeIsSentToLogger() + { + $evt = $this->_createTransportChangeEvent(); + $logger = $this->_createLogger(); + $logger->expects($this->once()) + ->method('add') + ->with($this->anything()); + + $plugin = $this->_createPlugin($logger); + $plugin->beforeTransportStopped($evt); + } + + public function testExceptionsArePassedToDelegateAndLeftToBubbleUp() + { + $transport = $this->_createTransport(); + $evt = $this->_createTransportExceptionEvent(); + $logger = $this->_createLogger(); + $logger->expects($this->once()) + ->method('add') + ->with($this->anything()); + + $plugin = $this->_createPlugin($logger); + try { + $plugin->exceptionThrown($evt); + $this->fail('Exception should bubble up.'); + } catch (Swift_TransportException $ex) { + } + } + + // -- Creation Methods + + private function _createLogger() + { + return $this->getMockBuilder('Swift_Plugins_Logger')->getMock(); + } + + private function _createPlugin($logger) + { + return new Swift_Plugins_LoggerPlugin($logger); + } + + private function _createCommandEvent($command) + { + $evt = $this->getMockBuilder('Swift_Events_CommandEvent') + ->disableOriginalConstructor() + ->getMock(); + $evt->expects($this->any()) + ->method('getCommand') + ->will($this->returnValue($command)); + + return $evt; + } + + private function _createResponseEvent($response) + { + $evt = $this->getMockBuilder('Swift_Events_ResponseEvent') + ->disableOriginalConstructor() + ->getMock(); + $evt->expects($this->any()) + ->method('getResponse') + ->will($this->returnValue($response)); + + return $evt; + } + + private function _createTransport() + { + return $this->getMockBuilder('Swift_Transport')->getMock(); + } + + private function _createTransportChangeEvent() + { + $evt = $this->getMockBuilder('Swift_Events_TransportChangeEvent') + ->disableOriginalConstructor() + ->getMock(); + $evt->expects($this->any()) + ->method('getSource') + ->will($this->returnValue($this->_createTransport())); + + return $evt; + } + + public function _createTransportExceptionEvent() + { + $evt = $this->getMockBuilder('Swift_Events_TransportExceptionEvent') + ->disableOriginalConstructor() + ->getMock(); + $evt->expects($this->any()) + ->method('getException') + ->will($this->returnValue(new Swift_TransportException(''))); + + return $evt; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Loggers/ArrayLoggerTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Loggers/ArrayLoggerTest.php new file mode 100644 index 0000000000..880bb32c9b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Loggers/ArrayLoggerTest.php @@ -0,0 +1,65 @@ +add(">> Foo\r\n"); + $this->assertEquals(">> Foo\r\n", $logger->dump()); + } + + public function testAddingMultipleEntriesDumpsMultipleLines() + { + $logger = new Swift_Plugins_Loggers_ArrayLogger(); + $logger->add(">> FOO\r\n"); + $logger->add("<< 502 That makes no sense\r\n"); + $logger->add(">> RSET\r\n"); + $logger->add("<< 250 OK\r\n"); + + $this->assertEquals( + ">> FOO\r\n".PHP_EOL. + "<< 502 That makes no sense\r\n".PHP_EOL. + ">> RSET\r\n".PHP_EOL. + "<< 250 OK\r\n", + $logger->dump() + ); + } + + public function testLogCanBeCleared() + { + $logger = new Swift_Plugins_Loggers_ArrayLogger(); + $logger->add(">> FOO\r\n"); + $logger->add("<< 502 That makes no sense\r\n"); + $logger->add(">> RSET\r\n"); + $logger->add("<< 250 OK\r\n"); + + $this->assertEquals( + ">> FOO\r\n".PHP_EOL. + "<< 502 That makes no sense\r\n".PHP_EOL. + ">> RSET\r\n".PHP_EOL. + "<< 250 OK\r\n", + $logger->dump() + ); + + $logger->clear(); + + $this->assertEquals('', $logger->dump()); + } + + public function testLengthCanBeTruncated() + { + $logger = new Swift_Plugins_Loggers_ArrayLogger(2); + $logger->add(">> FOO\r\n"); + $logger->add("<< 502 That makes no sense\r\n"); + $logger->add(">> RSET\r\n"); + $logger->add("<< 250 OK\r\n"); + + $this->assertEquals( + ">> RSET\r\n".PHP_EOL. + "<< 250 OK\r\n", + $logger->dump(), + '%s: Log should be truncated to last 2 entries' + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Loggers/EchoLoggerTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Loggers/EchoLoggerTest.php new file mode 100644 index 0000000000..6134fe64e5 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Loggers/EchoLoggerTest.php @@ -0,0 +1,24 @@ +add('>> Foo'); + $data = ob_get_clean(); + + $this->assertEquals('>> Foo'.PHP_EOL, $data); + } + + public function testAddingEntryDumpsEscapedLineWithHtml() + { + $logger = new Swift_Plugins_Loggers_EchoLogger(true); + ob_start(); + $logger->add('>> Foo'); + $data = ob_get_clean(); + + $this->assertEquals('>> Foo
'.PHP_EOL, $data); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/PopBeforeSmtpPluginTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/PopBeforeSmtpPluginTest.php new file mode 100644 index 0000000000..d7c55afcd1 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/PopBeforeSmtpPluginTest.php @@ -0,0 +1,103 @@ +_createConnection(); + $connection->expects($this->once()) + ->method('connect'); + + $plugin = $this->_createPlugin('pop.host.tld', 110); + $plugin->setConnection($connection); + + $transport = $this->_createTransport(); + $evt = $this->_createTransportChangeEvent($transport); + + $plugin->beforeTransportStarted($evt); + } + + public function testPluginDisconnectsFromPop3HostBeforeTransportStarts() + { + $connection = $this->_createConnection(); + $connection->expects($this->once()) + ->method('disconnect'); + + $plugin = $this->_createPlugin('pop.host.tld', 110); + $plugin->setConnection($connection); + + $transport = $this->_createTransport(); + $evt = $this->_createTransportChangeEvent($transport); + + $plugin->beforeTransportStarted($evt); + } + + public function testPluginDoesNotConnectToSmtpIfBoundToDifferentTransport() + { + $connection = $this->_createConnection(); + $connection->expects($this->never()) + ->method('disconnect'); + $connection->expects($this->never()) + ->method('connect'); + + $smtp = $this->_createTransport(); + + $plugin = $this->_createPlugin('pop.host.tld', 110); + $plugin->setConnection($connection); + $plugin->bindSmtp($smtp); + + $transport = $this->_createTransport(); + $evt = $this->_createTransportChangeEvent($transport); + + $plugin->beforeTransportStarted($evt); + } + + public function testPluginCanBindToSpecificTransport() + { + $connection = $this->_createConnection(); + $connection->expects($this->once()) + ->method('connect'); + + $smtp = $this->_createTransport(); + + $plugin = $this->_createPlugin('pop.host.tld', 110); + $plugin->setConnection($connection); + $plugin->bindSmtp($smtp); + + $evt = $this->_createTransportChangeEvent($smtp); + + $plugin->beforeTransportStarted($evt); + } + + // -- Creation Methods + + private function _createTransport() + { + return $this->getMockBuilder('Swift_Transport')->getMock(); + } + + private function _createTransportChangeEvent($transport) + { + $evt = $this->getMockBuilder('Swift_Events_TransportChangeEvent') + ->disableOriginalConstructor() + ->getMock(); + $evt->expects($this->any()) + ->method('getSource') + ->will($this->returnValue($transport)); + $evt->expects($this->any()) + ->method('getTransport') + ->will($this->returnValue($transport)); + + return $evt; + } + + public function _createConnection() + { + return $this->getMockBuilder('Swift_Plugins_Pop_Pop3Connection')->getMock(); + } + + public function _createPlugin($host, $port, $crypto = null) + { + return new Swift_Plugins_PopBeforeSmtpPlugin($host, $port, $crypto); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/RedirectingPluginTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/RedirectingPluginTest.php new file mode 100644 index 0000000000..4cc7e89f1f --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/RedirectingPluginTest.php @@ -0,0 +1,185 @@ +assertEquals('fabien@example.com', $plugin->getRecipient()); + $plugin->setRecipient('chris@example.com'); + $this->assertEquals('chris@example.com', $plugin->getRecipient()); + } + + public function testPluginChangesRecipients() + { + $message = Swift_Message::newInstance() + ->setSubject('...') + ->setFrom(array('john@example.com' => 'John Doe')) + ->setTo($to = array( + 'fabien-to@example.com' => 'Fabien (To)', + 'chris-to@example.com' => 'Chris (To)', + )) + ->setCc($cc = array( + 'fabien-cc@example.com' => 'Fabien (Cc)', + 'chris-cc@example.com' => 'Chris (Cc)', + )) + ->setBcc($bcc = array( + 'fabien-bcc@example.com' => 'Fabien (Bcc)', + 'chris-bcc@example.com' => 'Chris (Bcc)', + )) + ->setBody('...') + ; + + $plugin = new Swift_Plugins_RedirectingPlugin('god@example.com'); + + $evt = $this->_createSendEvent($message); + + $plugin->beforeSendPerformed($evt); + + $this->assertEquals($message->getTo(), array('god@example.com' => '')); + $this->assertEquals($message->getCc(), array()); + $this->assertEquals($message->getBcc(), array()); + + $plugin->sendPerformed($evt); + + $this->assertEquals($message->getTo(), $to); + $this->assertEquals($message->getCc(), $cc); + $this->assertEquals($message->getBcc(), $bcc); + } + + public function testPluginRespectsUnsetToList() + { + $message = Swift_Message::newInstance() + ->setSubject('...') + ->setFrom(array('john@example.com' => 'John Doe')) + ->setCc($cc = array( + 'fabien-cc@example.com' => 'Fabien (Cc)', + 'chris-cc@example.com' => 'Chris (Cc)', + )) + ->setBcc($bcc = array( + 'fabien-bcc@example.com' => 'Fabien (Bcc)', + 'chris-bcc@example.com' => 'Chris (Bcc)', + )) + ->setBody('...') + ; + + $plugin = new Swift_Plugins_RedirectingPlugin('god@example.com'); + + $evt = $this->_createSendEvent($message); + + $plugin->beforeSendPerformed($evt); + + $this->assertEquals($message->getTo(), array('god@example.com' => '')); + $this->assertEquals($message->getCc(), array()); + $this->assertEquals($message->getBcc(), array()); + + $plugin->sendPerformed($evt); + + $this->assertEquals($message->getTo(), array()); + $this->assertEquals($message->getCc(), $cc); + $this->assertEquals($message->getBcc(), $bcc); + } + + public function testPluginRespectsAWhitelistOfPatterns() + { + $message = Swift_Message::newInstance() + ->setSubject('...') + ->setFrom(array('john@example.com' => 'John Doe')) + ->setTo($to = array( + 'fabien-to@example.com' => 'Fabien (To)', + 'chris-to@example.com' => 'Chris (To)', + 'lars-to@internal.com' => 'Lars (To)', + )) + ->setCc($cc = array( + 'fabien-cc@example.com' => 'Fabien (Cc)', + 'chris-cc@example.com' => 'Chris (Cc)', + 'lars-cc@internal.org' => 'Lars (Cc)', + )) + ->setBcc($bcc = array( + 'fabien-bcc@example.com' => 'Fabien (Bcc)', + 'chris-bcc@example.com' => 'Chris (Bcc)', + 'john-bcc@example.org' => 'John (Bcc)', + )) + ->setBody('...') + ; + + $recipient = 'god@example.com'; + $patterns = array('/^.*@internal.[a-z]+$/', '/^john-.*$/'); + + $plugin = new Swift_Plugins_RedirectingPlugin($recipient, $patterns); + + $this->assertEquals($recipient, $plugin->getRecipient()); + $this->assertEquals($plugin->getWhitelist(), $patterns); + + $evt = $this->_createSendEvent($message); + + $plugin->beforeSendPerformed($evt); + + $this->assertEquals($message->getTo(), array('lars-to@internal.com' => 'Lars (To)', 'god@example.com' => null)); + $this->assertEquals($message->getCc(), array('lars-cc@internal.org' => 'Lars (Cc)')); + $this->assertEquals($message->getBcc(), array('john-bcc@example.org' => 'John (Bcc)')); + + $plugin->sendPerformed($evt); + + $this->assertEquals($message->getTo(), $to); + $this->assertEquals($message->getCc(), $cc); + $this->assertEquals($message->getBcc(), $bcc); + } + + public function testArrayOfRecipientsCanBeExplicitlyDefined() + { + $message = Swift_Message::newInstance() + ->setSubject('...') + ->setFrom(array('john@example.com' => 'John Doe')) + ->setTo(array( + 'fabien@example.com' => 'Fabien', + 'chris@example.com' => 'Chris (To)', + 'lars-to@internal.com' => 'Lars (To)', + )) + ->setCc(array( + 'fabien@example.com' => 'Fabien', + 'chris-cc@example.com' => 'Chris (Cc)', + 'lars-cc@internal.org' => 'Lars (Cc)', + )) + ->setBcc(array( + 'fabien@example.com' => 'Fabien', + 'chris-bcc@example.com' => 'Chris (Bcc)', + 'john-bcc@example.org' => 'John (Bcc)', + )) + ->setBody('...') + ; + + $recipients = array('god@example.com', 'fabien@example.com'); + $patterns = array('/^.*@internal.[a-z]+$/'); + + $plugin = new Swift_Plugins_RedirectingPlugin($recipients, $patterns); + + $evt = $this->_createSendEvent($message); + + $plugin->beforeSendPerformed($evt); + + $this->assertEquals( + $message->getTo(), + array('fabien@example.com' => 'Fabien', 'lars-to@internal.com' => 'Lars (To)', 'god@example.com' => null) + ); + $this->assertEquals( + $message->getCc(), + array('fabien@example.com' => 'Fabien', 'lars-cc@internal.org' => 'Lars (Cc)') + ); + $this->assertEquals($message->getBcc(), array('fabien@example.com' => 'Fabien')); + } + + // -- Creation Methods + + private function _createSendEvent(Swift_Mime_Message $message) + { + $evt = $this->getMockBuilder('Swift_Events_SendEvent') + ->disableOriginalConstructor() + ->getMock(); + $evt->expects($this->any()) + ->method('getMessage') + ->will($this->returnValue($message)); + + return $evt; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/ReporterPluginTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/ReporterPluginTest.php new file mode 100644 index 0000000000..8101883007 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/ReporterPluginTest.php @@ -0,0 +1,88 @@ +_createMessage(); + $evt = $this->_createSendEvent(); + $reporter = $this->_createReporter(); + + $message->shouldReceive('getTo')->zeroOrMoreTimes()->andReturn(array('foo@bar.tld' => 'Foo')); + $evt->shouldReceive('getMessage')->zeroOrMoreTimes()->andReturn($message); + $evt->shouldReceive('getFailedRecipients')->zeroOrMoreTimes()->andReturn(array()); + $reporter->shouldReceive('notify')->once()->with($message, 'foo@bar.tld', Swift_Plugins_Reporter::RESULT_PASS); + + $plugin = new Swift_Plugins_ReporterPlugin($reporter); + $plugin->sendPerformed($evt); + } + + public function testReportingFailedTo() + { + $message = $this->_createMessage(); + $evt = $this->_createSendEvent(); + $reporter = $this->_createReporter(); + + $message->shouldReceive('getTo')->zeroOrMoreTimes()->andReturn(array('foo@bar.tld' => 'Foo', 'zip@button' => 'Zip')); + $evt->shouldReceive('getMessage')->zeroOrMoreTimes()->andReturn($message); + $evt->shouldReceive('getFailedRecipients')->zeroOrMoreTimes()->andReturn(array('zip@button')); + $reporter->shouldReceive('notify')->once()->with($message, 'foo@bar.tld', Swift_Plugins_Reporter::RESULT_PASS); + $reporter->shouldReceive('notify')->once()->with($message, 'zip@button', Swift_Plugins_Reporter::RESULT_FAIL); + + $plugin = new Swift_Plugins_ReporterPlugin($reporter); + $plugin->sendPerformed($evt); + } + + public function testReportingFailedCc() + { + $message = $this->_createMessage(); + $evt = $this->_createSendEvent(); + $reporter = $this->_createReporter(); + + $message->shouldReceive('getTo')->zeroOrMoreTimes()->andReturn(array('foo@bar.tld' => 'Foo')); + $message->shouldReceive('getCc')->zeroOrMoreTimes()->andReturn(array('zip@button' => 'Zip', 'test@test.com' => 'Test')); + $evt->shouldReceive('getMessage')->zeroOrMoreTimes()->andReturn($message); + $evt->shouldReceive('getFailedRecipients')->zeroOrMoreTimes()->andReturn(array('zip@button')); + $reporter->shouldReceive('notify')->once()->with($message, 'foo@bar.tld', Swift_Plugins_Reporter::RESULT_PASS); + $reporter->shouldReceive('notify')->once()->with($message, 'zip@button', Swift_Plugins_Reporter::RESULT_FAIL); + $reporter->shouldReceive('notify')->once()->with($message, 'test@test.com', Swift_Plugins_Reporter::RESULT_PASS); + + $plugin = new Swift_Plugins_ReporterPlugin($reporter); + $plugin->sendPerformed($evt); + } + + public function testReportingFailedBcc() + { + $message = $this->_createMessage(); + $evt = $this->_createSendEvent(); + $reporter = $this->_createReporter(); + + $message->shouldReceive('getTo')->zeroOrMoreTimes()->andReturn(array('foo@bar.tld' => 'Foo')); + $message->shouldReceive('getBcc')->zeroOrMoreTimes()->andReturn(array('zip@button' => 'Zip', 'test@test.com' => 'Test')); + $evt->shouldReceive('getMessage')->zeroOrMoreTimes()->andReturn($message); + $evt->shouldReceive('getFailedRecipients')->zeroOrMoreTimes()->andReturn(array('zip@button')); + $reporter->shouldReceive('notify')->once()->with($message, 'foo@bar.tld', Swift_Plugins_Reporter::RESULT_PASS); + $reporter->shouldReceive('notify')->once()->with($message, 'zip@button', Swift_Plugins_Reporter::RESULT_FAIL); + $reporter->shouldReceive('notify')->once()->with($message, 'test@test.com', Swift_Plugins_Reporter::RESULT_PASS); + + $plugin = new Swift_Plugins_ReporterPlugin($reporter); + $plugin->sendPerformed($evt); + } + + // -- Creation Methods + + private function _createMessage() + { + return $this->getMockery('Swift_Mime_Message')->shouldIgnoreMissing(); + } + + private function _createSendEvent() + { + return $this->getMockery('Swift_Events_SendEvent')->shouldIgnoreMissing(); + } + + private function _createReporter() + { + return $this->getMockery('Swift_Plugins_Reporter')->shouldIgnoreMissing(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Reporters/HitReporterTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Reporters/HitReporterTest.php new file mode 100644 index 0000000000..33dcc750e2 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Reporters/HitReporterTest.php @@ -0,0 +1,64 @@ +_hitReporter = new Swift_Plugins_Reporters_HitReporter(); + $this->_message = $this->getMockBuilder('Swift_Mime_Message')->getMock(); + } + + public function testReportingFail() + { + $this->_hitReporter->notify($this->_message, 'foo@bar.tld', + Swift_Plugins_Reporter::RESULT_FAIL + ); + $this->assertEquals(array('foo@bar.tld'), + $this->_hitReporter->getFailedRecipients() + ); + } + + public function testMultipleReports() + { + $this->_hitReporter->notify($this->_message, 'foo@bar.tld', + Swift_Plugins_Reporter::RESULT_FAIL + ); + $this->_hitReporter->notify($this->_message, 'zip@button', + Swift_Plugins_Reporter::RESULT_FAIL + ); + $this->assertEquals(array('foo@bar.tld', 'zip@button'), + $this->_hitReporter->getFailedRecipients() + ); + } + + public function testReportingPassIsIgnored() + { + $this->_hitReporter->notify($this->_message, 'foo@bar.tld', + Swift_Plugins_Reporter::RESULT_FAIL + ); + $this->_hitReporter->notify($this->_message, 'zip@button', + Swift_Plugins_Reporter::RESULT_PASS + ); + $this->assertEquals(array('foo@bar.tld'), + $this->_hitReporter->getFailedRecipients() + ); + } + + public function testBufferCanBeCleared() + { + $this->_hitReporter->notify($this->_message, 'foo@bar.tld', + Swift_Plugins_Reporter::RESULT_FAIL + ); + $this->_hitReporter->notify($this->_message, 'zip@button', + Swift_Plugins_Reporter::RESULT_FAIL + ); + $this->assertEquals(array('foo@bar.tld', 'zip@button'), + $this->_hitReporter->getFailedRecipients() + ); + $this->_hitReporter->clear(); + $this->assertEquals(array(), $this->_hitReporter->getFailedRecipients()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Reporters/HtmlReporterTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Reporters/HtmlReporterTest.php new file mode 100644 index 0000000000..a6c6a277aa --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Reporters/HtmlReporterTest.php @@ -0,0 +1,54 @@ +_html = new Swift_Plugins_Reporters_HtmlReporter(); + $this->_message = $this->getMockBuilder('Swift_Mime_Message')->getMock(); + } + + public function testReportingPass() + { + ob_start(); + $this->_html->notify($this->_message, 'foo@bar.tld', + Swift_Plugins_Reporter::RESULT_PASS + ); + $html = ob_get_clean(); + + $this->assertRegExp('~ok|pass~i', $html, '%s: Reporter should indicate pass'); + $this->assertRegExp('~foo@bar\.tld~', $html, '%s: Reporter should show address'); + } + + public function testReportingFail() + { + ob_start(); + $this->_html->notify($this->_message, 'zip@button', + Swift_Plugins_Reporter::RESULT_FAIL + ); + $html = ob_get_clean(); + + $this->assertRegExp('~fail~i', $html, '%s: Reporter should indicate fail'); + $this->assertRegExp('~zip@button~', $html, '%s: Reporter should show address'); + } + + public function testMultipleReports() + { + ob_start(); + $this->_html->notify($this->_message, 'foo@bar.tld', + Swift_Plugins_Reporter::RESULT_PASS + ); + $this->_html->notify($this->_message, 'zip@button', + Swift_Plugins_Reporter::RESULT_FAIL + ); + $html = ob_get_clean(); + + $this->assertRegExp('~ok|pass~i', $html, '%s: Reporter should indicate pass'); + $this->assertRegExp('~foo@bar\.tld~', $html, '%s: Reporter should show address'); + $this->assertRegExp('~fail~i', $html, '%s: Reporter should indicate fail'); + $this->assertRegExp('~zip@button~', $html, '%s: Reporter should show address'); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/ThrottlerPluginTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/ThrottlerPluginTest.php new file mode 100644 index 0000000000..a50f14f9f0 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/ThrottlerPluginTest.php @@ -0,0 +1,104 @@ +_createSleeper(); + $timer = $this->_createTimer(); + + //10MB/min + $plugin = new Swift_Plugins_ThrottlerPlugin( + 10000000, Swift_Plugins_ThrottlerPlugin::BYTES_PER_MINUTE, + $sleeper, $timer + ); + + $timer->shouldReceive('getTimestamp')->once()->andReturn(0); + $timer->shouldReceive('getTimestamp')->once()->andReturn(1); //expected 0.6 + $timer->shouldReceive('getTimestamp')->once()->andReturn(1); //expected 1.2 (sleep 1) + $timer->shouldReceive('getTimestamp')->once()->andReturn(2); //expected 1.8 + $timer->shouldReceive('getTimestamp')->once()->andReturn(2); //expected 2.4 (sleep 1) + $sleeper->shouldReceive('sleep')->twice()->with(1); + + //10,000,000 bytes per minute + //100,000 bytes per email + + // .: (10,000,000/100,000)/60 emails per second = 1.667 emais/sec + + $message = $this->_createMessageWithByteCount(100000); //100KB + + $evt = $this->_createSendEvent($message); + + for ($i = 0; $i < 5; ++$i) { + $plugin->beforeSendPerformed($evt); + $plugin->sendPerformed($evt); + } + } + + public function testMessagesPerMinuteThrottling() + { + $sleeper = $this->_createSleeper(); + $timer = $this->_createTimer(); + + //60/min + $plugin = new Swift_Plugins_ThrottlerPlugin( + 60, Swift_Plugins_ThrottlerPlugin::MESSAGES_PER_MINUTE, + $sleeper, $timer + ); + + $timer->shouldReceive('getTimestamp')->once()->andReturn(0); + $timer->shouldReceive('getTimestamp')->once()->andReturn(0); //expected 1 (sleep 1) + $timer->shouldReceive('getTimestamp')->once()->andReturn(2); //expected 2 + $timer->shouldReceive('getTimestamp')->once()->andReturn(2); //expected 3 (sleep 1) + $timer->shouldReceive('getTimestamp')->once()->andReturn(4); //expected 4 + $sleeper->shouldReceive('sleep')->twice()->with(1); + + //60 messages per minute + //1 message per second + + $message = $this->_createMessageWithByteCount(10); + + $evt = $this->_createSendEvent($message); + + for ($i = 0; $i < 5; ++$i) { + $plugin->beforeSendPerformed($evt); + $plugin->sendPerformed($evt); + } + } + + // -- Creation Methods + + private function _createSleeper() + { + return $this->getMockery('Swift_Plugins_Sleeper'); + } + + private function _createTimer() + { + return $this->getMockery('Swift_Plugins_Timer'); + } + + private function _createMessageWithByteCount($bytes) + { + $msg = $this->getMockery('Swift_Mime_Message'); + $msg->shouldReceive('toByteStream') + ->zeroOrMoreTimes() + ->andReturnUsing(function ($is) use ($bytes) { + for ($i = 0; $i < $bytes; ++$i) { + $is->write('x'); + } + }); + + return $msg; + } + + private function _createSendEvent($message) + { + $evt = $this->getMockery('Swift_Events_SendEvent'); + $evt->shouldReceive('getMessage') + ->zeroOrMoreTimes() + ->andReturn($message); + + return $evt; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Signers/DKIMSignerTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Signers/DKIMSignerTest.php new file mode 100644 index 0000000000..13c1b4c1e4 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Signers/DKIMSignerTest.php @@ -0,0 +1,227 @@ +markTestSkipped( + 'skipping because of https://bugs.php.net/bug.php?id=61421' + ); + } + } + + public function testBasicSigningHeaderManipulation() + { + $headers = $this->_createHeaders(); + $messageContent = 'Hello World'; + $signer = new Swift_Signers_DKIMSigner(file_get_contents(dirname(dirname(dirname(__DIR__))).'/_samples/dkim/dkim.test.priv'), 'dummy.nxdomain.be', 'dummySelector'); + /* @var $signer Swift_Signers_HeaderSigner */ + $altered = $signer->getAlteredHeaders(); + $signer->reset(); + // Headers + $signer->setHeaders($headers); + // Body + $signer->startBody(); + $signer->write($messageContent); + $signer->endBody(); + // Signing + $signer->addSignature($headers); + } + + // Default Signing + public function testSigningDefaults() + { + $headerSet = $this->_createHeaderSet(); + $messageContent = 'Hello World'; + $signer = new Swift_Signers_DKIMSigner(file_get_contents(dirname(dirname(dirname(__DIR__))).'/_samples/dkim/dkim.test.priv'), 'dummy.nxdomain.be', 'dummySelector'); + $signer->setSignatureTimestamp('1299879181'); + $altered = $signer->getAlteredHeaders(); + $this->assertEquals(array('DKIM-Signature'), $altered); + $signer->reset(); + $signer->setHeaders($headerSet); + $this->assertFalse($headerSet->has('DKIM-Signature')); + $signer->startBody(); + $signer->write($messageContent); + $signer->endBody(); + $signer->addSignature($headerSet); + $this->assertTrue($headerSet->has('DKIM-Signature')); + $dkim = $headerSet->getAll('DKIM-Signature'); + $sig = reset($dkim); + $this->assertEquals($sig->getValue(), 'v=1; a=rsa-sha1; bh=wlbYcY9O9OPInGJ4D0E/rGsvMLE=; d=dummy.nxdomain.be; h=; i=@dummy.nxdomain.be; s=dummySelector; t=1299879181; b=RMSNelzM2O5MAAnMjT3G3/VF36S3DGJXoPCXR001F1WDReu0prGphWjuzK/m6V1pwqQL8cCNg Hi74mTx2bvyAvmkjvQtJf1VMUOCc9WHGcm1Yec66I3ZWoNMGSWZ1EKAm2CtTzyG0IFw4ml9DI wSkyAFxlgicckDD6FibhqwX4w='); + } + + // SHA256 Signing + public function testSigning256() + { + $headerSet = $this->_createHeaderSet(); + $messageContent = 'Hello World'; + $signer = new Swift_Signers_DKIMSigner(file_get_contents(dirname(dirname(dirname(__DIR__))).'/_samples/dkim/dkim.test.priv'), 'dummy.nxdomain.be', 'dummySelector'); + $signer->setHashAlgorithm('rsa-sha256'); + $signer->setSignatureTimestamp('1299879181'); + $altered = $signer->getAlteredHeaders(); + $this->assertEquals(array('DKIM-Signature'), $altered); + $signer->reset(); + $signer->setHeaders($headerSet); + $this->assertFalse($headerSet->has('DKIM-Signature')); + $signer->startBody(); + $signer->write($messageContent); + $signer->endBody(); + $signer->addSignature($headerSet); + $this->assertTrue($headerSet->has('DKIM-Signature')); + $dkim = $headerSet->getAll('DKIM-Signature'); + $sig = reset($dkim); + $this->assertEquals($sig->getValue(), 'v=1; a=rsa-sha256; bh=f+W+hu8dIhf2VAni89o8lF6WKTXi7nViA4RrMdpD5/U=; d=dummy.nxdomain.be; h=; i=@dummy.nxdomain.be; s=dummySelector; t=1299879181; b=jqPmieHzF5vR9F4mXCAkowuphpO4iJ8IAVuioh1BFZ3VITXZj5jlOFxULJMBiiApm2keJirnh u4mzogj444QkpT3lJg8/TBGAYQPdcvkG3KC0jdyN6QpSgpITBJG2BwWa+keXsv2bkQgLRAzNx qRhP45vpHCKun0Tg9LrwW/KCg='); + } + + // Relaxed/Relaxed Hash Signing + public function testSigningRelaxedRelaxed256() + { + $headerSet = $this->_createHeaderSet(); + $messageContent = 'Hello World'; + $signer = new Swift_Signers_DKIMSigner(file_get_contents(dirname(dirname(dirname(__DIR__))).'/_samples/dkim/dkim.test.priv'), 'dummy.nxdomain.be', 'dummySelector'); + $signer->setHashAlgorithm('rsa-sha256'); + $signer->setSignatureTimestamp('1299879181'); + $signer->setBodyCanon('relaxed'); + $signer->setHeaderCanon('relaxed'); + $altered = $signer->getAlteredHeaders(); + $this->assertEquals(array('DKIM-Signature'), $altered); + $signer->reset(); + $signer->setHeaders($headerSet); + $this->assertFalse($headerSet->has('DKIM-Signature')); + $signer->startBody(); + $signer->write($messageContent); + $signer->endBody(); + $signer->addSignature($headerSet); + $this->assertTrue($headerSet->has('DKIM-Signature')); + $dkim = $headerSet->getAll('DKIM-Signature'); + $sig = reset($dkim); + $this->assertEquals($sig->getValue(), 'v=1; a=rsa-sha256; bh=f+W+hu8dIhf2VAni89o8lF6WKTXi7nViA4RrMdpD5/U=; d=dummy.nxdomain.be; h=; i=@dummy.nxdomain.be; s=dummySelector; c=relaxed/relaxed; t=1299879181; b=gzOI+PX6HpZKQFzwwmxzcVJsyirdLXOS+4pgfCpVHQIdqYusKLrhlLeFBTNoz75HrhNvGH6T0 Rt3w5aTqkrWfUuAEYt0Ns14GowLM7JojaFN+pZ4eYnRB3CBBgW6fee4NEMD5WPca3uS09tr1E 10RYh9ILlRtl+84sovhx5id3Y='); + } + + // Relaxed/Simple Hash Signing + public function testSigningRelaxedSimple256() + { + $headerSet = $this->_createHeaderSet(); + $messageContent = 'Hello World'; + $signer = new Swift_Signers_DKIMSigner(file_get_contents(dirname(dirname(dirname(__DIR__))).'/_samples/dkim/dkim.test.priv'), 'dummy.nxdomain.be', 'dummySelector'); + $signer->setHashAlgorithm('rsa-sha256'); + $signer->setSignatureTimestamp('1299879181'); + $signer->setHeaderCanon('relaxed'); + $altered = $signer->getAlteredHeaders(); + $this->assertEquals(array('DKIM-Signature'), $altered); + $signer->reset(); + $signer->setHeaders($headerSet); + $this->assertFalse($headerSet->has('DKIM-Signature')); + $signer->startBody(); + $signer->write($messageContent); + $signer->endBody(); + $signer->addSignature($headerSet); + $this->assertTrue($headerSet->has('DKIM-Signature')); + $dkim = $headerSet->getAll('DKIM-Signature'); + $sig = reset($dkim); + $this->assertEquals($sig->getValue(), 'v=1; a=rsa-sha256; bh=f+W+hu8dIhf2VAni89o8lF6WKTXi7nViA4RrMdpD5/U=; d=dummy.nxdomain.be; h=; i=@dummy.nxdomain.be; s=dummySelector; c=relaxed; t=1299879181; b=dLPJNec5v81oelyzGOY0qPqTlGnQeNfUNBOrV/JKbStr3NqWGI9jH4JAe2YvO2V32lfPNoby1 4MMzZ6EPkaZkZDDSPa+53YbCPQAlqiD9QZZIUe2UNM33HN8yAMgiWEF5aP7MbQnxeVZMfVLEl 9S8qOImu+K5JZqhQQTL0dgLwA='); + } + + // Simple/Relaxed Hash Signing + public function testSigningSimpleRelaxed256() + { + $headerSet = $this->_createHeaderSet(); + $messageContent = 'Hello World'; + $signer = new Swift_Signers_DKIMSigner(file_get_contents(dirname(dirname(dirname(__DIR__))).'/_samples/dkim/dkim.test.priv'), 'dummy.nxdomain.be', 'dummySelector'); + $signer->setHashAlgorithm('rsa-sha256'); + $signer->setSignatureTimestamp('1299879181'); + $signer->setBodyCanon('relaxed'); + $altered = $signer->getAlteredHeaders(); + $this->assertEquals(array('DKIM-Signature'), $altered); + $signer->reset(); + $signer->setHeaders($headerSet); + $this->assertFalse($headerSet->has('DKIM-Signature')); + $signer->startBody(); + $signer->write($messageContent); + $signer->endBody(); + $signer->addSignature($headerSet); + $this->assertTrue($headerSet->has('DKIM-Signature')); + $dkim = $headerSet->getAll('DKIM-Signature'); + $sig = reset($dkim); + $this->assertEquals($sig->getValue(), 'v=1; a=rsa-sha256; bh=f+W+hu8dIhf2VAni89o8lF6WKTXi7nViA4RrMdpD5/U=; d=dummy.nxdomain.be; h=; i=@dummy.nxdomain.be; s=dummySelector; c=simple/relaxed; t=1299879181; b=M5eomH/zamyzix9kOes+6YLzQZxuJdBP4x3nP9zF2N26eMLG2/cBKbnNyqiOTDhJdYfWPbLIa 1CWnjST0j5p4CpeOkGYuiE+M4TWEZwhRmRWootlPO3Ii6XpbBJKFk1o9zviS7OmXblUUE4aqb yRSIMDhtLdCK5GlaCneFLN7RQ='); + } + + // -- Creation Methods + private function _createHeaderSet() + { + $cache = new Swift_KeyCache_ArrayKeyCache(new Swift_KeyCache_SimpleKeyCacheInputStream()); + $factory = new Swift_CharacterReaderFactory_SimpleCharacterReaderFactory(); + $contentEncoder = new Swift_Mime_ContentEncoder_Base64ContentEncoder(); + + $headerEncoder = new Swift_Mime_HeaderEncoder_QpHeaderEncoder(new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8')); + $paramEncoder = new Swift_Encoder_Rfc2231Encoder(new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8')); + $grammar = new Swift_Mime_Grammar(); + $headers = new Swift_Mime_SimpleHeaderSet(new Swift_Mime_SimpleHeaderFactory($headerEncoder, $paramEncoder, $grammar)); + + return $headers; + } + + /** + * @return Swift_Mime_Headers + */ + private function _createHeaders() + { + $x = 0; + $cache = new Swift_KeyCache_ArrayKeyCache(new Swift_KeyCache_SimpleKeyCacheInputStream()); + $factory = new Swift_CharacterReaderFactory_SimpleCharacterReaderFactory(); + $contentEncoder = new Swift_Mime_ContentEncoder_Base64ContentEncoder(); + + $headerEncoder = new Swift_Mime_HeaderEncoder_QpHeaderEncoder(new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8')); + $paramEncoder = new Swift_Encoder_Rfc2231Encoder(new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8')); + $grammar = new Swift_Mime_Grammar(); + $headerFactory = new Swift_Mime_SimpleHeaderFactory($headerEncoder, $paramEncoder, $grammar); + $headers = $this->getMockery('Swift_Mime_HeaderSet'); + + $headers->shouldReceive('listAll') + ->zeroOrMoreTimes() + ->andReturn(array('From', 'To', 'Date', 'Subject')); + $headers->shouldReceive('has') + ->zeroOrMoreTimes() + ->with('From') + ->andReturn(true); + $headers->shouldReceive('getAll') + ->zeroOrMoreTimes() + ->with('From') + ->andReturn(array($headerFactory->createMailboxHeader('From', 'test@test.test'))); + $headers->shouldReceive('has') + ->zeroOrMoreTimes() + ->with('To') + ->andReturn(true); + $headers->shouldReceive('getAll') + ->zeroOrMoreTimes() + ->with('To') + ->andReturn(array($headerFactory->createMailboxHeader('To', 'test@test.test'))); + $headers->shouldReceive('has') + ->zeroOrMoreTimes() + ->with('Date') + ->andReturn(true); + $headers->shouldReceive('getAll') + ->zeroOrMoreTimes() + ->with('Date') + ->andReturn(array($headerFactory->createTextHeader('Date', 'Fri, 11 Mar 2011 20:56:12 +0000 (GMT)'))); + $headers->shouldReceive('has') + ->zeroOrMoreTimes() + ->with('Subject') + ->andReturn(true); + $headers->shouldReceive('getAll') + ->zeroOrMoreTimes() + ->with('Subject') + ->andReturn(array($headerFactory->createTextHeader('Subject', 'Foo Bar Text Message'))); + $headers->shouldReceive('addTextHeader') + ->zeroOrMoreTimes() + ->with('DKIM-Signature', \Mockery::any()) + ->andReturn(true); + $headers->shouldReceive('getAll') + ->zeroOrMoreTimes() + ->with('DKIM-Signature') + ->andReturn(array($headerFactory->createTextHeader('DKIM-Signature', 'Foo Bar Text Message'))); + + return $headers; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Signers/OpenDKIMSignerTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Signers/OpenDKIMSignerTest.php new file mode 100644 index 0000000000..00e48c139c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Signers/OpenDKIMSignerTest.php @@ -0,0 +1,45 @@ +markTestSkipped( + 'Need OpenDKIM extension run these tests.' + ); + } + } + + public function testBasicSigningHeaderManipulation() + { + } + + // Default Signing + public function testSigningDefaults() + { + } + + // SHA256 Signing + public function testSigning256() + { + } + + // Relaxed/Relaxed Hash Signing + public function testSigningRelaxedRelaxed256() + { + } + + // Relaxed/Simple Hash Signing + public function testSigningRelaxedSimple256() + { + } + + // Simple/Relaxed Hash Signing + public function testSigningSimpleRelaxed256() + { + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Signers/SMimeSignerTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Signers/SMimeSignerTest.php new file mode 100644 index 0000000000..df745ef2dc --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Signers/SMimeSignerTest.php @@ -0,0 +1,554 @@ +replacementFactory = Swift_DependencyContainer::getInstance() + ->lookup('transport.replacementfactory'); + + $this->samplesDir = str_replace('\\', '/', realpath(__DIR__.'/../../../_samples/')).'/'; + } + + public function testUnSingedMessage() + { + $message = Swift_SignedMessage::newInstance('Wonderful Subject') + ->setFrom(array('john@doe.com' => 'John Doe')) + ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name')) + ->setBody('Here is the message itself'); + + $this->assertEquals('Here is the message itself', $message->getBody()); + } + + public function testSingedMessage() + { + $message = Swift_SignedMessage::newInstance('Wonderful Subject') + ->setFrom(array('john@doe.com' => 'John Doe')) + ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name')) + ->setBody('Here is the message itself'); + + $signer = new Swift_Signers_SMimeSigner(); + $signer->setSignCertificate($this->samplesDir.'smime/sign.crt', $this->samplesDir.'smime/sign.key'); + $message->attachSigner($signer); + + $messageStream = $this->newFilteredStream(); + $message->toByteStream($messageStream); + $messageStream->commit(); + + $entityString = $messageStream->getContent(); + $headers = self::getHeadersOfMessage($entityString); + + if (!($boundary = $this->getBoundary($headers['content-type']))) { + return false; + } + + $expectedBody = <<assertValidVerify($expectedBody, $messageStream); + unset($messageStream); + } + + public function testSingedMessageExtraCerts() + { + $message = Swift_SignedMessage::newInstance('Wonderful Subject') + ->setFrom(array('john@doe.com' => 'John Doe')) + ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name')) + ->setBody('Here is the message itself'); + + $signer = new Swift_Signers_SMimeSigner(); + $signer->setSignCertificate($this->samplesDir.'smime/sign2.crt', $this->samplesDir.'smime/sign2.key', PKCS7_DETACHED, $this->samplesDir.'smime/intermediate.crt'); + $message->attachSigner($signer); + + $messageStream = $this->newFilteredStream(); + $message->toByteStream($messageStream); + $messageStream->commit(); + + $entityString = $messageStream->getContent(); + $headers = self::getHeadersOfMessage($entityString); + + if (!($boundary = $this->getBoundary($headers['content-type']))) { + return false; + } + + $expectedBody = <<assertValidVerify($expectedBody, $messageStream); + unset($messageStream); + } + + public function testSingedMessageBinary() + { + $message = Swift_SignedMessage::newInstance('Wonderful Subject') + ->setFrom(array('john@doe.com' => 'John Doe')) + ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name')) + ->setBody('Here is the message itself'); + + $signer = new Swift_Signers_SMimeSigner(); + $signer->setSignCertificate($this->samplesDir.'smime/sign.crt', $this->samplesDir.'smime/sign.key', PKCS7_BINARY); + $message->attachSigner($signer); + + $messageStream = $this->newFilteredStream(); + $message->toByteStream($messageStream); + $messageStream->commit(); + + $entityString = $messageStream->getContent(); + $headers = self::getHeadersOfMessage($entityString); + + if (!preg_match('#^application/(x\-)?pkcs7-mime; smime-type=signed\-data;#', $headers['content-type'])) { + $this->fail('Content-type does not match.'); + + return false; + } + + $this->assertEquals($headers['content-transfer-encoding'], 'base64'); + $this->assertEquals($headers['content-disposition'], 'attachment; filename="smime.p7m"'); + + $expectedBody = '(?:^[a-zA-Z0-9\/\\r\\n+]*={0,2})'; + + $messageStreamClean = $this->newFilteredStream(); + + $this->assertValidVerify($expectedBody, $messageStream); + unset($messageStreamClean, $messageStream); + } + + public function testSingedMessageWithAttachments() + { + $message = Swift_SignedMessage::newInstance('Wonderful Subject') + ->setFrom(array('john@doe.com' => 'John Doe')) + ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name')) + ->setBody('Here is the message itself'); + + $message->attach(Swift_Attachment::fromPath($this->samplesDir.'/files/textfile.zip')); + + $signer = new Swift_Signers_SMimeSigner(); + $signer->setSignCertificate($this->samplesDir.'smime/sign.crt', $this->samplesDir.'smime/sign.key'); + $message->attachSigner($signer); + + $messageStream = $this->newFilteredStream(); + $message->toByteStream($messageStream); + $messageStream->commit(); + + $entityString = $messageStream->getContent(); + $headers = self::getHeadersOfMessage($entityString); + + if (!($boundary = $this->getBoundary($headers['content-type']))) { + return false; + } + + $expectedBody = <<assertValidVerify($expectedBody, $messageStream); + unset($messageStream); + } + + public function testEncryptedMessage() + { + $message = Swift_SignedMessage::newInstance('Wonderful Subject') + ->setFrom(array('john@doe.com' => 'John Doe')) + ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name')) + ->setBody('Here is the message itself'); + + $originalMessage = $this->cleanMessage($message->toString()); + + $signer = new Swift_Signers_SMimeSigner(); + $signer->setEncryptCertificate($this->samplesDir.'smime/encrypt.crt'); + $message->attachSigner($signer); + + $messageStream = new Swift_ByteStream_TemporaryFileByteStream(); + $message->toByteStream($messageStream); + $messageStream->commit(); + + $entityString = $messageStream->getContent(); + $headers = self::getHeadersOfMessage($entityString); + + if (!preg_match('#^application/(x\-)?pkcs7-mime; smime-type=enveloped\-data;#', $headers['content-type'])) { + $this->fail('Content-type does not match.'); + + return false; + } + + $expectedBody = '(?:^[a-zA-Z0-9\/\\r\\n+]*={0,2})'; + + $decryptedMessageStream = new Swift_ByteStream_TemporaryFileByteStream(); + + if (!openssl_pkcs7_decrypt($messageStream->getPath(), $decryptedMessageStream->getPath(), 'file://'.$this->samplesDir.'smime/encrypt.crt', array('file://'.$this->samplesDir.'smime/encrypt.key', 'swift'))) { + $this->fail(sprintf('Decrypt of the message failed. Internal error "%s".', openssl_error_string())); + } + + $this->assertEquals($originalMessage, $decryptedMessageStream->getContent()); + unset($decryptedMessageStream, $messageStream); + } + + public function testEncryptedMessageWithMultipleCerts() + { + $message = Swift_SignedMessage::newInstance('Wonderful Subject') + ->setFrom(array('john@doe.com' => 'John Doe')) + ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name')) + ->setBody('Here is the message itself'); + + $originalMessage = $this->cleanMessage($message->toString()); + + $signer = new Swift_Signers_SMimeSigner(); + $signer->setEncryptCertificate(array($this->samplesDir.'smime/encrypt.crt', $this->samplesDir.'smime/encrypt2.crt')); + $message->attachSigner($signer); + + $messageStream = new Swift_ByteStream_TemporaryFileByteStream(); + $message->toByteStream($messageStream); + $messageStream->commit(); + + $entityString = $messageStream->getContent(); + $headers = self::getHeadersOfMessage($entityString); + + if (!preg_match('#^application/(x\-)?pkcs7-mime; smime-type=enveloped\-data;#', $headers['content-type'])) { + $this->fail('Content-type does not match.'); + + return false; + } + + $expectedBody = '(?:^[a-zA-Z0-9\/\\r\\n+]*={0,2})'; + + $decryptedMessageStream = new Swift_ByteStream_TemporaryFileByteStream(); + + if (!openssl_pkcs7_decrypt($messageStream->getPath(), $decryptedMessageStream->getPath(), 'file://'.$this->samplesDir.'smime/encrypt.crt', array('file://'.$this->samplesDir.'smime/encrypt.key', 'swift'))) { + $this->fail(sprintf('Decrypt of the message failed. Internal error "%s".', openssl_error_string())); + } + + $this->assertEquals($originalMessage, $decryptedMessageStream->getContent()); + unset($decryptedMessageStream); + + $decryptedMessageStream = new Swift_ByteStream_TemporaryFileByteStream(); + + if (!openssl_pkcs7_decrypt($messageStream->getPath(), $decryptedMessageStream->getPath(), 'file://'.$this->samplesDir.'smime/encrypt2.crt', array('file://'.$this->samplesDir.'smime/encrypt2.key', 'swift'))) { + $this->fail(sprintf('Decrypt of the message failed. Internal error "%s".', openssl_error_string())); + } + + $this->assertEquals($originalMessage, $decryptedMessageStream->getContent()); + unset($decryptedMessageStream, $messageStream); + } + + public function testSignThenEncryptedMessage() + { + $message = Swift_SignedMessage::newInstance('Wonderful Subject') + ->setFrom(array('john@doe.com' => 'John Doe')) + ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name')) + ->setBody('Here is the message itself'); + + $signer = new Swift_Signers_SMimeSigner(); + $signer->setSignCertificate($this->samplesDir.'smime/sign.crt', $this->samplesDir.'smime/sign.key'); + $signer->setEncryptCertificate($this->samplesDir.'smime/encrypt.crt'); + $message->attachSigner($signer); + + $messageStream = new Swift_ByteStream_TemporaryFileByteStream(); + $message->toByteStream($messageStream); + $messageStream->commit(); + + $entityString = $messageStream->getContent(); + $headers = self::getHeadersOfMessage($entityString); + + if (!preg_match('#^application/(x\-)?pkcs7-mime; smime-type=enveloped\-data;#', $headers['content-type'])) { + $this->fail('Content-type does not match.'); + + return false; + } + + $expectedBody = '(?:^[a-zA-Z0-9\/\\r\\n+]*={0,2})'; + + $decryptedMessageStream = new Swift_ByteStream_TemporaryFileByteStream(); + + if (!openssl_pkcs7_decrypt($messageStream->getPath(), $decryptedMessageStream->getPath(), 'file://'.$this->samplesDir.'smime/encrypt.crt', array('file://'.$this->samplesDir.'smime/encrypt.key', 'swift'))) { + $this->fail(sprintf('Decrypt of the message failed. Internal error "%s".', openssl_error_string())); + } + + $entityString = $decryptedMessageStream->getContent(); + $headers = self::getHeadersOfMessage($entityString); + + if (!($boundary = $this->getBoundary($headers['content-type']))) { + return false; + } + + $expectedBody = <<assertValidVerify($expectedBody, $decryptedMessageStream)) { + return false; + } + + unset($decryptedMessageStream, $messageStream); + } + + public function testEncryptThenSignMessage() + { + $message = Swift_SignedMessage::newInstance('Wonderful Subject') + ->setFrom(array('john@doe.com' => 'John Doe')) + ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name')) + ->setBody('Here is the message itself'); + + $originalMessage = $this->cleanMessage($message->toString()); + + $signer = Swift_Signers_SMimeSigner::newInstance(); + $signer->setSignCertificate($this->samplesDir.'smime/sign.crt', $this->samplesDir.'smime/sign.key'); + $signer->setEncryptCertificate($this->samplesDir.'smime/encrypt.crt'); + $signer->setSignThenEncrypt(false); + $message->attachSigner($signer); + + $messageStream = $this->newFilteredStream(); + $message->toByteStream($messageStream); + $messageStream->commit(); + + $entityString = $messageStream->getContent(); + $headers = self::getHeadersOfMessage($entityString); + + if (!($boundary = $this->getBoundary($headers['content-type']))) { + return false; + } + + $expectedBody = <<MIME-Version: 1\.0 +Content-Disposition: attachment; filename="smime\.p7m" +Content-Type: application/(x\-)?pkcs7-mime; smime-type=enveloped-data; name="smime\.p7m" +Content-Transfer-Encoding: base64 + +(?:^[a-zA-Z0-9\/\\r\\n+]*={0,2}) + + +)--$boundary +Content-Type: application/(x\-)?pkcs7-signature; name="smime\.p7s" +Content-Transfer-Encoding: base64 +Content-Disposition: attachment; filename="smime\.p7s" + +(?:^[a-zA-Z0-9\/\\r\\n+]*={0,2}) + +--$boundary-- +OEL; + + if (!$this->assertValidVerify($expectedBody, $messageStream)) { + return false; + } + + $expectedBody = str_replace("\n", "\r\n", $expectedBody); + if (!preg_match('%'.$expectedBody.'*%m', $entityString, $entities)) { + $this->fail('Failed regex match.'); + + return false; + } + + $messageStreamClean = new Swift_ByteStream_TemporaryFileByteStream(); + $messageStreamClean->write($entities['encrypted_message']); + + $decryptedMessageStream = new Swift_ByteStream_TemporaryFileByteStream(); + + if (!openssl_pkcs7_decrypt($messageStreamClean->getPath(), $decryptedMessageStream->getPath(), 'file://'.$this->samplesDir.'smime/encrypt.crt', array('file://'.$this->samplesDir.'smime/encrypt.key', 'swift'))) { + $this->fail(sprintf('Decrypt of the message failed. Internal error "%s".', openssl_error_string())); + } + + $this->assertEquals($originalMessage, $decryptedMessageStream->getContent()); + unset($messageStreamClean, $messageStream, $decryptedMessageStream); + } + + protected function assertValidVerify($expected, Swift_ByteStream_TemporaryFileByteStream $messageStream) + { + $actual = $messageStream->getContent(); + + // File is UNIX encoded so convert them to correct line ending + $expected = str_replace("\n", "\r\n", $expected); + + $actual = trim(self::getBodyOfMessage($actual)); + if (!$this->assertRegExp('%^'.$expected.'$\s*%m', $actual)) { + return false; + } + + $opensslOutput = new Swift_ByteStream_TemporaryFileByteStream(); + $verify = openssl_pkcs7_verify($messageStream->getPath(), null, $opensslOutput->getPath(), array($this->samplesDir.'smime/ca.crt')); + + if (false === $verify) { + $this->fail('Verification of the message failed.'); + + return false; + } elseif (-1 === $verify) { + $this->fail(sprintf('Verification of the message failed. Internal error "%s".', openssl_error_string())); + + return false; + } + + return true; + } + + protected function getBoundary($contentType) + { + if (!preg_match('/boundary=("[^"]+"|(?:[^\s]+|$))/is', $contentType, $contentTypeData)) { + $this->fail('Failed to find Boundary parameter'); + + return false; + } + + return trim($contentTypeData[1], '"'); + } + + protected function newFilteredStream() + { + $messageStream = new Swift_ByteStream_TemporaryFileByteStream(); + $messageStream->addFilter($this->replacementFactory->createFilter("\r\n", "\n"), 'CRLF to LF'); + $messageStream->addFilter($this->replacementFactory->createFilter("\n", "\r\n"), 'LF to CRLF'); + + return $messageStream; + } + + protected static function getBodyOfMessage($message) + { + return substr($message, strpos($message, "\r\n\r\n")); + } + + /** + * Strips of the sender headers and Mime-Version. + * + * @param Swift_ByteStream_TemporaryFileByteStream $messageStream + * @param Swift_ByteStream_TemporaryFileByteStream $inputStream + */ + protected function cleanMessage($content) + { + $newContent = ''; + + $headers = self::getHeadersOfMessage($content); + foreach ($headers as $headerName => $value) { + if (!in_array($headerName, array('content-type', 'content-transfer-encoding', 'content-disposition'))) { + continue; + } + + $headerName = explode('-', $headerName); + $headerName = array_map('ucfirst', $headerName); + $headerName = implode('-', $headerName); + + if (strlen($value) > 62) { + $value = wordwrap($value, 62, "\n "); + } + + $newContent .= "$headerName: $value\r\n"; + } + + return $newContent."\r\n".ltrim(self::getBodyOfMessage($content)); + } + + /** + * Returns the headers of the message. + * + * Header-names are lowercase. + * + * @param string $message + * + * @return array + */ + protected static function getHeadersOfMessage($message) + { + $headersPosEnd = strpos($message, "\r\n\r\n"); + $headerData = substr($message, 0, $headersPosEnd); + $headerLines = explode("\r\n", $headerData); + + if (empty($headerLines)) { + return array(); + } + + $headers = array(); + + foreach ($headerLines as $headerLine) { + if (ctype_space($headerLines[0]) || false === strpos($headerLine, ':')) { + $headers[$currentHeaderName] .= ' '.trim($headerLine); + continue; + } + + $header = explode(':', $headerLine, 2); + $currentHeaderName = strtolower($header[0]); + $headers[$currentHeaderName] = trim($header[1]); + } + + return $headers; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/StreamFilters/ByteArrayReplacementFilterTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/StreamFilters/ByteArrayReplacementFilterTest.php new file mode 100644 index 0000000000..e4d4f48d80 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/StreamFilters/ByteArrayReplacementFilterTest.php @@ -0,0 +1,131 @@ +_createFilter(array(0x61, 0x62), array(0x63, 0x64)); + $this->assertEquals( + array(0x59, 0x60, 0x63, 0x64, 0x65), + $filter->filter(array(0x59, 0x60, 0x61, 0x62, 0x65)) + ); + } + + public function testShouldBufferReturnsTrueIfPartialMatchAtEndOfBuffer() + { + $filter = $this->_createFilter(array(0x61, 0x62), array(0x63, 0x64)); + $this->assertTrue($filter->shouldBuffer(array(0x59, 0x60, 0x61)), + '%s: Filter should buffer since 0x61 0x62 is the needle and the ending '. + '0x61 could be from 0x61 0x62' + ); + } + + public function testFilterCanMakeMultipleReplacements() + { + $filter = $this->_createFilter(array(array(0x61), array(0x62)), array(0x63)); + $this->assertEquals( + array(0x60, 0x63, 0x60, 0x63, 0x60), + $filter->filter(array(0x60, 0x61, 0x60, 0x62, 0x60)) + ); + } + + public function testMultipleReplacementsCanBeDifferent() + { + $filter = $this->_createFilter(array(array(0x61), array(0x62)), array(array(0x63), array(0x64))); + $this->assertEquals( + array(0x60, 0x63, 0x60, 0x64, 0x60), + $filter->filter(array(0x60, 0x61, 0x60, 0x62, 0x60)) + ); + } + + public function testShouldBufferReturnsFalseIfPartialMatchNotAtEndOfString() + { + $filter = $this->_createFilter(array(0x0D, 0x0A), array(0x0A)); + $this->assertFalse($filter->shouldBuffer(array(0x61, 0x62, 0x0D, 0x0A, 0x63)), + '%s: Filter should not buffer since x0Dx0A is the needle and is not at EOF' + ); + } + + public function testShouldBufferReturnsTrueIfAnyOfMultipleMatchesAtEndOfString() + { + $filter = $this->_createFilter(array(array(0x61, 0x62), array(0x63)), array(0x64)); + $this->assertTrue($filter->shouldBuffer(array(0x59, 0x60, 0x61)), + '%s: Filter should buffer since 0x61 0x62 is a needle and the ending '. + '0x61 could be from 0x61 0x62' + ); + } + + public function testConvertingAllLineEndingsToCRLFWhenInputIsLF() + { + $filter = $this->_createFilter( + array(array(0x0D, 0x0A), array(0x0D), array(0x0A)), + array(array(0x0A), array(0x0A), array(0x0D, 0x0A)) + ); + + $this->assertEquals( + array(0x60, 0x0D, 0x0A, 0x61, 0x0D, 0x0A, 0x62, 0x0D, 0x0A, 0x63), + $filter->filter(array(0x60, 0x0A, 0x61, 0x0A, 0x62, 0x0A, 0x63)) + ); + } + + public function testConvertingAllLineEndingsToCRLFWhenInputIsCR() + { + $filter = $this->_createFilter( + array(array(0x0D, 0x0A), array(0x0D), array(0x0A)), + array(array(0x0A), array(0x0A), array(0x0D, 0x0A)) + ); + + $this->assertEquals( + array(0x60, 0x0D, 0x0A, 0x61, 0x0D, 0x0A, 0x62, 0x0D, 0x0A, 0x63), + $filter->filter(array(0x60, 0x0D, 0x61, 0x0D, 0x62, 0x0D, 0x63)) + ); + } + + public function testConvertingAllLineEndingsToCRLFWhenInputIsCRLF() + { + $filter = $this->_createFilter( + array(array(0x0D, 0x0A), array(0x0D), array(0x0A)), + array(array(0x0A), array(0x0A), array(0x0D, 0x0A)) + ); + + $this->assertEquals( + array(0x60, 0x0D, 0x0A, 0x61, 0x0D, 0x0A, 0x62, 0x0D, 0x0A, 0x63), + $filter->filter(array(0x60, 0x0D, 0x0A, 0x61, 0x0D, 0x0A, 0x62, 0x0D, 0x0A, 0x63)) + ); + } + + public function testConvertingAllLineEndingsToCRLFWhenInputIsLFCR() + { + $filter = $this->_createFilter( + array(array(0x0D, 0x0A), array(0x0D), array(0x0A)), + array(array(0x0A), array(0x0A), array(0x0D, 0x0A)) + ); + + $this->assertEquals( + array(0x60, 0x0D, 0x0A, 0x0D, 0x0A, 0x61, 0x0D, 0x0A, 0x0D, 0x0A, 0x62, 0x0D, 0x0A, 0x0D, 0x0A, 0x63), + $filter->filter(array(0x60, 0x0A, 0x0D, 0x61, 0x0A, 0x0D, 0x62, 0x0A, 0x0D, 0x63)) + ); + } + + public function testConvertingAllLineEndingsToCRLFWhenInputContainsLFLF() + { + //Lighthouse Bug #23 + + $filter = $this->_createFilter( + array(array(0x0D, 0x0A), array(0x0D), array(0x0A)), + array(array(0x0A), array(0x0A), array(0x0D, 0x0A)) + ); + + $this->assertEquals( + array(0x60, 0x0D, 0x0A, 0x0D, 0x0A, 0x61, 0x0D, 0x0A, 0x0D, 0x0A, 0x62, 0x0D, 0x0A, 0x0D, 0x0A, 0x63), + $filter->filter(array(0x60, 0x0A, 0x0A, 0x61, 0x0A, 0x0A, 0x62, 0x0A, 0x0A, 0x63)) + ); + } + + // -- Creation methods + + private function _createFilter($search, $replace) + { + return new Swift_StreamFilters_ByteArrayReplacementFilter($search, $replace); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/StreamFilters/StringReplacementFilterFactoryTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/StreamFilters/StringReplacementFilterFactoryTest.php new file mode 100644 index 0000000000..bd44f5c74b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/StreamFilters/StringReplacementFilterFactoryTest.php @@ -0,0 +1,38 @@ +_createFactory(); + $this->assertInstanceof( + 'Swift_StreamFilters_StringReplacementFilter', + $factory->createFilter('a', 'b') + ); + } + + public function testSameInstancesAreCached() + { + $factory = $this->_createFactory(); + $filter1 = $factory->createFilter('a', 'b'); + $filter2 = $factory->createFilter('a', 'b'); + $this->assertSame($filter1, $filter2, '%s: Instances should be cached'); + } + + public function testDifferingInstancesAreNotCached() + { + $factory = $this->_createFactory(); + $filter1 = $factory->createFilter('a', 'b'); + $filter2 = $factory->createFilter('a', 'c'); + $this->assertNotEquals($filter1, $filter2, + '%s: Differing instances should not be cached' + ); + } + + // -- Creation methods + + private function _createFactory() + { + return new Swift_StreamFilters_StringReplacementFilterFactory(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/StreamFilters/StringReplacementFilterTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/StreamFilters/StringReplacementFilterTest.php new file mode 100644 index 0000000000..7a98a2f273 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/StreamFilters/StringReplacementFilterTest.php @@ -0,0 +1,55 @@ +_createFilter('foo', 'bar'); + $this->assertEquals('XbarYbarZ', $filter->filter('XfooYfooZ')); + } + + public function testShouldBufferReturnsTrueIfPartialMatchAtEndOfBuffer() + { + $filter = $this->_createFilter('foo', 'bar'); + $this->assertTrue($filter->shouldBuffer('XfooYf'), + '%s: Filter should buffer since "foo" is the needle and the ending '. + '"f" could be from "foo"' + ); + } + + public function testFilterCanMakeMultipleReplacements() + { + $filter = $this->_createFilter(array('a', 'b'), 'foo'); + $this->assertEquals('XfooYfooZ', $filter->filter('XaYbZ')); + } + + public function testMultipleReplacementsCanBeDifferent() + { + $filter = $this->_createFilter(array('a', 'b'), array('foo', 'zip')); + $this->assertEquals('XfooYzipZ', $filter->filter('XaYbZ')); + } + + public function testShouldBufferReturnsFalseIfPartialMatchNotAtEndOfString() + { + $filter = $this->_createFilter("\r\n", "\n"); + $this->assertFalse($filter->shouldBuffer("foo\r\nbar"), + '%s: Filter should not buffer since x0Dx0A is the needle and is not at EOF' + ); + } + + public function testShouldBufferReturnsTrueIfAnyOfMultipleMatchesAtEndOfString() + { + $filter = $this->_createFilter(array('foo', 'zip'), 'bar'); + $this->assertTrue($filter->shouldBuffer('XfooYzi'), + '%s: Filter should buffer since "zip" is a needle and the ending '. + '"zi" could be from "zip"' + ); + } + + // -- Creation methods + + private function _createFilter($search, $replace) + { + return new Swift_StreamFilters_StringReplacementFilter($search, $replace); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/AbstractSmtpEventSupportTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/AbstractSmtpEventSupportTest.php new file mode 100644 index 0000000000..121aaba09c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/AbstractSmtpEventSupportTest.php @@ -0,0 +1,560 @@ +_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $listener = $this->getMockery('Swift_Events_EventListener'); + $smtp = $this->_getTransport($buf, $dispatcher); + $dispatcher->shouldReceive('bindEventListener') + ->once() + ->with($listener); + + $smtp->registerPlugin($listener); + } + + public function testSendingDispatchesBeforeSendEvent() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $message = $this->_createMessage(); + $smtp = $this->_getTransport($buf, $dispatcher); + $evt = $this->getMockery('Swift_Events_SendEvent')->shouldIgnoreMissing(); + + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(array('chris@swiftmailer.org' => null)); + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('mark@swiftmailer.org' => 'Mark')); + $dispatcher->shouldReceive('createSendEvent') + ->once() + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'beforeSendPerformed'); + $dispatcher->shouldReceive('dispatchEvent') + ->zeroOrMoreTimes(); + $evt->shouldReceive('bubbleCancelled') + ->zeroOrMoreTimes() + ->andReturn(false); + + $this->_finishBuffer($buf); + $smtp->start(); + $this->assertEquals(1, $smtp->send($message)); + } + + public function testSendingDispatchesSendEvent() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $message = $this->_createMessage(); + $smtp = $this->_getTransport($buf, $dispatcher); + $evt = $this->getMockery('Swift_Events_SendEvent')->shouldIgnoreMissing(); + + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(array('chris@swiftmailer.org' => null)); + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('mark@swiftmailer.org' => 'Mark')); + $dispatcher->shouldReceive('createSendEvent') + ->once() + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'sendPerformed'); + $dispatcher->shouldReceive('dispatchEvent') + ->zeroOrMoreTimes(); + $evt->shouldReceive('bubbleCancelled') + ->zeroOrMoreTimes() + ->andReturn(false); + + $this->_finishBuffer($buf); + $smtp->start(); + $this->assertEquals(1, $smtp->send($message)); + } + + public function testSendEventCapturesFailures() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_SendEvent')->shouldIgnoreMissing(); + $smtp = $this->_getTransport($buf, $dispatcher); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(array('chris@swiftmailer.org' => null)); + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('mark@swiftmailer.org' => 'Mark')); + $buf->shouldReceive('write') + ->once() + ->with("MAIL FROM:\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250 OK\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn("500 Not now\r\n"); + $dispatcher->shouldReceive('createSendEvent') + ->zeroOrMoreTimes() + ->with($smtp, \Mockery::any()) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'sendPerformed'); + $dispatcher->shouldReceive('dispatchEvent') + ->zeroOrMoreTimes(); + $evt->shouldReceive('bubbleCancelled') + ->zeroOrMoreTimes() + ->andReturn(false); + $evt->shouldReceive('setFailedRecipients') + ->once() + ->with(array('mark@swiftmailer.org')); + + $this->_finishBuffer($buf); + $smtp->start(); + $this->assertEquals(0, $smtp->send($message)); + } + + public function testSendEventHasResultFailedIfAllFailures() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_SendEvent')->shouldIgnoreMissing(); + $smtp = $this->_getTransport($buf, $dispatcher); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(array('chris@swiftmailer.org' => null)); + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('mark@swiftmailer.org' => 'Mark')); + $buf->shouldReceive('write') + ->once() + ->with("MAIL FROM:\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250 OK\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn("500 Not now\r\n"); + $dispatcher->shouldReceive('createSendEvent') + ->zeroOrMoreTimes() + ->with($smtp, \Mockery::any()) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'sendPerformed'); + $dispatcher->shouldReceive('dispatchEvent') + ->zeroOrMoreTimes(); + $evt->shouldReceive('bubbleCancelled') + ->zeroOrMoreTimes() + ->andReturn(false); + $evt->shouldReceive('setResult') + ->once() + ->with(Swift_Events_SendEvent::RESULT_FAILED); + + $this->_finishBuffer($buf); + $smtp->start(); + $this->assertEquals(0, $smtp->send($message)); + } + + public function testSendEventHasResultTentativeIfSomeFailures() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_SendEvent')->shouldIgnoreMissing(); + $smtp = $this->_getTransport($buf, $dispatcher); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(array('chris@swiftmailer.org' => null)); + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array( + 'mark@swiftmailer.org' => 'Mark', + 'chris@site.tld' => 'Chris', + )); + $buf->shouldReceive('write') + ->once() + ->with("MAIL FROM:\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250 OK\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn("500 Not now\r\n"); + $dispatcher->shouldReceive('createSendEvent') + ->zeroOrMoreTimes() + ->with($smtp, \Mockery::any()) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'sendPerformed'); + $dispatcher->shouldReceive('dispatchEvent') + ->zeroOrMoreTimes(); + $evt->shouldReceive('bubbleCancelled') + ->zeroOrMoreTimes() + ->andReturn(false); + $evt->shouldReceive('setResult') + ->once() + ->with(Swift_Events_SendEvent::RESULT_TENTATIVE); + + $this->_finishBuffer($buf); + $smtp->start(); + $this->assertEquals(1, $smtp->send($message)); + } + + public function testSendEventHasResultSuccessIfNoFailures() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_SendEvent')->shouldIgnoreMissing(); + $smtp = $this->_getTransport($buf, $dispatcher); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(array('chris@swiftmailer.org' => null)); + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array( + 'mark@swiftmailer.org' => 'Mark', + 'chris@site.tld' => 'Chris', + )); + $dispatcher->shouldReceive('createSendEvent') + ->zeroOrMoreTimes() + ->with($smtp, \Mockery::any()) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'sendPerformed'); + $dispatcher->shouldReceive('dispatchEvent') + ->zeroOrMoreTimes(); + $evt->shouldReceive('bubbleCancelled') + ->zeroOrMoreTimes() + ->andReturn(false); + $evt->shouldReceive('setResult') + ->once() + ->with(Swift_Events_SendEvent::RESULT_SUCCESS); + + $this->_finishBuffer($buf); + $smtp->start(); + $this->assertEquals(2, $smtp->send($message)); + } + + public function testCancellingEventBubbleBeforeSendStopsEvent() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_SendEvent')->shouldIgnoreMissing(); + $smtp = $this->_getTransport($buf, $dispatcher); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(array('chris@swiftmailer.org' => null)); + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('mark@swiftmailer.org' => 'Mark')); + $dispatcher->shouldReceive('createSendEvent') + ->zeroOrMoreTimes() + ->with($smtp, \Mockery::any()) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'beforeSendPerformed'); + $dispatcher->shouldReceive('dispatchEvent') + ->zeroOrMoreTimes(); + $evt->shouldReceive('bubbleCancelled') + ->atLeast()->once() + ->andReturn(true); + + $this->_finishBuffer($buf); + $smtp->start(); + $this->assertEquals(0, $smtp->send($message)); + } + + public function testStartingTransportDispatchesTransportChangeEvent() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_TransportChangeEvent'); + $smtp = $this->_getTransport($buf, $dispatcher); + + $dispatcher->shouldReceive('createTransportChangeEvent') + ->atLeast()->once() + ->with($smtp) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'transportStarted'); + $dispatcher->shouldReceive('dispatchEvent') + ->zeroOrMoreTimes(); + $evt->shouldReceive('bubbleCancelled') + ->atLeast()->once() + ->andReturn(false); + + $this->_finishBuffer($buf); + $smtp->start(); + } + + public function testStartingTransportDispatchesBeforeTransportChangeEvent() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_TransportChangeEvent'); + $smtp = $this->_getTransport($buf, $dispatcher); + + $dispatcher->shouldReceive('createTransportChangeEvent') + ->atLeast()->once() + ->with($smtp) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'beforeTransportStarted'); + $dispatcher->shouldReceive('dispatchEvent') + ->zeroOrMoreTimes(); + $evt->shouldReceive('bubbleCancelled') + ->atLeast()->once() + ->andReturn(false); + + $this->_finishBuffer($buf); + $smtp->start(); + } + + public function testCancellingBubbleBeforeTransportStartStopsEvent() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_TransportChangeEvent'); + $smtp = $this->_getTransport($buf, $dispatcher); + + $dispatcher->shouldReceive('createTransportChangeEvent') + ->atLeast()->once() + ->with($smtp) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'beforeTransportStarted'); + $dispatcher->shouldReceive('dispatchEvent') + ->zeroOrMoreTimes(); + $evt->shouldReceive('bubbleCancelled') + ->atLeast()->once() + ->andReturn(true); + + $this->_finishBuffer($buf); + $smtp->start(); + + $this->assertFalse($smtp->isStarted(), + '%s: Transport should not be started since event bubble was cancelled' + ); + } + + public function testStoppingTransportDispatchesTransportChangeEvent() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_TransportChangeEvent')->shouldIgnoreMissing(); + $smtp = $this->_getTransport($buf, $dispatcher); + + $dispatcher->shouldReceive('createTransportChangeEvent') + ->atLeast()->once() + ->with($smtp) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'transportStopped'); + $dispatcher->shouldReceive('dispatchEvent') + ->zeroOrMoreTimes(); + + $this->_finishBuffer($buf); + $smtp->start(); + $smtp->stop(); + } + + public function testStoppingTransportDispatchesBeforeTransportChangeEvent() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_TransportChangeEvent')->shouldIgnoreMissing(); + $smtp = $this->_getTransport($buf, $dispatcher); + + $dispatcher->shouldReceive('createTransportChangeEvent') + ->atLeast()->once() + ->with($smtp) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'beforeTransportStopped'); + $dispatcher->shouldReceive('dispatchEvent') + ->zeroOrMoreTimes(); + + $this->_finishBuffer($buf); + $smtp->start(); + $smtp->stop(); + } + + public function testCancellingBubbleBeforeTransportStoppedStopsEvent() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_TransportChangeEvent'); + $smtp = $this->_getTransport($buf, $dispatcher); + + $hasRun = false; + $dispatcher->shouldReceive('createTransportChangeEvent') + ->atLeast()->once() + ->with($smtp) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'beforeTransportStopped') + ->andReturnUsing(function () use (&$hasRun) { + $hasRun = true; + }); + $dispatcher->shouldReceive('dispatchEvent') + ->zeroOrMoreTimes(); + $evt->shouldReceive('bubbleCancelled') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$hasRun) { + return $hasRun; + }); + + $this->_finishBuffer($buf); + $smtp->start(); + $smtp->stop(); + + $this->assertTrue($smtp->isStarted(), + '%s: Transport should not be stopped since event bubble was cancelled' + ); + } + + public function testResponseEventsAreGenerated() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_ResponseEvent'); + $smtp = $this->_getTransport($buf, $dispatcher); + + $dispatcher->shouldReceive('createResponseEvent') + ->atLeast()->once() + ->with($smtp, \Mockery::any(), \Mockery::any()) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->atLeast()->once() + ->with($evt, 'responseReceived'); + + $this->_finishBuffer($buf); + $smtp->start(); + } + + public function testCommandEventsAreGenerated() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_CommandEvent'); + $smtp = $this->_getTransport($buf, $dispatcher); + + $dispatcher->shouldReceive('createCommandEvent') + ->once() + ->with($smtp, \Mockery::any(), \Mockery::any()) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'commandSent'); + + $this->_finishBuffer($buf); + $smtp->start(); + } + + public function testExceptionsCauseExceptionEvents() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_TransportExceptionEvent'); + $smtp = $this->_getTransport($buf, $dispatcher); + + $buf->shouldReceive('readLine') + ->atLeast()->once() + ->andReturn("503 I'm sleepy, go away!\r\n"); + $dispatcher->shouldReceive('createTransportExceptionEvent') + ->zeroOrMoreTimes() + ->with($smtp, \Mockery::any()) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'exceptionThrown'); + $evt->shouldReceive('bubbleCancelled') + ->atLeast()->once() + ->andReturn(false); + + try { + $smtp->start(); + $this->fail('TransportException should be thrown on invalid response'); + } catch (Swift_TransportException $e) { + } + } + + public function testExceptionBubblesCanBeCancelled() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_TransportExceptionEvent'); + $smtp = $this->_getTransport($buf, $dispatcher); + + $buf->shouldReceive('readLine') + ->atLeast()->once() + ->andReturn("503 I'm sleepy, go away!\r\n"); + $dispatcher->shouldReceive('createTransportExceptionEvent') + ->twice() + ->with($smtp, \Mockery::any()) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->twice() + ->with($evt, 'exceptionThrown'); + $evt->shouldReceive('bubbleCancelled') + ->atLeast()->once() + ->andReturn(true); + + $this->_finishBuffer($buf); + $smtp->start(); + } + + // -- Creation Methods + + protected function _createEventDispatcher($stub = true) + { + return $this->getMockery('Swift_Events_EventDispatcher')->shouldIgnoreMissing(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/AbstractSmtpTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/AbstractSmtpTest.php new file mode 100644 index 0000000000..f49b489ed5 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/AbstractSmtpTest.php @@ -0,0 +1,1249 @@ +_getBuffer(); + $smtp = $this->_getTransport($buf); + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 some.server.tld bleh\r\n"); + + $this->_finishBuffer($buf); + try { + $this->assertFalse($smtp->isStarted(), '%s: SMTP should begin non-started'); + $smtp->start(); + $this->assertTrue($smtp->isStarted(), '%s: start() should have started connection'); + } catch (Exception $e) { + $this->fail('220 is a valid SMTP greeting and should be accepted'); + } + } + + public function testBadGreetingCausesException() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("554 I'm busy\r\n"); + $this->_finishBuffer($buf); + try { + $this->assertFalse($smtp->isStarted(), '%s: SMTP should begin non-started'); + $smtp->start(); + $this->fail('554 greeting indicates an error and should cause an exception'); + } catch (Exception $e) { + $this->assertFalse($smtp->isStarted(), '%s: start() should have failed'); + } + } + + public function testStartSendsHeloToInitiate() + { + /* -- RFC 2821, 3.2. + + 3.2 Client Initiation + + Once the server has sent the welcoming message and the client has + received it, the client normally sends the EHLO command to the + server, indicating the client's identity. In addition to opening the + session, use of EHLO indicates that the client is able to process + service extensions and requests that the server provide a list of the + extensions it supports. Older SMTP systems which are unable to + support service extensions and contemporary clients which do not + require service extensions in the mail session being initiated, MAY + use HELO instead of EHLO. Servers MUST NOT return the extended + EHLO-style response to a HELO command. For a particular connection + attempt, if the server returns a "command not recognized" response to + EHLO, the client SHOULD be able to fall back and send HELO. + + In the EHLO command the host sending the command identifies itself; + the command may be interpreted as saying "Hello, I am " (and, + in the case of EHLO, "and I support service extension requests"). + + -- RFC 2281, 4.1.1.1. + + ehlo = "EHLO" SP Domain CRLF + helo = "HELO" SP Domain CRLF + + -- RFC 2821, 4.3.2. + + EHLO or HELO + S: 250 + E: 504, 550 + + */ + + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 some.server.tld bleh\r\n"); + $buf->shouldReceive('write') + ->once() + ->with('~^HELO .*?\r\n$~D') + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('250 ServerName'."\r\n"); + + $this->_finishBuffer($buf); + try { + $smtp->start(); + } catch (Exception $e) { + $this->fail('Starting SMTP should send HELO and accept 250 response'); + } + } + + public function testInvalidHeloResponseCausesException() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 some.server.tld bleh\r\n"); + $buf->shouldReceive('write') + ->once() + ->with('~^HELO .*?\r\n$~D') + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('504 WTF'."\r\n"); + + $this->_finishBuffer($buf); + try { + $this->assertFalse($smtp->isStarted(), '%s: SMTP should begin non-started'); + $smtp->start(); + $this->fail('Non 250 HELO response should raise Exception'); + } catch (Exception $e) { + $this->assertFalse($smtp->isStarted(), '%s: SMTP start() should have failed'); + } + } + + public function testDomainNameIsPlacedInHelo() + { + /* -- RFC 2821, 4.1.4. + + The SMTP client MUST, if possible, ensure that the domain parameter + to the EHLO command is a valid principal host name (not a CNAME or MX + name) for its host. If this is not possible (e.g., when the client's + address is dynamically assigned and the client does not have an + obvious name), an address literal SHOULD be substituted for the + domain name and supplemental information provided that will assist in + identifying the client. + */ + + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 some.server.tld bleh\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("HELO mydomain.com\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('250 ServerName'."\r\n"); + + $this->_finishBuffer($buf); + $smtp->setLocalDomain('mydomain.com'); + $smtp->start(); + } + + public function testSuccessfulMailCommand() + { + /* -- RFC 2821, 3.3. + + There are three steps to SMTP mail transactions. The transaction + starts with a MAIL command which gives the sender identification. + + ..... + + The first step in the procedure is the MAIL command. + + MAIL FROM: [SP ] + + -- RFC 2821, 4.1.1.2. + + Syntax: + + "MAIL FROM:" ("<>" / Reverse-Path) + [SP Mail-parameters] CRLF + -- RFC 2821, 4.1.2. + + Reverse-path = Path + Forward-path = Path + Path = "<" [ A-d-l ":" ] Mailbox ">" + A-d-l = At-domain *( "," A-d-l ) + ; Note that this form, the so-called "source route", + ; MUST BE accepted, SHOULD NOT be generated, and SHOULD be + ; ignored. + At-domain = "@" domain + + -- RFC 2821, 4.3.2. + + MAIL + S: 250 + E: 552, 451, 452, 550, 553, 503 + */ + + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + $message->shouldReceive('getFrom') + ->once() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array('foo@bar' => null)); + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('write') + ->once() + ->with("MAIL FROM:\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250 OK\r\n"); + + $this->_finishBuffer($buf); + try { + $smtp->start(); + $smtp->send($message); + } catch (Exception $e) { + $this->fail('MAIL FROM should accept a 250 response'); + } + } + + public function testInvalidResponseCodeFromMailCausesException() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->once() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array('foo@bar' => null)); + $buf->shouldReceive('write') + ->once() + ->with("MAIL FROM:\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('553 Bad'."\r\n"); + + $this->_finishBuffer($buf); + try { + $smtp->start(); + $smtp->send($message); + $this->fail('MAIL FROM should accept a 250 response'); + } catch (Exception $e) { + } + } + + public function testSenderIsPreferredOverFrom() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->once() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getSender') + ->once() + ->andReturn(array('another@domain.com' => 'Someone')); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array('foo@bar' => null)); + $buf->shouldReceive('write') + ->once() + ->with("MAIL FROM:\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('250 OK'."\r\n"); + + $this->_finishBuffer($buf); + $smtp->start(); + $smtp->send($message); + } + + public function testReturnPathIsPreferredOverSender() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->once() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getSender') + ->once() + ->andReturn(array('another@domain.com' => 'Someone')); + $message->shouldReceive('getReturnPath') + ->once() + ->andReturn('more@domain.com'); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array('foo@bar' => null)); + $buf->shouldReceive('write') + ->once() + ->with("MAIL FROM:\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('250 OK'."\r\n"); + + $this->_finishBuffer($buf); + $smtp->start(); + $smtp->send($message); + } + + public function testSuccessfulRcptCommandWith250Response() + { + /* -- RFC 2821, 3.3. + + The second step in the procedure is the RCPT command. + + RCPT TO: [ SP ] + + The first or only argument to this command includes a forward-path + (normally a mailbox and domain, always surrounded by "<" and ">" + brackets) identifying one recipient. If accepted, the SMTP server + returns a 250 OK reply and stores the forward-path. If the recipient + is known not to be a deliverable address, the SMTP server returns a + 550 reply, typically with a string such as "no such user - " and the + mailbox name (other circumstances and reply codes are possible). + This step of the procedure can be repeated any number of times. + + -- RFC 2821, 4.1.1.3. + + This command is used to identify an individual recipient of the mail + data; multiple recipients are specified by multiple use of this + command. The argument field contains a forward-path and may contain + optional parameters. + + The forward-path normally consists of the required destination + mailbox. Sending systems SHOULD not generate the optional list of + hosts known as a source route. + + ....... + + "RCPT TO:" ("" / "" / Forward-Path) + [SP Rcpt-parameters] CRLF + + -- RFC 2821, 4.2.2. + + 250 Requested mail action okay, completed + 251 User not local; will forward to + (See section 3.4) + 252 Cannot VRFY user, but will accept message and attempt + delivery + + -- RFC 2821, 4.3.2. + + RCPT + S: 250, 251 (but see section 3.4 for discussion of 251 and 551) + E: 550, 551, 552, 553, 450, 451, 452, 503, 550 + */ + + //We'll treat 252 as accepted since it isn't really a failure + + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->once() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array('foo@bar' => null)); + $buf->shouldReceive('write') + ->once() + ->with("MAIL FROM:\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('250 OK'."\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn('250 OK'."\r\n"); + + $this->_finishBuffer($buf); + try { + $smtp->start(); + $smtp->send($message); + } catch (Exception $e) { + $this->fail('RCPT TO should accept a 250 response'); + } + } + + public function testMailFromCommandIsOnlySentOncePerMessage() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->once() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array('foo@bar' => null)); + $buf->shouldReceive('write') + ->once() + ->with("MAIL FROM:\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('250 OK'."\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn('250 OK'."\r\n"); + $buf->shouldReceive('write') + ->never() + ->with("MAIL FROM:\r\n"); + + $this->_finishBuffer($buf); + $smtp->start(); + $smtp->send($message); + } + + public function testMultipleRecipientsSendsMultipleRcpt() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->once() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array( + 'foo@bar' => null, + 'zip@button' => 'Zip Button', + 'test@domain' => 'Test user', + )); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('250 OK'."\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn('250 OK'."\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:\r\n") + ->andReturn(3); + $buf->shouldReceive('readLine') + ->once() + ->with(3) + ->andReturn('250 OK'."\r\n"); + + $this->_finishBuffer($buf); + $smtp->start(); + $smtp->send($message); + } + + public function testCcRecipientsSendsMultipleRcpt() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->once() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array('foo@bar' => null)); + $message->shouldReceive('getCc') + ->once() + ->andReturn(array( + 'zip@button' => 'Zip Button', + 'test@domain' => 'Test user', + )); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('250 OK'."\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn('250 OK'."\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:\r\n") + ->andReturn(3); + $buf->shouldReceive('readLine') + ->once() + ->with(3) + ->andReturn('250 OK'."\r\n"); + + $this->_finishBuffer($buf); + $smtp->start(); + $smtp->send($message); + } + + public function testSendReturnsNumberOfSuccessfulRecipients() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->once() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array('foo@bar' => null)); + $message->shouldReceive('getCc') + ->once() + ->andReturn(array( + 'zip@button' => 'Zip Button', + 'test@domain' => 'Test user', + )); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('250 OK'."\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn('501 Nobody here'."\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:\r\n") + ->andReturn(3); + $buf->shouldReceive('readLine') + ->once() + ->with(3) + ->andReturn('250 OK'."\r\n"); + + $this->_finishBuffer($buf); + $smtp->start(); + $this->assertEquals(2, $smtp->send($message), + '%s: 1 of 3 recipients failed so 2 should be returned' + ); + } + + public function testRsetIsSentIfNoSuccessfulRecipients() + { + /* --RFC 2821, 4.1.1.5. + + This command specifies that the current mail transaction will be + aborted. Any stored sender, recipients, and mail data MUST be + discarded, and all buffers and state tables cleared. The receiver + MUST send a "250 OK" reply to a RSET command with no arguments. A + reset command may be issued by the client at any time. + + -- RFC 2821, 4.3.2. + + RSET + S: 250 + */ + + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->once() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array('foo@bar' => null)); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('503 Bad'."\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RSET\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn('250 OK'."\r\n"); + + $this->_finishBuffer($buf); + $smtp->start(); + $this->assertEquals(0, $smtp->send($message), + '%s: 1 of 1 recipients failed so 0 should be returned' + ); + } + + public function testSuccessfulDataCommand() + { + /* -- RFC 2821, 3.3. + + The third step in the procedure is the DATA command (or some + alternative specified in a service extension). + + DATA + + If accepted, the SMTP server returns a 354 Intermediate reply and + considers all succeeding lines up to but not including the end of + mail data indicator to be the message text. + + -- RFC 2821, 4.1.1.4. + + The receiver normally sends a 354 response to DATA, and then treats + the lines (strings ending in sequences, as described in + section 2.3.7) following the command as mail data from the sender. + This command causes the mail data to be appended to the mail data + buffer. The mail data may contain any of the 128 ASCII character + codes, although experience has indicated that use of control + characters other than SP, HT, CR, and LF may cause problems and + SHOULD be avoided when possible. + + -- RFC 2821, 4.3.2. + + DATA + I: 354 -> data -> S: 250 + E: 552, 554, 451, 452 + E: 451, 554, 503 + */ + + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->once() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array('foo@bar' => null)); + $buf->shouldReceive('write') + ->once() + ->with("DATA\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('354 Go ahead'."\r\n"); + + $this->_finishBuffer($buf); + try { + $smtp->start(); + $smtp->send($message); + } catch (Exception $e) { + $this->fail('354 is the expected response to DATA'); + } + } + + public function testBadDataResponseCausesException() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->once() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array('foo@bar' => null)); + $buf->shouldReceive('write') + ->once() + ->with("DATA\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('451 Bad'."\r\n"); + + $this->_finishBuffer($buf); + try { + $smtp->start(); + $smtp->send($message); + $this->fail('354 is the expected response to DATA (not observed)'); + } catch (Exception $e) { + } + } + + public function testMessageIsStreamedToBufferForData() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->once() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array('foo@bar' => null)); + $buf->shouldReceive('write') + ->once() + ->with("DATA\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('354 OK'."\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("\r\n.\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn('250 OK'."\r\n"); + + $this->_finishBuffer($buf); + $smtp->start(); + $smtp->send($message); + } + + public function testBadResponseAfterDataTransmissionCausesException() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->once() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array('foo@bar' => null)); + $buf->shouldReceive('write') + ->once() + ->with("DATA\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('354 OK'."\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("\r\n.\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn('554 Error'."\r\n"); + + $this->_finishBuffer($buf); + try { + $smtp->start(); + $smtp->send($message); + $this->fail('250 is the expected response after a DATA transmission (not observed)'); + } catch (Exception $e) { + } + } + + public function testBccRecipientsAreRemovedFromHeaders() + { + /* -- RFC 2821, 7.2. + + Addresses that do not appear in the message headers may appear in the + RCPT commands to an SMTP server for a number of reasons. The two + most common involve the use of a mailing address as a "list exploder" + (a single address that resolves into multiple addresses) and the + appearance of "blind copies". Especially when more than one RCPT + command is present, and in order to avoid defeating some of the + purpose of these mechanisms, SMTP clients and servers SHOULD NOT copy + the full set of RCPT command arguments into the headers, either as + part of trace headers or as informational or private-extension + headers. Since this rule is often violated in practice, and cannot + be enforced, sending SMTP systems that are aware of "bcc" use MAY + find it helpful to send each blind copy as a separate message + transaction containing only a single RCPT command. + */ + + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('foo@bar' => null)); + $message->shouldReceive('getBcc') + ->zeroOrMoreTimes() + ->andReturn(array( + 'zip@button' => 'Zip Button', + 'test@domain' => 'Test user', + )); + $message->shouldReceive('setBcc') + ->once() + ->with(array()); + $message->shouldReceive('setBcc') + ->zeroOrMoreTimes(); + + $this->_finishBuffer($buf); + $smtp->start(); + $smtp->send($message); + } + + public function testEachBccRecipientIsSentASeparateMessage() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('foo@bar' => null)); + $message->shouldReceive('getBcc') + ->zeroOrMoreTimes() + ->andReturn(array( + 'zip@button' => 'Zip Button', + 'test@domain' => 'Test user', + )); + $message->shouldReceive('setBcc') + ->atLeast()->once() + ->with(array()); + $message->shouldReceive('setBcc') + ->once() + ->with(array('zip@button' => 'Zip Button')); + $message->shouldReceive('setBcc') + ->once() + ->with(array('test@domain' => 'Test user')); + $message->shouldReceive('setBcc') + ->atLeast()->once() + ->with(array( + 'zip@button' => 'Zip Button', + 'test@domain' => 'Test user', + )); + + $buf->shouldReceive('write')->once()->with("MAIL FROM:\r\n")->andReturn(1); + $buf->shouldReceive('readLine')->once()->with(1)->andReturn("250 OK\r\n"); + $buf->shouldReceive('write')->once()->with("RCPT TO:\r\n")->andReturn(2); + $buf->shouldReceive('readLine')->once()->with(2)->andReturn("250 OK\r\n"); + $buf->shouldReceive('write')->once()->with("DATA\r\n")->andReturn(3); + $buf->shouldReceive('readLine')->once()->with(3)->andReturn("354 OK\r\n"); + $buf->shouldReceive('write')->once()->with("\r\n.\r\n")->andReturn(4); + $buf->shouldReceive('readLine')->once()->with(4)->andReturn("250 OK\r\n"); + + $buf->shouldReceive('write')->once()->with("MAIL FROM:\r\n")->andReturn(5); + $buf->shouldReceive('readLine')->once()->with(5)->andReturn("250 OK\r\n"); + $buf->shouldReceive('write')->once()->with("RCPT TO:\r\n")->andReturn(6); + $buf->shouldReceive('readLine')->once()->with(6)->andReturn("250 OK\r\n"); + $buf->shouldReceive('write')->once()->with("DATA\r\n")->andReturn(7); + $buf->shouldReceive('readLine')->once()->with(7)->andReturn("354 OK\r\n"); + $buf->shouldReceive('write')->once()->with("\r\n.\r\n")->andReturn(8); + $buf->shouldReceive('readLine')->once()->with(8)->andReturn("250 OK\r\n"); + + $buf->shouldReceive('write')->once()->with("MAIL FROM:\r\n")->andReturn(9); + $buf->shouldReceive('readLine')->once()->with(9)->andReturn("250 OK\r\n"); + $buf->shouldReceive('write')->once()->with("RCPT TO:\r\n")->andReturn(10); + $buf->shouldReceive('readLine')->once()->with(10)->andReturn("250 OK\r\n"); + $buf->shouldReceive('write')->once()->with("DATA\r\n")->andReturn(11); + $buf->shouldReceive('readLine')->once()->with(11)->andReturn("354 OK\r\n"); + $buf->shouldReceive('write')->once()->with("\r\n.\r\n")->andReturn(12); + $buf->shouldReceive('readLine')->once()->with(12)->andReturn("250 OK\r\n"); + + $this->_finishBuffer($buf); + $smtp->start(); + $this->assertEquals(3, $smtp->send($message)); + } + + public function testMessageStateIsRestoredOnFailure() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('foo@bar' => null)); + $message->shouldReceive('getBcc') + ->zeroOrMoreTimes() + ->andReturn(array( + 'zip@button' => 'Zip Button', + 'test@domain' => 'Test user', + )); + $message->shouldReceive('setBcc') + ->once() + ->with(array()); + $message->shouldReceive('setBcc') + ->once() + ->with(array( + 'zip@button' => 'Zip Button', + 'test@domain' => 'Test user', + )); + $buf->shouldReceive('write') + ->once() + ->with("MAIL FROM:\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250 OK\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn("250 OK\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("DATA\r\n") + ->andReturn(3); + $buf->shouldReceive('readLine') + ->once() + ->with(3) + ->andReturn("451 No\r\n"); + + $this->_finishBuffer($buf); + + $smtp->start(); + try { + $smtp->send($message); + $this->fail('A bad response was given so exception is expected'); + } catch (Exception $e) { + } + } + + public function testStopSendsQuitCommand() + { + /* -- RFC 2821, 4.1.1.10. + + This command specifies that the receiver MUST send an OK reply, and + then close the transmission channel. + + The receiver MUST NOT intentionally close the transmission channel + until it receives and replies to a QUIT command (even if there was an + error). The sender MUST NOT intentionally close the transmission + channel until it sends a QUIT command and SHOULD wait until it + receives the reply (even if there was an error response to a previous + command). If the connection is closed prematurely due to violations + of the above or system or network failure, the server MUST cancel any + pending transaction, but not undo any previously completed + transaction, and generally MUST act as if the command or transaction + in progress had received a temporary error (i.e., a 4yz response). + + The QUIT command may be issued at any time. + + Syntax: + "QUIT" CRLF + */ + + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('write') + ->once() + ->with("QUIT\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("221 Bye\r\n"); + $buf->shouldReceive('terminate') + ->once(); + + $this->_finishBuffer($buf); + + $this->assertFalse($smtp->isStarted()); + $smtp->start(); + $this->assertTrue($smtp->isStarted()); + $smtp->stop(); + $this->assertFalse($smtp->isStarted()); + } + + public function testBufferCanBeFetched() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $ref = $smtp->getBuffer(); + $this->assertEquals($buf, $ref); + } + + public function testBufferCanBeWrittenToUsingExecuteCommand() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + $buf->shouldReceive('write') + ->zeroOrMoreTimes() + ->with("FOO\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->zeroOrMoreTimes() + ->with(1) + ->andReturn("250 OK\r\n"); + + $res = $smtp->executeCommand("FOO\r\n"); + $this->assertEquals("250 OK\r\n", $res); + } + + public function testResponseCodesAreValidated() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + $buf->shouldReceive('write') + ->zeroOrMoreTimes() + ->with("FOO\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->zeroOrMoreTimes() + ->with(1) + ->andReturn("551 Not ok\r\n"); + + try { + $smtp->executeCommand("FOO\r\n", array(250, 251)); + $this->fail('A 250 or 251 response was needed but 551 was returned.'); + } catch (Exception $e) { + } + } + + public function testFailedRecipientsCanBeCollectedByReference() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('foo@bar' => null)); + $message->shouldReceive('getBcc') + ->zeroOrMoreTimes() + ->andReturn(array( + 'zip@button' => 'Zip Button', + 'test@domain' => 'Test user', + )); + $message->shouldReceive('setBcc') + ->atLeast()->once() + ->with(array()); + $message->shouldReceive('setBcc') + ->once() + ->with(array('zip@button' => 'Zip Button')); + $message->shouldReceive('setBcc') + ->once() + ->with(array('test@domain' => 'Test user')); + $message->shouldReceive('setBcc') + ->atLeast()->once() + ->with(array( + 'zip@button' => 'Zip Button', + 'test@domain' => 'Test user', + )); + + $buf->shouldReceive('write')->once()->with("MAIL FROM:\r\n")->andReturn(1); + $buf->shouldReceive('readLine')->once()->with(1)->andReturn("250 OK\r\n"); + $buf->shouldReceive('write')->once()->with("RCPT TO:\r\n")->andReturn(2); + $buf->shouldReceive('readLine')->once()->with(2)->andReturn("250 OK\r\n"); + $buf->shouldReceive('write')->once()->with("DATA\r\n")->andReturn(3); + $buf->shouldReceive('readLine')->once()->with(3)->andReturn("354 OK\r\n"); + $buf->shouldReceive('write')->once()->with("\r\n.\r\n")->andReturn(4); + $buf->shouldReceive('readLine')->once()->with(4)->andReturn("250 OK\r\n"); + + $buf->shouldReceive('write')->once()->with("MAIL FROM:\r\n")->andReturn(5); + $buf->shouldReceive('readLine')->once()->with(5)->andReturn("250 OK\r\n"); + $buf->shouldReceive('write')->once()->with("RCPT TO:\r\n")->andReturn(6); + $buf->shouldReceive('readLine')->once()->with(6)->andReturn("500 Bad\r\n"); + $buf->shouldReceive('write')->once()->with("RSET\r\n")->andReturn(7); + $buf->shouldReceive('readLine')->once()->with(7)->andReturn("250 OK\r\n"); + + $buf->shouldReceive('write')->once()->with("MAIL FROM:\r\n")->andReturn(9); + $buf->shouldReceive('readLine')->once()->with(9)->andReturn("250 OK\r\n"); + $buf->shouldReceive('write')->once()->with("RCPT TO:\r\n")->andReturn(10); + $buf->shouldReceive('readLine')->once()->with(10)->andReturn("500 Bad\r\n"); + $buf->shouldReceive('write')->once()->with("RSET\r\n")->andReturn(11); + $buf->shouldReceive('readLine')->once()->with(11)->andReturn("250 OK\r\n"); + + $this->_finishBuffer($buf); + $smtp->start(); + $this->assertEquals(1, $smtp->send($message, $failures)); + $this->assertEquals(array('zip@button', 'test@domain'), $failures, + '%s: Failures should be caught in an array' + ); + } + + public function testSendingRegeneratesMessageId() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('foo@bar' => null)); + $message->shouldReceive('generateId') + ->once(); + + $this->_finishBuffer($buf); + $smtp->start(); + $smtp->send($message); + } + + protected function _getBuffer() + { + return $this->getMockery('Swift_Transport_IoBuffer')->shouldIgnoreMissing(); + } + + protected function _createMessage() + { + return $this->getMockery('Swift_Mime_Message')->shouldIgnoreMissing(); + } + + protected function _finishBuffer($buf) + { + $buf->shouldReceive('readLine') + ->zeroOrMoreTimes() + ->with(0) + ->andReturn('220 server.com foo'."\r\n"); + $buf->shouldReceive('write') + ->zeroOrMoreTimes() + ->with('~^(EH|HE)LO .*?\r\n$~D') + ->andReturn($x = uniqid()); + $buf->shouldReceive('readLine') + ->zeroOrMoreTimes() + ->with($x) + ->andReturn('250 ServerName'."\r\n"); + $buf->shouldReceive('write') + ->zeroOrMoreTimes() + ->with('~^MAIL FROM:<.*?>\r\n$~D') + ->andReturn($x = uniqid()); + $buf->shouldReceive('readLine') + ->zeroOrMoreTimes() + ->with($x) + ->andReturn("250 OK\r\n"); + $buf->shouldReceive('write') + ->zeroOrMoreTimes() + ->with('~^RCPT TO:<.*?>\r\n$~D') + ->andReturn($x = uniqid()); + $buf->shouldReceive('readLine') + ->zeroOrMoreTimes() + ->with($x) + ->andReturn("250 OK\r\n"); + $buf->shouldReceive('write') + ->zeroOrMoreTimes() + ->with("DATA\r\n") + ->andReturn($x = uniqid()); + $buf->shouldReceive('readLine') + ->zeroOrMoreTimes() + ->with($x) + ->andReturn("354 OK\r\n"); + $buf->shouldReceive('write') + ->zeroOrMoreTimes() + ->with("\r\n.\r\n") + ->andReturn($x = uniqid()); + $buf->shouldReceive('readLine') + ->zeroOrMoreTimes() + ->with($x) + ->andReturn("250 OK\r\n"); + $buf->shouldReceive('write') + ->zeroOrMoreTimes() + ->with("RSET\r\n") + ->andReturn($x = uniqid()); + $buf->shouldReceive('readLine') + ->zeroOrMoreTimes() + ->with($x) + ->andReturn("250 OK\r\n"); + + $buf->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturn(false); + $buf->shouldReceive('readLine') + ->zeroOrMoreTimes() + ->andReturn(false); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/CramMd5AuthenticatorTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/CramMd5AuthenticatorTest.php new file mode 100644 index 0000000000..f64b0716a3 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/CramMd5AuthenticatorTest.php @@ -0,0 +1,66 @@ +_agent = $this->getMockery('Swift_Transport_SmtpAgent')->shouldIgnoreMissing(); + } + + public function testKeywordIsCramMd5() + { + /* -- RFC 2195, 2. + The authentication type associated with CRAM is "CRAM-MD5". + */ + + $cram = $this->_getAuthenticator(); + $this->assertEquals('CRAM-MD5', $cram->getAuthKeyword()); + } + + public function testSuccessfulAuthentication() + { + $cram = $this->_getAuthenticator(); + + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with("AUTH CRAM-MD5\r\n", array(334)) + ->andReturn('334 '.base64_encode('')."\r\n"); + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with(\Mockery::any(), array(235)); + + $this->assertTrue($cram->authenticate($this->_agent, 'jack', 'pass'), + '%s: The buffer accepted all commands authentication should succeed' + ); + } + + public function testAuthenticationFailureSendRsetAndReturnFalse() + { + $cram = $this->_getAuthenticator(); + + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with("AUTH CRAM-MD5\r\n", array(334)) + ->andReturn('334 '.base64_encode('')."\r\n"); + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with(\Mockery::any(), array(235)) + ->andThrow(new Swift_TransportException('')); + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with("RSET\r\n", array(250)); + + $this->assertFalse($cram->authenticate($this->_agent, 'jack', 'pass'), + '%s: Authentication fails, so RSET should be sent' + ); + } + + // -- Private helpers + + private function _getAuthenticator() + { + return new Swift_Transport_Esmtp_Auth_CramMd5Authenticator(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/LoginAuthenticatorTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/LoginAuthenticatorTest.php new file mode 100644 index 0000000000..fc6e806956 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/LoginAuthenticatorTest.php @@ -0,0 +1,66 @@ +_agent = $this->getMockery('Swift_Transport_SmtpAgent')->shouldIgnoreMissing(); + } + + public function testKeywordIsLogin() + { + $login = $this->_getAuthenticator(); + $this->assertEquals('LOGIN', $login->getAuthKeyword()); + } + + public function testSuccessfulAuthentication() + { + $login = $this->_getAuthenticator(); + + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with("AUTH LOGIN\r\n", array(334)); + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with(base64_encode('jack')."\r\n", array(334)); + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with(base64_encode('pass')."\r\n", array(235)); + + $this->assertTrue($login->authenticate($this->_agent, 'jack', 'pass'), + '%s: The buffer accepted all commands authentication should succeed' + ); + } + + public function testAuthenticationFailureSendRsetAndReturnFalse() + { + $login = $this->_getAuthenticator(); + + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with("AUTH LOGIN\r\n", array(334)); + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with(base64_encode('jack')."\r\n", array(334)); + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with(base64_encode('pass')."\r\n", array(235)) + ->andThrow(new Swift_TransportException('')); + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with("RSET\r\n", array(250)); + + $this->assertFalse($login->authenticate($this->_agent, 'jack', 'pass'), + '%s: Authentication fails, so RSET should be sent' + ); + } + + // -- Private helpers + + private function _getAuthenticator() + { + return new Swift_Transport_Esmtp_Auth_LoginAuthenticator(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/NTLMAuthenticatorTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/NTLMAuthenticatorTest.php new file mode 100644 index 0000000000..09ace83434 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/NTLMAuthenticatorTest.php @@ -0,0 +1,235 @@ +markTestSkipped( + 'One of the required functions is not available.' + ); + } + } + + public function testKeywordIsNtlm() + { + $login = $this->_getAuthenticator(); + $this->assertEquals('NTLM', $login->getAuthKeyword()); + } + + public function testMessage1Generator() + { + $login = $this->_getAuthenticator(); + $message1 = $this->_invokePrivateMethod('createMessage1', $login); + + $this->assertEquals($this->_message1, bin2hex($message1), + '%s: We send the smallest ntlm message which should never fail.' + ); + } + + public function testLMv1Generator() + { + $password = 'test1234'; + $challenge = 'b019d38bad875c9d'; + $lmv1 = '1879f60127f8a877022132ec221bcbf3ca016a9f76095606'; + + $login = $this->_getAuthenticator(); + $lmv1Result = $this->_invokePrivateMethod('createLMPassword', $login, array($password, $this->hex2bin($challenge))); + + $this->assertEquals($lmv1, bin2hex($lmv1Result), + '%s: The keys should be the same cause we use the same values to generate them.' + ); + } + + public function testLMv2Generator() + { + $username = 'user'; + $password = 'SecREt01'; + $domain = 'DOMAIN'; + $challenge = '0123456789abcdef'; + $lmv2 = 'd6e6152ea25d03b7c6ba6629c2d6aaf0ffffff0011223344'; + + $login = $this->_getAuthenticator(); + $lmv2Result = $this->_invokePrivateMethod('createLMv2Password', $login, array($password, $username, $domain, $this->hex2bin($challenge), $this->hex2bin('ffffff0011223344'))); + + $this->assertEquals($lmv2, bin2hex($lmv2Result), + '%s: The keys should be the same cause we use the same values to generate them.' + ); + } + + public function testMessage3v1Generator() + { + $username = 'test'; + $domain = 'TESTNT'; + $workstation = 'MEMBER'; + $lmResponse = '1879f60127f8a877022132ec221bcbf3ca016a9f76095606'; + $ntlmResponse = 'e6285df3287c5d194f84df1a94817c7282d09754b6f9e02a'; + $message3T = '4e544c4d5353500003000000180018006000000018001800780000000c000c0040000000080008004c0000000c000c0054000000000000009a0000000102000054004500530054004e00540074006500730074004d0045004d004200450052001879f60127f8a877022132ec221bcbf3ca016a9f76095606e6285df3287c5d194f84df1a94817c7282d09754b6f9e02a'; + + $login = $this->_getAuthenticator(); + $message3 = $this->_invokePrivateMethod('createMessage3', $login, array($domain, $username, $workstation, $this->hex2bin($lmResponse), $this->hex2bin($ntlmResponse))); + + $this->assertEquals($message3T, bin2hex($message3), + '%s: We send the same information as the example is created with so this should be the same' + ); + } + + public function testMessage3v2Generator() + { + $username = 'test'; + $domain = 'TESTNT'; + $workstation = 'MEMBER'; + $lmResponse = 'bf2e015119f6bdb3f6fdb768aa12d478f5ce3d2401c8f6e9'; + $ntlmResponse = 'caa4da8f25d5e840974ed8976d3ada46010100000000000030fa7e3c677bc301f5ce3d2401c8f6e90000000002000c0054004500530054004e00540001000c004d0045004d0042004500520003001e006d0065006d006200650072002e0074006500730074002e0063006f006d000000000000000000'; + + $login = $this->_getAuthenticator(); + $message3 = $this->_invokePrivateMethod('createMessage3', $login, array($domain, $username, $workstation, $this->hex2bin($lmResponse), $this->hex2bin($ntlmResponse))); + + $this->assertEquals($this->_message3, bin2hex($message3), + '%s: We send the same information as the example is created with so this should be the same' + ); + } + + public function testGetDomainAndUsername() + { + $username = "DOMAIN\user"; + + $login = $this->_getAuthenticator(); + list($domain, $user) = $this->_invokePrivateMethod('getDomainAndUsername', $login, array($username)); + + $this->assertEquals('DOMAIN', $domain, + '%s: the fetched domain did not match' + ); + $this->assertEquals('user', $user, + '%s: the fetched user did not match' + ); + } + + public function testGetDomainAndUsernameWithExtension() + { + $username = "domain.com\user"; + + $login = $this->_getAuthenticator(); + list($domain, $user) = $this->_invokePrivateMethod('getDomainAndUsername', $login, array($username)); + + $this->assertEquals('domain.com', $domain, + '%s: the fetched domain did not match' + ); + $this->assertEquals('user', $user, + '%s: the fetched user did not match' + ); + } + + public function testGetDomainAndUsernameWithAtSymbol() + { + $username = 'user@DOMAIN'; + + $login = $this->_getAuthenticator(); + list($domain, $user) = $this->_invokePrivateMethod('getDomainAndUsername', $login, array($username)); + + $this->assertEquals('DOMAIN', $domain, + '%s: the fetched domain did not match' + ); + $this->assertEquals('user', $user, + '%s: the fetched user did not match' + ); + } + + public function testGetDomainAndUsernameWithAtSymbolAndExtension() + { + $username = 'user@domain.com'; + + $login = $this->_getAuthenticator(); + list($domain, $user) = $this->_invokePrivateMethod('getDomainAndUsername', $login, array($username)); + + $this->assertEquals('domain.com', $domain, + '%s: the fetched domain did not match' + ); + $this->assertEquals('user', $user, + '%s: the fetched user did not match' + ); + } + + public function testSuccessfulAuthentication() + { + $domain = 'TESTNT'; + $username = 'test'; + $secret = 'test1234'; + + $ntlm = $this->_getAuthenticator(); + $agent = $this->_getAgent(); + $agent->shouldReceive('executeCommand') + ->once() + ->with('AUTH NTLM '.base64_encode( + $this->_invokePrivateMethod('createMessage1', $ntlm) + )."\r\n", array(334)) + ->andReturn('334 '.base64_encode($this->hex2bin('4e544c4d53535000020000000c000c003000000035828980514246973ea892c10000000000000000460046003c00000054004500530054004e00540002000c0054004500530054004e00540001000c004d0045004d0042004500520003001e006d0065006d006200650072002e0074006500730074002e0063006f006d0000000000'))); + $agent->shouldReceive('executeCommand') + ->once() + ->with(base64_encode( + $this->_invokePrivateMethod('createMessage3', $ntlm, array($domain, $username, $this->hex2bin('4d0045004d00420045005200'), $this->hex2bin('bf2e015119f6bdb3f6fdb768aa12d478f5ce3d2401c8f6e9'), $this->hex2bin('caa4da8f25d5e840974ed8976d3ada46010100000000000030fa7e3c677bc301f5ce3d2401c8f6e90000000002000c0054004500530054004e00540001000c004d0045004d0042004500520003001e006d0065006d006200650072002e0074006500730074002e0063006f006d000000000000000000')) + ))."\r\n", array(235)); + + $this->assertTrue($ntlm->authenticate($agent, $username.'@'.$domain, $secret, $this->hex2bin('30fa7e3c677bc301'), $this->hex2bin('f5ce3d2401c8f6e9')), + '%s: The buffer accepted all commands authentication should succeed' + ); + } + + public function testAuthenticationFailureSendRsetAndReturnFalse() + { + $domain = 'TESTNT'; + $username = 'test'; + $secret = 'test1234'; + + $ntlm = $this->_getAuthenticator(); + $agent = $this->_getAgent(); + $agent->shouldReceive('executeCommand') + ->once() + ->with('AUTH NTLM '.base64_encode( + $this->_invokePrivateMethod('createMessage1', $ntlm) + )."\r\n", array(334)) + ->andThrow(new Swift_TransportException('')); + $agent->shouldReceive('executeCommand') + ->once() + ->with("RSET\r\n", array(250)); + + $this->assertFalse($ntlm->authenticate($agent, $username.'@'.$domain, $secret, $this->hex2bin('30fa7e3c677bc301'), $this->hex2bin('f5ce3d2401c8f6e9')), + '%s: Authentication fails, so RSET should be sent' + ); + } + + // -- Private helpers + private function _getAuthenticator() + { + return new Swift_Transport_Esmtp_Auth_NTLMAuthenticator(); + } + + private function _getAgent() + { + return $this->getMockery('Swift_Transport_SmtpAgent')->shouldIgnoreMissing(); + } + + private function _invokePrivateMethod($method, $instance, array $args = array()) + { + $methodC = new ReflectionMethod($instance, trim($method)); + $methodC->setAccessible(true); + + return $methodC->invokeArgs($instance, $args); + } + + /** + * Hex2bin replacement for < PHP 5.4. + * + * @param string $hex + * + * @return string Binary + */ + protected function hex2bin($hex) + { + return function_exists('hex2bin') ? hex2bin($hex) : pack('H*', $hex); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/PlainAuthenticatorTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/PlainAuthenticatorTest.php new file mode 100644 index 0000000000..4fe9db80e2 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/PlainAuthenticatorTest.php @@ -0,0 +1,69 @@ +_agent = $this->getMockery('Swift_Transport_SmtpAgent')->shouldIgnoreMissing(); + } + + public function testKeywordIsPlain() + { + /* -- RFC 4616, 1. + The name associated with this mechanism is "PLAIN". + */ + + $login = $this->_getAuthenticator(); + $this->assertEquals('PLAIN', $login->getAuthKeyword()); + } + + public function testSuccessfulAuthentication() + { + /* -- RFC 4616, 2. + The client presents the authorization identity (identity to act as), + followed by a NUL (U+0000) character, followed by the authentication + identity (identity whose password will be used), followed by a NUL + (U+0000) character, followed by the clear-text password. + */ + + $plain = $this->_getAuthenticator(); + + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with('AUTH PLAIN '.base64_encode( + 'jack'.chr(0).'jack'.chr(0).'pass' + )."\r\n", array(235)); + + $this->assertTrue($plain->authenticate($this->_agent, 'jack', 'pass'), + '%s: The buffer accepted all commands authentication should succeed' + ); + } + + public function testAuthenticationFailureSendRsetAndReturnFalse() + { + $plain = $this->_getAuthenticator(); + + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with('AUTH PLAIN '.base64_encode( + 'jack'.chr(0).'jack'.chr(0).'pass' + )."\r\n", array(235)) + ->andThrow(new Swift_TransportException('')); + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with("RSET\r\n", array(250)); + + $this->assertFalse($plain->authenticate($this->_agent, 'jack', 'pass'), + '%s: Authentication fails, so RSET should be sent' + ); + } + + // -- Private helpers + + private function _getAuthenticator() + { + return new Swift_Transport_Esmtp_Auth_PlainAuthenticator(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/AuthHandlerTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/AuthHandlerTest.php new file mode 100644 index 0000000000..64327d478f --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/AuthHandlerTest.php @@ -0,0 +1,167 @@ +_agent = $this->getMockery('Swift_Transport_SmtpAgent')->shouldIgnoreMissing(); + } + + public function testKeywordIsAuth() + { + $auth = $this->_createHandler(array()); + $this->assertEquals('AUTH', $auth->getHandledKeyword()); + } + + public function testUsernameCanBeSetAndFetched() + { + $auth = $this->_createHandler(array()); + $auth->setUsername('jack'); + $this->assertEquals('jack', $auth->getUsername()); + } + + public function testPasswordCanBeSetAndFetched() + { + $auth = $this->_createHandler(array()); + $auth->setPassword('pass'); + $this->assertEquals('pass', $auth->getPassword()); + } + + public function testAuthModeCanBeSetAndFetched() + { + $auth = $this->_createHandler(array()); + $auth->setAuthMode('PLAIN'); + $this->assertEquals('PLAIN', $auth->getAuthMode()); + } + + public function testMixinMethods() + { + $auth = $this->_createHandler(array()); + $mixins = $auth->exposeMixinMethods(); + $this->assertTrue(in_array('getUsername', $mixins), + '%s: getUsername() should be accessible via mixin' + ); + $this->assertTrue(in_array('setUsername', $mixins), + '%s: setUsername() should be accessible via mixin' + ); + $this->assertTrue(in_array('getPassword', $mixins), + '%s: getPassword() should be accessible via mixin' + ); + $this->assertTrue(in_array('setPassword', $mixins), + '%s: setPassword() should be accessible via mixin' + ); + $this->assertTrue(in_array('setAuthMode', $mixins), + '%s: setAuthMode() should be accessible via mixin' + ); + $this->assertTrue(in_array('getAuthMode', $mixins), + '%s: getAuthMode() should be accessible via mixin' + ); + } + + public function testAuthenticatorsAreCalledAccordingToParamsAfterEhlo() + { + $a1 = $this->_createMockAuthenticator('PLAIN'); + $a2 = $this->_createMockAuthenticator('LOGIN'); + + $a1->shouldReceive('authenticate') + ->never() + ->with($this->_agent, 'jack', 'pass'); + $a2->shouldReceive('authenticate') + ->once() + ->with($this->_agent, 'jack', 'pass') + ->andReturn(true); + + $auth = $this->_createHandler(array($a1, $a2)); + $auth->setUsername('jack'); + $auth->setPassword('pass'); + + $auth->setKeywordParams(array('CRAM-MD5', 'LOGIN')); + $auth->afterEhlo($this->_agent); + } + + public function testAuthenticatorsAreNotUsedIfNoUsernameSet() + { + $a1 = $this->_createMockAuthenticator('PLAIN'); + $a2 = $this->_createMockAuthenticator('LOGIN'); + + $a1->shouldReceive('authenticate') + ->never() + ->with($this->_agent, 'jack', 'pass'); + $a2->shouldReceive('authenticate') + ->never() + ->with($this->_agent, 'jack', 'pass') + ->andReturn(true); + + $auth = $this->_createHandler(array($a1, $a2)); + + $auth->setKeywordParams(array('CRAM-MD5', 'LOGIN')); + $auth->afterEhlo($this->_agent); + } + + public function testSeveralAuthenticatorsAreTriedIfNeeded() + { + $a1 = $this->_createMockAuthenticator('PLAIN'); + $a2 = $this->_createMockAuthenticator('LOGIN'); + + $a1->shouldReceive('authenticate') + ->once() + ->with($this->_agent, 'jack', 'pass') + ->andReturn(false); + $a2->shouldReceive('authenticate') + ->once() + ->with($this->_agent, 'jack', 'pass') + ->andReturn(true); + + $auth = $this->_createHandler(array($a1, $a2)); + $auth->setUsername('jack'); + $auth->setPassword('pass'); + + $auth->setKeywordParams(array('PLAIN', 'LOGIN')); + $auth->afterEhlo($this->_agent); + } + + public function testFirstAuthenticatorToPassBreaksChain() + { + $a1 = $this->_createMockAuthenticator('PLAIN'); + $a2 = $this->_createMockAuthenticator('LOGIN'); + $a3 = $this->_createMockAuthenticator('CRAM-MD5'); + + $a1->shouldReceive('authenticate') + ->once() + ->with($this->_agent, 'jack', 'pass') + ->andReturn(false); + $a2->shouldReceive('authenticate') + ->once() + ->with($this->_agent, 'jack', 'pass') + ->andReturn(true); + $a3->shouldReceive('authenticate') + ->never() + ->with($this->_agent, 'jack', 'pass'); + + $auth = $this->_createHandler(array($a1, $a2)); + $auth->setUsername('jack'); + $auth->setPassword('pass'); + + $auth->setKeywordParams(array('PLAIN', 'LOGIN', 'CRAM-MD5')); + $auth->afterEhlo($this->_agent); + } + + // -- Private helpers + + private function _createHandler($authenticators) + { + return new Swift_Transport_Esmtp_AuthHandler($authenticators); + } + + private function _createMockAuthenticator($type) + { + $authenticator = $this->getMockery('Swift_Transport_Esmtp_Authenticator')->shouldIgnoreMissing(); + $authenticator->shouldReceive('getAuthKeyword') + ->zeroOrMoreTimes() + ->andReturn($type); + + return $authenticator; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/EsmtpTransport/ExtensionSupportTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/EsmtpTransport/ExtensionSupportTest.php new file mode 100644 index 0000000000..8d6321f30e --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/EsmtpTransport/ExtensionSupportTest.php @@ -0,0 +1,528 @@ +_getBuffer(); + $smtp = $this->_getTransport($buf); + $ext1 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + $ext2 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + + $ext1->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('AUTH'); + $ext1->shouldReceive('getPriorityOver') + ->zeroOrMoreTimes() + ->with('STARTTLS') + ->andReturn(1); + $ext2->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('STARTTLS'); + $ext2->shouldReceive('getPriorityOver') + ->zeroOrMoreTimes() + ->with('AUTH') + ->andReturn(-1); + $this->_finishBuffer($buf); + + $smtp->setExtensionHandlers(array($ext1, $ext2)); + $this->assertEquals(array($ext2, $ext1), $smtp->getExtensionHandlers()); + } + + public function testHandlersAreNotifiedOfParams() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $ext1 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + $ext2 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 server.com foo\r\n"); + $buf->shouldReceive('write') + ->once() + ->with('~^EHLO .*?\r\n$~D') + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250-ServerName.tld\r\n"); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250-AUTH PLAIN LOGIN\r\n"); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250 SIZE=123456\r\n"); + + $ext1->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('AUTH'); + $ext1->shouldReceive('setKeywordParams') + ->once() + ->with(array('PLAIN', 'LOGIN')); + $ext2->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('SIZE'); + $ext2->shouldReceive('setKeywordParams') + ->zeroOrMoreTimes() + ->with(array('123456')); + $this->_finishBuffer($buf); + + $smtp->setExtensionHandlers(array($ext1, $ext2)); + $smtp->start(); + } + + public function testSupportedExtensionHandlersAreRunAfterEhlo() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $ext1 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + $ext2 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + $ext3 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 server.com foo\r\n"); + $buf->shouldReceive('write') + ->once() + ->with('~^EHLO .*?\r\n$~D') + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250-ServerName.tld\r\n"); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250-AUTH PLAIN LOGIN\r\n"); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250 SIZE=123456\r\n"); + + $ext1->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('AUTH'); + $ext1->shouldReceive('afterEhlo') + ->once() + ->with($smtp); + $ext2->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('SIZE'); + $ext2->shouldReceive('afterEhlo') + ->zeroOrMoreTimes() + ->with($smtp); + $ext3->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('STARTTLS'); + $ext3->shouldReceive('afterEhlo') + ->never() + ->with($smtp); + $this->_finishBuffer($buf); + + $smtp->setExtensionHandlers(array($ext1, $ext2, $ext3)); + $smtp->start(); + } + + public function testExtensionsCanModifyMailFromParams() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(); + $smtp = new Swift_Transport_EsmtpTransport($buf, array(), $dispatcher); + $ext1 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + $ext2 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + $ext3 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(array('me@domain' => 'Me')); + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('foo@bar' => null)); + + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 server.com foo\r\n"); + $buf->shouldReceive('write') + ->once() + ->with('~^EHLO .*?\r\n$~D') + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250-ServerName.tld\r\n"); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250-AUTH PLAIN LOGIN\r\n"); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250 SIZE=123456\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("MAIL FROM: FOO ZIP\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn("250 OK\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:\r\n") + ->andReturn(3); + $buf->shouldReceive('readLine') + ->once() + ->with(3) + ->andReturn("250 OK\r\n"); + $this->_finishBuffer($buf); + + $ext1->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('AUTH'); + $ext1->shouldReceive('getMailParams') + ->once() + ->andReturn('FOO'); + $ext1->shouldReceive('getPriorityOver') + ->zeroOrMoreTimes() + ->with('AUTH') + ->andReturn(-1); + $ext2->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('SIZE'); + $ext2->shouldReceive('getMailParams') + ->once() + ->andReturn('ZIP'); + $ext2->shouldReceive('getPriorityOver') + ->zeroOrMoreTimes() + ->with('AUTH') + ->andReturn(1); + $ext3->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('STARTTLS'); + $ext3->shouldReceive('getMailParams') + ->never(); + + $smtp->setExtensionHandlers(array($ext1, $ext2, $ext3)); + $smtp->start(); + $smtp->send($message); + } + + public function testExtensionsCanModifyRcptParams() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(); + $smtp = new Swift_Transport_EsmtpTransport($buf, array(), $dispatcher); + $ext1 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + $ext2 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + $ext3 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(array('me@domain' => 'Me')); + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('foo@bar' => null)); + + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 server.com foo\r\n"); + $buf->shouldReceive('write') + ->once() + ->with('~^EHLO .+?\r\n$~D') + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250-ServerName.tld\r\n"); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250-AUTH PLAIN LOGIN\r\n"); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250 SIZE=123456\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("MAIL FROM:\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn("250 OK\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO: FOO ZIP\r\n") + ->andReturn(3); + $buf->shouldReceive('readLine') + ->once() + ->with(3) + ->andReturn("250 OK\r\n"); + $this->_finishBuffer($buf); + + $ext1->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('AUTH'); + $ext1->shouldReceive('getRcptParams') + ->once() + ->andReturn('FOO'); + $ext1->shouldReceive('getPriorityOver') + ->zeroOrMoreTimes() + ->with('AUTH') + ->andReturn(-1); + $ext2->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('SIZE'); + $ext2->shouldReceive('getRcptParams') + ->once() + ->andReturn('ZIP'); + $ext2->shouldReceive('getPriorityOver') + ->zeroOrMoreTimes() + ->with('AUTH') + ->andReturn(1); + $ext3->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('STARTTLS'); + $ext3->shouldReceive('getRcptParams') + ->never(); + + $smtp->setExtensionHandlers(array($ext1, $ext2, $ext3)); + $smtp->start(); + $smtp->send($message); + } + + public function testExtensionsAreNotifiedOnCommand() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $ext1 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + $ext2 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + $ext3 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 server.com foo\r\n"); + $buf->shouldReceive('write') + ->once() + ->with('~^EHLO .+?\r\n$~D') + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250-ServerName.tld\r\n"); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250-AUTH PLAIN LOGIN\r\n"); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250 SIZE=123456\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("FOO\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn("250 Cool\r\n"); + $this->_finishBuffer($buf); + + $ext1->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('AUTH'); + $ext1->shouldReceive('onCommand') + ->once() + ->with($smtp, "FOO\r\n", array(250, 251), \Mockery::any(), \Mockery::any()); + $ext2->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('SIZE'); + $ext2->shouldReceive('onCommand') + ->once() + ->with($smtp, "FOO\r\n", array(250, 251), \Mockery::any(), \Mockery::any()); + $ext3->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('STARTTLS'); + $ext3->shouldReceive('onCommand') + ->never() + ->with(\Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any()); + + $smtp->setExtensionHandlers(array($ext1, $ext2, $ext3)); + $smtp->start(); + $smtp->executeCommand("FOO\r\n", array(250, 251)); + } + + public function testChainOfCommandAlgorithmWhenNotifyingExtensions() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $ext1 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + $ext2 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + $ext3 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 server.com foo\r\n"); + $buf->shouldReceive('write') + ->once() + ->with('~^EHLO .+?\r\n$~D') + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250-ServerName.tld\r\n"); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250-AUTH PLAIN LOGIN\r\n"); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250 SIZE=123456\r\n"); + $buf->shouldReceive('write') + ->never() + ->with("FOO\r\n"); + $this->_finishBuffer($buf); + + $ext1->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('AUTH'); + $ext1->shouldReceive('onCommand') + ->once() + ->with($smtp, "FOO\r\n", array(250, 251), \Mockery::any(), \Mockery::any()) + ->andReturnUsing(function ($a, $b, $c, $d, &$e) { + $e = true; + + return '250 ok'; + }); + $ext2->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('SIZE'); + $ext2->shouldReceive('onCommand') + ->never() + ->with(\Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any()); + + $ext3->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('STARTTLS'); + $ext3->shouldReceive('onCommand') + ->never() + ->with(\Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any()); + + $smtp->setExtensionHandlers(array($ext1, $ext2, $ext3)); + $smtp->start(); + $smtp->executeCommand("FOO\r\n", array(250, 251)); + } + + public function testExtensionsCanExposeMixinMethods() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $ext1 = $this->getMockery('Swift_Transport_EsmtpHandlerMixin')->shouldIgnoreMissing(); + $ext2 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + + $ext1->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('AUTH'); + $ext1->shouldReceive('exposeMixinMethods') + ->zeroOrMoreTimes() + ->andReturn(array('setUsername', 'setPassword')); + $ext1->shouldReceive('setUsername') + ->once() + ->with('mick'); + $ext1->shouldReceive('setPassword') + ->once() + ->with('pass'); + $ext2->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('STARTTLS'); + $this->_finishBuffer($buf); + + $smtp->setExtensionHandlers(array($ext1, $ext2)); + $smtp->setUsername('mick'); + $smtp->setPassword('pass'); + } + + public function testMixinMethodsBeginningWithSetAndNullReturnAreFluid() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $ext1 = $this->getMockery('Swift_Transport_EsmtpHandlerMixin')->shouldIgnoreMissing(); + $ext2 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + + $ext1->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('AUTH'); + $ext1->shouldReceive('exposeMixinMethods') + ->zeroOrMoreTimes() + ->andReturn(array('setUsername', 'setPassword')); + $ext1->shouldReceive('setUsername') + ->once() + ->with('mick') + ->andReturn(null); + $ext1->shouldReceive('setPassword') + ->once() + ->with('pass') + ->andReturn(null); + $ext2->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('STARTTLS'); + $this->_finishBuffer($buf); + + $smtp->setExtensionHandlers(array($ext1, $ext2)); + $ret = $smtp->setUsername('mick'); + $this->assertEquals($smtp, $ret); + $ret = $smtp->setPassword('pass'); + $this->assertEquals($smtp, $ret); + } + + public function testMixinSetterWhichReturnValuesAreNotFluid() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $ext1 = $this->getMockery('Swift_Transport_EsmtpHandlerMixin')->shouldIgnoreMissing(); + $ext2 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + + $ext1->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('AUTH'); + $ext1->shouldReceive('exposeMixinMethods') + ->zeroOrMoreTimes() + ->andReturn(array('setUsername', 'setPassword')); + $ext1->shouldReceive('setUsername') + ->once() + ->with('mick') + ->andReturn('x'); + $ext1->shouldReceive('setPassword') + ->once() + ->with('pass') + ->andReturn('x'); + $ext2->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('STARTTLS'); + $this->_finishBuffer($buf); + + $smtp->setExtensionHandlers(array($ext1, $ext2)); + $this->assertEquals('x', $smtp->setUsername('mick')); + $this->assertEquals('x', $smtp->setPassword('pass')); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/EsmtpTransportTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/EsmtpTransportTest.php new file mode 100644 index 0000000000..e6cca15b0e --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/EsmtpTransportTest.php @@ -0,0 +1,297 @@ +_createEventDispatcher(); + } + + return new Swift_Transport_EsmtpTransport($buf, array(), $dispatcher); + } + + public function testHostCanBeSetAndFetched() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $smtp->setHost('foo'); + $this->assertEquals('foo', $smtp->getHost(), '%s: Host should be returned'); + } + + public function testPortCanBeSetAndFetched() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $smtp->setPort(25); + $this->assertEquals(25, $smtp->getPort(), '%s: Port should be returned'); + } + + public function testTimeoutCanBeSetAndFetched() + { + $buf = $this->_getBuffer(); + $buf->shouldReceive('setParam') + ->once() + ->with('timeout', 10); + + $smtp = $this->_getTransport($buf); + $smtp->setTimeout(10); + $this->assertEquals(10, $smtp->getTimeout(), '%s: Timeout should be returned'); + } + + public function testEncryptionCanBeSetAndFetched() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $smtp->setEncryption('tls'); + $this->assertEquals('tls', $smtp->getEncryption(), '%s: Crypto should be returned'); + } + + public function testStartSendsHeloToInitiate() + { + //Overridden for EHLO instead + } + + public function testStartSendsEhloToInitiate() + { + /* -- RFC 2821, 3.2. + + 3.2 Client Initiation + + Once the server has sent the welcoming message and the client has + received it, the client normally sends the EHLO command to the + server, indicating the client's identity. In addition to opening the + session, use of EHLO indicates that the client is able to process + service extensions and requests that the server provide a list of the + extensions it supports. Older SMTP systems which are unable to + support service extensions and contemporary clients which do not + require service extensions in the mail session being initiated, MAY + use HELO instead of EHLO. Servers MUST NOT return the extended + EHLO-style response to a HELO command. For a particular connection + attempt, if the server returns a "command not recognized" response to + EHLO, the client SHOULD be able to fall back and send HELO. + + In the EHLO command the host sending the command identifies itself; + the command may be interpreted as saying "Hello, I am " (and, + in the case of EHLO, "and I support service extension requests"). + + -- RFC 2281, 4.1.1.1. + + ehlo = "EHLO" SP Domain CRLF + helo = "HELO" SP Domain CRLF + + -- RFC 2821, 4.3.2. + + EHLO or HELO + S: 250 + E: 504, 550 + + */ + + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 some.server.tld bleh\r\n"); + $buf->shouldReceive('write') + ->once() + ->with('~^EHLO .+?\r\n$~D') + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('250 ServerName'."\r\n"); + + $this->_finishBuffer($buf); + try { + $smtp->start(); + } catch (Exception $e) { + $this->fail('Starting Esmtp should send EHLO and accept 250 response'); + } + } + + public function testHeloIsUsedAsFallback() + { + /* -- RFC 2821, 4.1.4. + + If the EHLO command is not acceptable to the SMTP server, 501, 500, + or 502 failure replies MUST be returned as appropriate. The SMTP + server MUST stay in the same state after transmitting these replies + that it was in before the EHLO was received. + */ + + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 some.server.tld bleh\r\n"); + $buf->shouldReceive('write') + ->once() + ->with('~^EHLO .+?\r\n$~D') + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('501 WTF'."\r\n"); + $buf->shouldReceive('write') + ->once() + ->with('~^HELO .+?\r\n$~D') + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn('250 HELO'."\r\n"); + + $this->_finishBuffer($buf); + try { + $smtp->start(); + } catch (Exception $e) { + $this->fail( + 'Starting Esmtp should fallback to HELO if needed and accept 250 response' + ); + } + } + + public function testInvalidHeloResponseCausesException() + { + //Overridden to first try EHLO + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 some.server.tld bleh\r\n"); + $buf->shouldReceive('write') + ->once() + ->with('~^EHLO .+?\r\n$~D') + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('501 WTF'."\r\n"); + $buf->shouldReceive('write') + ->once() + ->with('~^HELO .+?\r\n$~D') + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn('504 WTF'."\r\n"); + $this->_finishBuffer($buf); + + try { + $this->assertFalse($smtp->isStarted(), '%s: SMTP should begin non-started'); + $smtp->start(); + $this->fail('Non 250 HELO response should raise Exception'); + } catch (Exception $e) { + $this->assertFalse($smtp->isStarted(), '%s: SMTP start() should have failed'); + } + } + + public function testDomainNameIsPlacedInEhlo() + { + /* -- RFC 2821, 4.1.4. + + The SMTP client MUST, if possible, ensure that the domain parameter + to the EHLO command is a valid principal host name (not a CNAME or MX + name) for its host. If this is not possible (e.g., when the client's + address is dynamically assigned and the client does not have an + obvious name), an address literal SHOULD be substituted for the + domain name and supplemental information provided that will assist in + identifying the client. + */ + + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 some.server.tld bleh\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("EHLO mydomain.com\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('250 ServerName'."\r\n"); + + $this->_finishBuffer($buf); + $smtp->setLocalDomain('mydomain.com'); + $smtp->start(); + } + + public function testDomainNameIsPlacedInHelo() + { + //Overridden to include ESMTP + /* -- RFC 2821, 4.1.4. + + The SMTP client MUST, if possible, ensure that the domain parameter + to the EHLO command is a valid principal host name (not a CNAME or MX + name) for its host. If this is not possible (e.g., when the client's + address is dynamically assigned and the client does not have an + obvious name), an address literal SHOULD be substituted for the + domain name and supplemental information provided that will assist in + identifying the client. + */ + + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 some.server.tld bleh\r\n"); + $buf->shouldReceive('write') + ->once() + ->with('~^EHLO .+?\r\n$~D') + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('501 WTF'."\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("HELO mydomain.com\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn('250 ServerName'."\r\n"); + + $this->_finishBuffer($buf); + $smtp->setLocalDomain('mydomain.com'); + $smtp->start(); + } + + public function testFluidInterface() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $buf->shouldReceive('setParam') + ->once() + ->with('timeout', 30); + + $ref = $smtp + ->setHost('foo') + ->setPort(25) + ->setEncryption('tls') + ->setTimeout(30) + ; + $this->assertEquals($ref, $smtp); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/FailoverTransportTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/FailoverTransportTest.php new file mode 100644 index 0000000000..8d80f35930 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/FailoverTransportTest.php @@ -0,0 +1,520 @@ +getMockery('Swift_Mime_Message'); + $message2 = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $connectionState = false; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState) { + return $connectionState; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState) { + if (!$connectionState) { + $connectionState = true; + } + }); + $t1->shouldReceive('send') + ->twice() + ->with(\Mockery::anyOf($message1, $message2), \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState) { + if ($connectionState) { + return 1; + } + }); + $t2->shouldReceive('start')->never(); + $t2->shouldReceive('send')->never(); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + $this->assertEquals(1, $transport->send($message1)); + $this->assertEquals(1, $transport->send($message2)); + } + + public function testMessageCanBeTriedOnNextTransportIfExceptionThrown() + { + $e = new Swift_TransportException('b0rken'); + + $message = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $connectionState1 = false; + $connectionState2 = false; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState1) { + if (!$connectionState1) { + $connectionState1 = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1, $e) { + if ($connectionState1) { + throw $e; + } + }); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if (!$connectionState2) { + $connectionState2 = true; + } + }); + $t2->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2, $e) { + if ($connectionState2) { + return 1; + } + }); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + $this->assertEquals(1, $transport->send($message)); + } + + public function testZeroIsReturnedIfTransportReturnsZero() + { + $message = $this->getMockery('Swift_Mime_Message')->shouldIgnoreMissing(); + $t1 = $this->getMockery('Swift_Transport')->shouldIgnoreMissing(); + + $connectionState = false; + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState) { + return $connectionState; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState) { + if (!$connectionState) { + $connectionState = true; + } + }); + $testCase = $this; + $t1->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState, $testCase) { + if (!$connectionState) { + $testCase->fail(); + } + + return 0; + }); + + $transport = $this->_getTransport(array($t1)); + $transport->start(); + $this->assertEquals(0, $transport->send($message)); + } + + public function testTransportsWhichThrowExceptionsAreNotRetried() + { + $e = new Swift_TransportException('maur b0rken'); + + $message1 = $this->getMockery('Swift_Mime_Message'); + $message2 = $this->getMockery('Swift_Mime_Message'); + $message3 = $this->getMockery('Swift_Mime_Message'); + $message4 = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $connectionState1 = false; + $connectionState2 = false; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState1) { + if (!$connectionState1) { + $connectionState1 = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message1, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1, $e) { + if ($connectionState1) { + throw $e; + } + }); + $t1->shouldReceive('send') + ->never() + ->with($message2, \Mockery::any()); + $t1->shouldReceive('send') + ->never() + ->with($message3, \Mockery::any()); + $t1->shouldReceive('send') + ->never() + ->with($message4, \Mockery::any()); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if (!$connectionState2) { + $connectionState2 = true; + } + }); + $t2->shouldReceive('send') + ->times(4) + ->with(\Mockery::anyOf($message1, $message2, $message3, $message4), \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2, $e) { + if ($connectionState2) { + return 1; + } + }); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + $this->assertEquals(1, $transport->send($message1)); + $this->assertEquals(1, $transport->send($message2)); + $this->assertEquals(1, $transport->send($message3)); + $this->assertEquals(1, $transport->send($message4)); + } + + public function testExceptionIsThrownIfAllTransportsDie() + { + $e = new Swift_TransportException('b0rken'); + + $message = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $connectionState1 = false; + $connectionState2 = false; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState1) { + if (!$connectionState1) { + $connectionState1 = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1, $e) { + if ($connectionState1) { + throw $e; + } + }); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if (!$connectionState2) { + $connectionState2 = true; + } + }); + $t2->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2, $e) { + if ($connectionState2) { + throw $e; + } + }); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + try { + $transport->send($message); + $this->fail('All transports failed so Exception should be thrown'); + } catch (Exception $e) { + } + } + + public function testStoppingTransportStopsAllDelegates() + { + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + + $connectionState1 = true; + $connectionState2 = true; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('stop') + ->once() + ->andReturnUsing(function () use (&$connectionState1) { + if ($connectionState1) { + $connectionState1 = false; + } + }); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('stop') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if ($connectionState2) { + $connectionState2 = false; + } + }); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + $transport->stop(); + } + + public function testTransportShowsAsNotStartedIfAllDelegatesDead() + { + $e = new Swift_TransportException('b0rken'); + + $message = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + + $connectionState1 = false; + $connectionState2 = false; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState1) { + if (!$connectionState1) { + $connectionState1 = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1, $e) { + if ($connectionState1) { + $connectionState1 = false; + throw $e; + } + }); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if (!$connectionState2) { + $connectionState2 = true; + } + }); + $t2->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2, $e) { + if ($connectionState2) { + $connectionState2 = false; + throw $e; + } + }); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + $this->assertTrue($transport->isStarted()); + try { + $transport->send($message); + $this->fail('All transports failed so Exception should be thrown'); + } catch (Exception $e) { + $this->assertFalse($transport->isStarted()); + } + } + + public function testRestartingTransportRestartsDeadDelegates() + { + $e = new Swift_TransportException('b0rken'); + + $message1 = $this->getMockery('Swift_Mime_Message'); + $message2 = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + + $connectionState1 = false; + $connectionState2 = false; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('start') + ->twice() + ->andReturnUsing(function () use (&$connectionState1) { + if (!$connectionState1) { + $connectionState1 = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message1, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1, $e) { + if ($connectionState1) { + $connectionState1 = false; + throw $e; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message2, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1) { + if ($connectionState1) { + return 10; + } + }); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if (!$connectionState2) { + $connectionState2 = true; + } + }); + $t2->shouldReceive('send') + ->once() + ->with($message1, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2, $e) { + if ($connectionState2) { + $connectionState2 = false; + throw $e; + } + }); + $t2->shouldReceive('send') + ->never() + ->with($message2, \Mockery::any()); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + $this->assertTrue($transport->isStarted()); + try { + $transport->send($message1); + $this->fail('All transports failed so Exception should be thrown'); + } catch (Exception $e) { + $this->assertFalse($transport->isStarted()); + } + //Restart and re-try + $transport->start(); + $this->assertTrue($transport->isStarted()); + $this->assertEquals(10, $transport->send($message2)); + } + + public function testFailureReferenceIsPassedToDelegates() + { + $failures = array(); + + $message = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + + $connectionState = false; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use ($connectionState) { + return $connectionState; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use ($connectionState) { + if (!$connectionState) { + $connectionState = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message, $failures) + ->andReturnUsing(function () use ($connectionState) { + if ($connectionState) { + return 1; + } + }); + + $transport = $this->_getTransport(array($t1)); + $transport->start(); + $transport->send($message, $failures); + } + + public function testRegisterPluginDelegatesToLoadedTransports() + { + $plugin = $this->_createPlugin(); + + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $t1->shouldReceive('registerPlugin') + ->once() + ->with($plugin); + $t2->shouldReceive('registerPlugin') + ->once() + ->with($plugin); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->registerPlugin($plugin); + } + + // -- Private helpers + + private function _getTransport(array $transports) + { + $transport = new Swift_Transport_FailoverTransport(); + $transport->setTransports($transports); + + return $transport; + } + + private function _createPlugin() + { + return $this->getMockery('Swift_Events_EventListener'); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/LoadBalancedTransportTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/LoadBalancedTransportTest.php new file mode 100644 index 0000000000..cc7297b15a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/LoadBalancedTransportTest.php @@ -0,0 +1,751 @@ +getMockery('Swift_Mime_Message'); + $message2 = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $connectionState1 = false; + $connectionState2 = false; + + $testCase = $this; + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState1) { + if (!$connectionState1) { + $connectionState1 = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message1, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1, $testCase) { + if ($connectionState1) { + return 1; + } + $testCase->fail(); + }); + $t1->shouldReceive('send') + ->never() + ->with($message2, \Mockery::any()); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if (!$connectionState2) { + $connectionState2 = true; + } + }); + $t2->shouldReceive('send') + ->once() + ->with($message2, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2, $testCase) { + if ($connectionState2) { + return 1; + } + $testCase->fail(); + }); + $t2->shouldReceive('send') + ->never() + ->with($message1, \Mockery::any()); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + $this->assertEquals(1, $transport->send($message1)); + $this->assertEquals(1, $transport->send($message2)); + } + + public function testTransportsAreReusedInRotatingFashion() + { + $message1 = $this->getMockery('Swift_Mime_Message'); + $message2 = $this->getMockery('Swift_Mime_Message'); + $message3 = $this->getMockery('Swift_Mime_Message'); + $message4 = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $connectionState1 = false; + $connectionState2 = false; + + $testCase = $this; + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState1) { + if (!$connectionState1) { + $connectionState1 = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message1, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1, $testCase) { + if ($connectionState1) { + return 1; + } + $testCase->fail(); + }); + $t1->shouldReceive('send') + ->never() + ->with($message2, \Mockery::any()); + $t1->shouldReceive('send') + ->once() + ->with($message3, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1, $testCase) { + if ($connectionState1) { + return 1; + } + $testCase->fail(); + }); + $t1->shouldReceive('send') + ->never() + ->with($message4, \Mockery::any()); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if (!$connectionState2) { + $connectionState2 = true; + } + }); + $t2->shouldReceive('send') + ->once() + ->with($message2, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2, $testCase) { + if ($connectionState2) { + return 1; + } + $testCase->fail(); + }); + $t2->shouldReceive('send') + ->never() + ->with($message1, \Mockery::any()); + $t2->shouldReceive('send') + ->once() + ->with($message4, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2, $testCase) { + if ($connectionState2) { + return 1; + } + $testCase->fail(); + }); + $t2->shouldReceive('send') + ->never() + ->with($message3, \Mockery::any()); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + + $this->assertEquals(1, $transport->send($message1)); + $this->assertEquals(1, $transport->send($message2)); + $this->assertEquals(1, $transport->send($message3)); + $this->assertEquals(1, $transport->send($message4)); + } + + public function testMessageCanBeTriedOnNextTransportIfExceptionThrown() + { + $e = new Swift_TransportException('b0rken'); + + $message = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $connectionState1 = false; + $connectionState2 = false; + + $testCase = $this; + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState1) { + if (!$connectionState1) { + $connectionState1 = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1, $e, $testCase) { + if ($connectionState1) { + throw $e; + } + $testCase->fail(); + }); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if (!$connectionState2) { + $connectionState2 = true; + } + }); + $t2->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2, $testCase) { + if ($connectionState2) { + return 1; + } + $testCase->fail(); + }); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + $this->assertEquals(1, $transport->send($message)); + } + + public function testMessageIsTriedOnNextTransportIfZeroReturned() + { + $message = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $connectionState1 = false; + $connectionState2 = false; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState1) { + if (!$connectionState1) { + $connectionState1 = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1) { + if ($connectionState1) { + return 0; + } + + return 1; + }); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if (!$connectionState2) { + $connectionState2 = true; + } + }); + $t2->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2) { + if ($connectionState2) { + return 1; + } + + return 0; + }); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + $this->assertEquals(1, $transport->send($message)); + } + + public function testZeroIsReturnedIfAllTransportsReturnZero() + { + $message = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $connectionState1 = false; + $connectionState2 = false; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState1) { + if (!$connectionState1) { + $connectionState1 = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1) { + if ($connectionState1) { + return 0; + } + + return 1; + }); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if (!$connectionState2) { + $connectionState2 = true; + } + }); + $t2->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2) { + if ($connectionState2) { + return 0; + } + + return 1; + }); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + $this->assertEquals(0, $transport->send($message)); + } + + public function testTransportsWhichThrowExceptionsAreNotRetried() + { + $e = new Swift_TransportException('maur b0rken'); + + $message1 = $this->getMockery('Swift_Mime_Message'); + $message2 = $this->getMockery('Swift_Mime_Message'); + $message3 = $this->getMockery('Swift_Mime_Message'); + $message4 = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $connectionState1 = false; + $connectionState2 = false; + + $testCase = $this; + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState1) { + if (!$connectionState1) { + $connectionState1 = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message1, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1, $e, $testCase) { + if ($connectionState1) { + throw $e; + } + $testCase->fail(); + }); + $t1->shouldReceive('send') + ->never() + ->with($message2, \Mockery::any()); + $t1->shouldReceive('send') + ->never() + ->with($message3, \Mockery::any()); + $t1->shouldReceive('send') + ->never() + ->with($message4, \Mockery::any()); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if (!$connectionState2) { + $connectionState2 = true; + } + }); + $t2->shouldReceive('send') + ->times(4) + ->with(\Mockery::anyOf($message1, $message3, $message3, $message4), \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2, $testCase) { + if ($connectionState2) { + return 1; + } + $testCase->fail(); + }); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + $this->assertEquals(1, $transport->send($message1)); + $this->assertEquals(1, $transport->send($message2)); + $this->assertEquals(1, $transport->send($message3)); + $this->assertEquals(1, $transport->send($message4)); + } + + public function testExceptionIsThrownIfAllTransportsDie() + { + $e = new Swift_TransportException('b0rken'); + + $message = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $connectionState1 = false; + $connectionState2 = false; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState1) { + if (!$connectionState1) { + $connectionState1 = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1, $e) { + if ($connectionState1) { + throw $e; + } + }); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if (!$connectionState2) { + $connectionState2 = true; + } + }); + $t2->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2, $e) { + if ($connectionState2) { + throw $e; + } + }); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + try { + $transport->send($message); + $this->fail('All transports failed so Exception should be thrown'); + } catch (Exception $e) { + } + } + + public function testStoppingTransportStopsAllDelegates() + { + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $connectionState1 = true; + $connectionState2 = true; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('stop') + ->once() + ->andReturnUsing(function () use (&$connectionState1) { + if ($connectionState1) { + $connectionState1 = false; + } + }); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('stop') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if ($connectionState2) { + $connectionState2 = false; + } + }); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + $transport->stop(); + } + + public function testTransportShowsAsNotStartedIfAllDelegatesDead() + { + $e = new Swift_TransportException('b0rken'); + + $message = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $connectionState1 = false; + $connectionState2 = false; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState1) { + if (!$connectionState1) { + $connectionState1 = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1, $e) { + if ($connectionState1) { + throw $e; + } + }); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if (!$connectionState2) { + $connectionState2 = true; + } + }); + $t2->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2, $e) { + if ($connectionState2) { + throw $e; + } + }); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + $this->assertTrue($transport->isStarted()); + try { + $transport->send($message); + $this->fail('All transports failed so Exception should be thrown'); + } catch (Exception $e) { + $this->assertFalse($transport->isStarted()); + } + } + + public function testRestartingTransportRestartsDeadDelegates() + { + $e = new Swift_TransportException('b0rken'); + + $message1 = $this->getMockery('Swift_Mime_Message'); + $message2 = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $connectionState1 = false; + $connectionState2 = false; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('start') + ->twice() + ->andReturnUsing(function () use (&$connectionState1) { + if (!$connectionState1) { + $connectionState1 = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message1, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1, $e) { + if ($connectionState1) { + $connectionState1 = false; + throw $e; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message2, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1, $e) { + if ($connectionState1) { + return 10; + } + }); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if (!$connectionState2) { + $connectionState2 = true; + } + }); + $t2->shouldReceive('send') + ->once() + ->with($message1, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2, $e) { + if ($connectionState2) { + throw $e; + } + }); + $t2->shouldReceive('send') + ->never() + ->with($message2, \Mockery::any()); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + $this->assertTrue($transport->isStarted()); + try { + $transport->send($message1); + $this->fail('All transports failed so Exception should be thrown'); + } catch (Exception $e) { + $this->assertFalse($transport->isStarted()); + } + //Restart and re-try + $transport->start(); + $this->assertTrue($transport->isStarted()); + $this->assertEquals(10, $transport->send($message2)); + } + + public function testFailureReferenceIsPassedToDelegates() + { + $failures = array(); + $testCase = $this; + + $message = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $connectionState = false; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState) { + return $connectionState; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState) { + if (!$connectionState) { + $connectionState = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message, \Mockery::on(function (&$var) use (&$failures, $testCase) { + return $testCase->varsAreReferences($var, $failures); + })) + ->andReturnUsing(function () use (&$connectionState) { + if ($connectionState) { + return 1; + } + }); + + $transport = $this->_getTransport(array($t1)); + $transport->start(); + $transport->send($message, $failures); + } + + public function testRegisterPluginDelegatesToLoadedTransports() + { + $plugin = $this->_createPlugin(); + + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + + $t1->shouldReceive('registerPlugin') + ->once() + ->with($plugin); + $t2->shouldReceive('registerPlugin') + ->once() + ->with($plugin); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->registerPlugin($plugin); + } + + /** + * Adapted from Yay_Matchers_ReferenceMatcher. + */ + public function varsAreReferences(&$ref1, &$ref2) + { + if (is_object($ref2)) { + return ($ref1 === $ref2); + } + if ($ref1 !== $ref2) { + return false; + } + + $copy = $ref2; + $randomString = uniqid('yay'); + $ref2 = $randomString; + $isRef = ($ref1 === $ref2); + $ref2 = $copy; + + return $isRef; + } + + // -- Private helpers + + private function _getTransport(array $transports) + { + $transport = new Swift_Transport_LoadBalancedTransport(); + $transport->setTransports($transports); + + return $transport; + } + + private function _createPlugin() + { + return $this->getMockery('Swift_Events_EventListener'); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/MailTransportTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/MailTransportTest.php new file mode 100644 index 0000000000..96e9943dbd --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/MailTransportTest.php @@ -0,0 +1,535 @@ +_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $headers = $this->_createHeaders(); + $message = $this->_createMessageWithRecipient($headers); + + $invoker->shouldReceive('mail') + ->once(); + + $transport->send($message); + } + + public function testTransportUsesToFieldBodyInSending() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $to = $this->_createHeader(); + $headers = $this->_createHeaders(array( + 'To' => $to, + )); + $message = $this->_createMessageWithRecipient($headers); + + $to->shouldReceive('getFieldBody') + ->zeroOrMoreTimes() + ->andReturn('Foo '); + $invoker->shouldReceive('mail') + ->once() + ->with('Foo ', \Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any()); + + $transport->send($message); + } + + public function testTransportUsesSubjectFieldBodyInSending() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $subj = $this->_createHeader(); + $headers = $this->_createHeaders(array( + 'Subject' => $subj, + )); + $message = $this->_createMessageWithRecipient($headers); + + $subj->shouldReceive('getFieldBody') + ->zeroOrMoreTimes() + ->andReturn('Thing'); + $invoker->shouldReceive('mail') + ->once() + ->with(\Mockery::any(), 'Thing', \Mockery::any(), \Mockery::any(), \Mockery::any()); + + $transport->send($message); + } + + public function testTransportUsesBodyOfMessage() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $headers = $this->_createHeaders(); + $message = $this->_createMessageWithRecipient($headers); + + $message->shouldReceive('toString') + ->zeroOrMoreTimes() + ->andReturn( + "To: Foo \r\n". + "\r\n". + 'This body' + ); + $invoker->shouldReceive('mail') + ->once() + ->with(\Mockery::any(), \Mockery::any(), 'This body', \Mockery::any(), \Mockery::any()); + + $transport->send($message); + } + + public function testTransportSettingUsingReturnPathForExtraParams() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $headers = $this->_createHeaders(); + $message = $this->_createMessageWithRecipient($headers); + + $message->shouldReceive('getReturnPath') + ->zeroOrMoreTimes() + ->andReturn( + 'foo@bar' + ); + $invoker->shouldReceive('mail') + ->once() + ->with(\Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any(), '-ffoo@bar'); + + $transport->send($message); + } + + public function testTransportSettingEmptyExtraParams() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $headers = $this->_createHeaders(); + $message = $this->_createMessageWithRecipient($headers); + + $message->shouldReceive('getReturnPath') + ->zeroOrMoreTimes() + ->andReturn(null); + $message->shouldReceive('getSender') + ->zeroOrMoreTimes() + ->andReturn(null); + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(null); + $invoker->shouldReceive('mail') + ->once() + ->with(\Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any(), null); + + $transport->send($message); + } + + public function testTransportSettingSettingExtraParamsWithF() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + $transport->setExtraParams('-x\'foo\' -f%s'); + + $headers = $this->_createHeaders(); + $message = $this->_createMessageWithRecipient($headers); + + $message->shouldReceive('getReturnPath') + ->zeroOrMoreTimes() + ->andReturn( + 'foo@bar' + ); + $message->shouldReceive('getSender') + ->zeroOrMoreTimes() + ->andReturn(null); + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(null); + $invoker->shouldReceive('mail') + ->once() + ->with(\Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any(), '-x\'foo\' -ffoo@bar'); + + $transport->send($message); + } + + public function testTransportSettingSettingExtraParamsWithoutF() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + $transport->setExtraParams('-x\'foo\''); + + $headers = $this->_createHeaders(); + $message = $this->_createMessageWithRecipient($headers); + + $message->shouldReceive('getReturnPath') + ->zeroOrMoreTimes() + ->andReturn( + 'foo@bar' + ); + $message->shouldReceive('getSender') + ->zeroOrMoreTimes() + ->andReturn(null); + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(null); + $invoker->shouldReceive('mail') + ->once() + ->with(\Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any(), '-x\'foo\''); + + $transport->send($message); + } + + public function testTransportSettingInvalidFromEmail() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $headers = $this->_createHeaders(); + $message = $this->_createMessageWithRecipient($headers); + + $message->shouldReceive('getReturnPath') + ->zeroOrMoreTimes() + ->andReturn( + '"attacker\" -oQ/tmp/ -X/var/www/cache/phpcode.php "@email.com' + ); + $message->shouldReceive('getSender') + ->zeroOrMoreTimes() + ->andReturn(null); + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(null); + $invoker->shouldReceive('mail') + ->once() + ->with(\Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any(), null); + + $transport->send($message); + } + + public function testTransportUsesHeadersFromMessage() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $headers = $this->_createHeaders(); + $message = $this->_createMessageWithRecipient($headers); + + $message->shouldReceive('toString') + ->zeroOrMoreTimes() + ->andReturn( + "Subject: Stuff\r\n". + "\r\n". + 'This body' + ); + $invoker->shouldReceive('mail') + ->once() + ->with(\Mockery::any(), \Mockery::any(), \Mockery::any(), 'Subject: Stuff'.PHP_EOL, \Mockery::any()); + + $transport->send($message); + } + + public function testTransportReturnsCountOfAllRecipientsIfInvokerReturnsTrue() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $headers = $this->_createHeaders(); + $message = $this->_createMessage($headers); + + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('foo@bar' => null, 'zip@button' => null)); + $message->shouldReceive('getCc') + ->zeroOrMoreTimes() + ->andReturn(array('test@test' => null)); + $invoker->shouldReceive('mail') + ->once() + ->with(\Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn(true); + + $this->assertEquals(3, $transport->send($message)); + } + + public function testTransportReturnsZeroIfInvokerReturnsFalse() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $headers = $this->_createHeaders(); + $message = $this->_createMessage($headers); + + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('foo@bar' => null, 'zip@button' => null)); + $message->shouldReceive('getCc') + ->zeroOrMoreTimes() + ->andReturn(array('test@test' => null)); + $invoker->shouldReceive('mail') + ->once() + ->with(\Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn(false); + + $this->assertEquals(0, $transport->send($message)); + } + + public function testToHeaderIsRemovedFromHeaderSetDuringSending() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $to = $this->_createHeader(); + $headers = $this->_createHeaders(array( + 'To' => $to, + )); + $message = $this->_createMessageWithRecipient($headers); + + $headers->shouldReceive('remove') + ->once() + ->with('To'); + $headers->shouldReceive('remove') + ->zeroOrMoreTimes(); + $invoker->shouldReceive('mail') + ->once() + ->with(\Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any()); + + $transport->send($message); + } + + public function testSubjectHeaderIsRemovedFromHeaderSetDuringSending() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $subject = $this->_createHeader(); + $headers = $this->_createHeaders(array( + 'Subject' => $subject, + )); + $message = $this->_createMessageWithRecipient($headers); + + $headers->shouldReceive('remove') + ->once() + ->with('Subject'); + $headers->shouldReceive('remove') + ->zeroOrMoreTimes(); + $invoker->shouldReceive('mail') + ->once() + ->with(\Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any()); + + $transport->send($message); + } + + public function testToHeaderIsPutBackAfterSending() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $to = $this->_createHeader(); + $headers = $this->_createHeaders(array( + 'To' => $to, + )); + $message = $this->_createMessageWithRecipient($headers); + + $headers->shouldReceive('set') + ->once() + ->with($to); + $headers->shouldReceive('set') + ->zeroOrMoreTimes(); + $invoker->shouldReceive('mail') + ->once() + ->with(\Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any()); + + $transport->send($message); + } + + public function testSubjectHeaderIsPutBackAfterSending() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $subject = $this->_createHeader(); + $headers = $this->_createHeaders(array( + 'Subject' => $subject, + )); + $message = $this->_createMessageWithRecipient($headers); + + $headers->shouldReceive('set') + ->once() + ->with($subject); + $headers->shouldReceive('set') + ->zeroOrMoreTimes(); + $invoker->shouldReceive('mail') + ->once() + ->with(\Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any()); + + $transport->send($message); + } + + public function testMessageHeadersOnlyHavePHPEolsDuringSending() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $subject = $this->_createHeader(); + $subject->shouldReceive('getFieldBody')->andReturn("Foo\r\nBar"); + + $headers = $this->_createHeaders(array( + 'Subject' => $subject, + )); + $message = $this->_createMessageWithRecipient($headers); + $message->shouldReceive('toString') + ->zeroOrMoreTimes() + ->andReturn( + "From: Foo\r\n\r\n". + "\r\n". + "This\r\n". + 'body' + ); + + if ("\r\n" != PHP_EOL) { + $expectedHeaders = "From: Foo\n\n"; + $expectedSubject = "Foo\nBar"; + $expectedBody = "This\nbody"; + } else { + $expectedHeaders = "From: Foo\r\n\r\n"; + $expectedSubject = "Foo\r\nBar"; + $expectedBody = "This\r\nbody"; + } + + $invoker->shouldReceive('mail') + ->once() + ->with(\Mockery::any(), $expectedSubject, $expectedBody, $expectedHeaders, \Mockery::any()); + + $transport->send($message); + } + + /** + * @expectedException Swift_TransportException + * @expectedExceptionMessage Cannot send message without a recipient + */ + public function testExceptionWhenNoRecipients() + { + $invoker = $this->_createInvoker(); + $invoker->shouldReceive('mail'); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $headers = $this->_createHeaders(); + $message = $this->_createMessage($headers); + + $transport->send($message); + } + + public function noExceptionWhenRecipientsExistProvider() + { + return array( + array('To'), + array('Cc'), + array('Bcc'), + ); + } + + /** + * @dataProvider noExceptionWhenRecipientsExistProvider + * + * @param string $header + */ + public function testNoExceptionWhenRecipientsExist($header) + { + $invoker = $this->_createInvoker(); + $invoker->shouldReceive('mail'); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $headers = $this->_createHeaders(); + $message = $this->_createMessage($headers); + $message->shouldReceive(sprintf('get%s', $header))->andReturn(array('foo@bar' => 'Foo')); + + $transport->send($message); + } + + // -- Creation Methods + + private function _createTransport($invoker, $dispatcher) + { + return new Swift_Transport_MailTransport($invoker, $dispatcher); + } + + private function _createEventDispatcher() + { + return $this->getMockery('Swift_Events_EventDispatcher')->shouldIgnoreMissing(); + } + + private function _createInvoker() + { + return $this->getMockery('Swift_Transport_MailInvoker'); + } + + private function _createMessage($headers) + { + $message = $this->getMockery('Swift_Mime_Message')->shouldIgnoreMissing(); + $message->shouldReceive('getHeaders') + ->zeroOrMoreTimes() + ->andReturn($headers); + + return $message; + } + + private function _createMessageWithRecipient($headers, $recipient = array('foo@bar' => 'Foo')) + { + $message = $this->_createMessage($headers); + $message->shouldReceive('getTo')->andReturn($recipient); + + return $message; + } + + private function _createHeaders($headers = array()) + { + $set = $this->getMockery('Swift_Mime_HeaderSet')->shouldIgnoreMissing(); + + if (count($headers) > 0) { + foreach ($headers as $name => $header) { + $set->shouldReceive('get') + ->zeroOrMoreTimes() + ->with($name) + ->andReturn($header); + $set->shouldReceive('has') + ->zeroOrMoreTimes() + ->with($name) + ->andReturn(true); + } + } + + $header = $this->_createHeader(); + $set->shouldReceive('get') + ->zeroOrMoreTimes() + ->andReturn($header); + $set->shouldReceive('has') + ->zeroOrMoreTimes() + ->andReturn(true); + + return $set; + } + + private function _createHeader() + { + return $this->getMockery('Swift_Mime_Header')->shouldIgnoreMissing(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/SendmailTransportTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/SendmailTransportTest.php new file mode 100644 index 0000000000..9040f9ea4d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/SendmailTransportTest.php @@ -0,0 +1,151 @@ +_createEventDispatcher(); + } + $transport = new Swift_Transport_SendmailTransport($buf, $dispatcher); + $transport->setCommand($command); + + return $transport; + } + + protected function _getSendmail($buf, $dispatcher = null) + { + if (!$dispatcher) { + $dispatcher = $this->_createEventDispatcher(); + } + $sendmail = new Swift_Transport_SendmailTransport($buf, $dispatcher); + + return $sendmail; + } + + public function testCommandCanBeSetAndFetched() + { + $buf = $this->_getBuffer(); + $sendmail = $this->_getSendmail($buf); + + $sendmail->setCommand('/usr/sbin/sendmail -bs'); + $this->assertEquals('/usr/sbin/sendmail -bs', $sendmail->getCommand()); + $sendmail->setCommand('/usr/sbin/sendmail -oi -t'); + $this->assertEquals('/usr/sbin/sendmail -oi -t', $sendmail->getCommand()); + } + + public function testSendingMessageIn_t_ModeUsesSimplePipe() + { + $buf = $this->_getBuffer(); + $sendmail = $this->_getSendmail($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('foo@bar' => 'Foobar', 'zip@button' => 'Zippy')); + $message->shouldReceive('toByteStream') + ->once() + ->with($buf); + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('terminate') + ->once(); + $buf->shouldReceive('setWriteTranslations') + ->once() + ->with(array("\r\n" => "\n", "\n." => "\n..")); + $buf->shouldReceive('setWriteTranslations') + ->once() + ->with(array()); + + $sendmail->setCommand('/usr/sbin/sendmail -t'); + $this->assertEquals(2, $sendmail->send($message)); + } + + public function testSendingIn_t_ModeWith_i_FlagDoesntEscapeDot() + { + $buf = $this->_getBuffer(); + $sendmail = $this->_getSendmail($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('foo@bar' => 'Foobar', 'zip@button' => 'Zippy')); + $message->shouldReceive('toByteStream') + ->once() + ->with($buf); + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('terminate') + ->once(); + $buf->shouldReceive('setWriteTranslations') + ->once() + ->with(array("\r\n" => "\n")); + $buf->shouldReceive('setWriteTranslations') + ->once() + ->with(array()); + + $sendmail->setCommand('/usr/sbin/sendmail -i -t'); + $this->assertEquals(2, $sendmail->send($message)); + } + + public function testSendingInTModeWith_oi_FlagDoesntEscapeDot() + { + $buf = $this->_getBuffer(); + $sendmail = $this->_getSendmail($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('foo@bar' => 'Foobar', 'zip@button' => 'Zippy')); + $message->shouldReceive('toByteStream') + ->once() + ->with($buf); + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('terminate') + ->once(); + $buf->shouldReceive('setWriteTranslations') + ->once() + ->with(array("\r\n" => "\n")); + $buf->shouldReceive('setWriteTranslations') + ->once() + ->with(array()); + + $sendmail->setCommand('/usr/sbin/sendmail -oi -t'); + $this->assertEquals(2, $sendmail->send($message)); + } + + public function testSendingMessageRegeneratesId() + { + $buf = $this->_getBuffer(); + $sendmail = $this->_getSendmail($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('foo@bar' => 'Foobar', 'zip@button' => 'Zippy')); + $message->shouldReceive('generateId'); + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('terminate') + ->once(); + $buf->shouldReceive('setWriteTranslations') + ->once() + ->with(array("\r\n" => "\n", "\n." => "\n..")); + $buf->shouldReceive('setWriteTranslations') + ->once() + ->with(array()); + + $sendmail->setCommand('/usr/sbin/sendmail -t'); + $this->assertEquals(2, $sendmail->send($message)); + } + + public function testFluidInterface() + { + $buf = $this->_getBuffer(); + $sendmail = $this->_getTransport($buf); + + $ref = $sendmail->setCommand('/foo'); + $this->assertEquals($ref, $sendmail); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/StreamBufferTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/StreamBufferTest.php new file mode 100644 index 0000000000..6108a95431 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/StreamBufferTest.php @@ -0,0 +1,45 @@ +_createFactory(); + $factory->expects($this->once()) + ->method('createFilter') + ->with('a', 'b') + ->will($this->returnCallback(array($this, '_createFilter'))); + + $buffer = $this->_createBuffer($factory); + $buffer->setWriteTranslations(array('a' => 'b')); + } + + public function testOverridingTranslationsOnlyAddsNeededFilters() + { + $factory = $this->_createFactory(); + $factory->expects($this->exactly(2)) + ->method('createFilter') + ->will($this->returnCallback(array($this, '_createFilter'))); + + $buffer = $this->_createBuffer($factory); + $buffer->setWriteTranslations(array('a' => 'b')); + $buffer->setWriteTranslations(array('x' => 'y', 'a' => 'b')); + } + + // -- Creation methods + + private function _createBuffer($replacementFactory) + { + return new Swift_Transport_StreamBuffer($replacementFactory); + } + + private function _createFactory() + { + return $this->getMockBuilder('Swift_ReplacementFilterFactory')->getMock(); + } + + public function _createFilter() + { + return $this->getMockBuilder('Swift_StreamFilter')->getMock(); + } +} diff --git a/vendor/symfony/console/.gitignore b/vendor/symfony/console/.gitignore new file mode 100644 index 0000000000..c49a5d8df5 --- /dev/null +++ b/vendor/symfony/console/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/vendor/symfony/console/Application.php b/vendor/symfony/console/Application.php new file mode 100644 index 0000000000..603559f0f6 --- /dev/null +++ b/vendor/symfony/console/Application.php @@ -0,0 +1,1144 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console; + +use Symfony\Component\Console\Descriptor\TextDescriptor; +use Symfony\Component\Console\Descriptor\XmlDescriptor; +use Symfony\Component\Console\Exception\ExceptionInterface; +use Symfony\Component\Console\Helper\DebugFormatterHelper; +use Symfony\Component\Console\Helper\ProcessHelper; +use Symfony\Component\Console\Helper\QuestionHelper; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputAwareInterface; +use Symfony\Component\Console\Output\BufferedOutput; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\ConsoleOutput; +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Command\HelpCommand; +use Symfony\Component\Console\Command\ListCommand; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Helper\FormatterHelper; +use Symfony\Component\Console\Helper\DialogHelper; +use Symfony\Component\Console\Helper\ProgressHelper; +use Symfony\Component\Console\Helper\TableHelper; +use Symfony\Component\Console\Event\ConsoleCommandEvent; +use Symfony\Component\Console\Event\ConsoleExceptionEvent; +use Symfony\Component\Console\Event\ConsoleTerminateEvent; +use Symfony\Component\Console\Exception\CommandNotFoundException; +use Symfony\Component\Console\Exception\LogicException; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; + +/** + * An Application is the container for a collection of commands. + * + * It is the main entry point of a Console application. + * + * This class is optimized for a standard CLI environment. + * + * Usage: + * + * $app = new Application('myapp', '1.0 (stable)'); + * $app->add(new SimpleCommand()); + * $app->run(); + * + * @author Fabien Potencier + */ +class Application +{ + private $commands = array(); + private $wantHelps = false; + private $runningCommand; + private $name; + private $version; + private $catchExceptions = true; + private $autoExit = true; + private $definition; + private $helperSet; + private $dispatcher; + private $terminalDimensions; + private $defaultCommand; + + /** + * Constructor. + * + * @param string $name The name of the application + * @param string $version The version of the application + */ + public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN') + { + $this->name = $name; + $this->version = $version; + $this->defaultCommand = 'list'; + $this->helperSet = $this->getDefaultHelperSet(); + $this->definition = $this->getDefaultInputDefinition(); + + foreach ($this->getDefaultCommands() as $command) { + $this->add($command); + } + } + + public function setDispatcher(EventDispatcherInterface $dispatcher) + { + $this->dispatcher = $dispatcher; + } + + /** + * Runs the current application. + * + * @param InputInterface $input An Input instance + * @param OutputInterface $output An Output instance + * + * @return int 0 if everything went fine, or an error code + * + * @throws \Exception When doRun returns Exception + */ + public function run(InputInterface $input = null, OutputInterface $output = null) + { + if (null === $input) { + $input = new ArgvInput(); + } + + if (null === $output) { + $output = new ConsoleOutput(); + } + + $this->configureIO($input, $output); + + try { + $exitCode = $this->doRun($input, $output); + } catch (\Exception $e) { + if (!$this->catchExceptions) { + throw $e; + } + + if ($output instanceof ConsoleOutputInterface) { + $this->renderException($e, $output->getErrorOutput()); + } else { + $this->renderException($e, $output); + } + + $exitCode = $e->getCode(); + if (is_numeric($exitCode)) { + $exitCode = (int) $exitCode; + if (0 === $exitCode) { + $exitCode = 1; + } + } else { + $exitCode = 1; + } + } + + if ($this->autoExit) { + if ($exitCode > 255) { + $exitCode = 255; + } + + exit($exitCode); + } + + return $exitCode; + } + + /** + * Runs the current application. + * + * @param InputInterface $input An Input instance + * @param OutputInterface $output An Output instance + * + * @return int 0 if everything went fine, or an error code + */ + public function doRun(InputInterface $input, OutputInterface $output) + { + if (true === $input->hasParameterOption(array('--version', '-V'))) { + $output->writeln($this->getLongVersion()); + + return 0; + } + + $name = $this->getCommandName($input); + if (true === $input->hasParameterOption(array('--help', '-h'))) { + if (!$name) { + $name = 'help'; + $input = new ArrayInput(array('command' => 'help')); + } else { + $this->wantHelps = true; + } + } + + if (!$name) { + $name = $this->defaultCommand; + $input = new ArrayInput(array('command' => $this->defaultCommand)); + } + + // the command name MUST be the first element of the input + $command = $this->find($name); + + $this->runningCommand = $command; + $exitCode = $this->doRunCommand($command, $input, $output); + $this->runningCommand = null; + + return $exitCode; + } + + /** + * Set a helper set to be used with the command. + * + * @param HelperSet $helperSet The helper set + */ + public function setHelperSet(HelperSet $helperSet) + { + $this->helperSet = $helperSet; + } + + /** + * Get the helper set associated with the command. + * + * @return HelperSet The HelperSet instance associated with this command + */ + public function getHelperSet() + { + return $this->helperSet; + } + + /** + * Set an input definition set to be used with this application. + * + * @param InputDefinition $definition The input definition + */ + public function setDefinition(InputDefinition $definition) + { + $this->definition = $definition; + } + + /** + * Gets the InputDefinition related to this Application. + * + * @return InputDefinition The InputDefinition instance + */ + public function getDefinition() + { + return $this->definition; + } + + /** + * Gets the help message. + * + * @return string A help message. + */ + public function getHelp() + { + return $this->getLongVersion(); + } + + /** + * Sets whether to catch exceptions or not during commands execution. + * + * @param bool $boolean Whether to catch exceptions or not during commands execution + */ + public function setCatchExceptions($boolean) + { + $this->catchExceptions = (bool) $boolean; + } + + /** + * Sets whether to automatically exit after a command execution or not. + * + * @param bool $boolean Whether to automatically exit after a command execution or not + */ + public function setAutoExit($boolean) + { + $this->autoExit = (bool) $boolean; + } + + /** + * Gets the name of the application. + * + * @return string The application name + */ + public function getName() + { + return $this->name; + } + + /** + * Sets the application name. + * + * @param string $name The application name + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * Gets the application version. + * + * @return string The application version + */ + public function getVersion() + { + return $this->version; + } + + /** + * Sets the application version. + * + * @param string $version The application version + */ + public function setVersion($version) + { + $this->version = $version; + } + + /** + * Returns the long version of the application. + * + * @return string The long application version + */ + public function getLongVersion() + { + if ('UNKNOWN' !== $this->getName()) { + if ('UNKNOWN' !== $this->getVersion()) { + return sprintf('%s version %s', $this->getName(), $this->getVersion()); + } + + return sprintf('%s', $this->getName()); + } + + return 'Console Tool'; + } + + /** + * Registers a new command. + * + * @param string $name The command name + * + * @return Command The newly created command + */ + public function register($name) + { + return $this->add(new Command($name)); + } + + /** + * Adds an array of command objects. + * + * @param Command[] $commands An array of commands + */ + public function addCommands(array $commands) + { + foreach ($commands as $command) { + $this->add($command); + } + } + + /** + * Adds a command object. + * + * If a command with the same name already exists, it will be overridden. + * + * @param Command $command A Command object + * + * @return Command The registered command + */ + public function add(Command $command) + { + $command->setApplication($this); + + if (!$command->isEnabled()) { + $command->setApplication(null); + + return; + } + + if (null === $command->getDefinition()) { + throw new LogicException(sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', get_class($command))); + } + + $this->commands[$command->getName()] = $command; + + foreach ($command->getAliases() as $alias) { + $this->commands[$alias] = $command; + } + + return $command; + } + + /** + * Returns a registered command by name or alias. + * + * @param string $name The command name or alias + * + * @return Command A Command object + * + * @throws CommandNotFoundException When command name given does not exist + */ + public function get($name) + { + if (!isset($this->commands[$name])) { + throw new CommandNotFoundException(sprintf('The command "%s" does not exist.', $name)); + } + + $command = $this->commands[$name]; + + if ($this->wantHelps) { + $this->wantHelps = false; + + $helpCommand = $this->get('help'); + $helpCommand->setCommand($command); + + return $helpCommand; + } + + return $command; + } + + /** + * Returns true if the command exists, false otherwise. + * + * @param string $name The command name or alias + * + * @return bool true if the command exists, false otherwise + */ + public function has($name) + { + return isset($this->commands[$name]); + } + + /** + * Returns an array of all unique namespaces used by currently registered commands. + * + * It does not returns the global namespace which always exists. + * + * @return array An array of namespaces + */ + public function getNamespaces() + { + $namespaces = array(); + foreach ($this->all() as $command) { + $namespaces = array_merge($namespaces, $this->extractAllNamespaces($command->getName())); + + foreach ($command->getAliases() as $alias) { + $namespaces = array_merge($namespaces, $this->extractAllNamespaces($alias)); + } + } + + return array_values(array_unique(array_filter($namespaces))); + } + + /** + * Finds a registered namespace by a name or an abbreviation. + * + * @param string $namespace A namespace or abbreviation to search for + * + * @return string A registered namespace + * + * @throws CommandNotFoundException When namespace is incorrect or ambiguous + */ + public function findNamespace($namespace) + { + $allNamespaces = $this->getNamespaces(); + $expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $namespace); + $namespaces = preg_grep('{^'.$expr.'}', $allNamespaces); + + if (empty($namespaces)) { + $message = sprintf('There are no commands defined in the "%s" namespace.', $namespace); + + if ($alternatives = $this->findAlternatives($namespace, $allNamespaces)) { + if (1 == count($alternatives)) { + $message .= "\n\nDid you mean this?\n "; + } else { + $message .= "\n\nDid you mean one of these?\n "; + } + + $message .= implode("\n ", $alternatives); + } + + throw new CommandNotFoundException($message, $alternatives); + } + + $exact = in_array($namespace, $namespaces, true); + if (count($namespaces) > 1 && !$exact) { + throw new CommandNotFoundException(sprintf('The namespace "%s" is ambiguous (%s).', $namespace, $this->getAbbreviationSuggestions(array_values($namespaces))), array_values($namespaces)); + } + + return $exact ? $namespace : reset($namespaces); + } + + /** + * Finds a command by name or alias. + * + * Contrary to get, this command tries to find the best + * match if you give it an abbreviation of a name or alias. + * + * @param string $name A command name or a command alias + * + * @return Command A Command instance + * + * @throws CommandNotFoundException When command name is incorrect or ambiguous + */ + public function find($name) + { + $allCommands = array_keys($this->commands); + $expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $name); + $commands = preg_grep('{^'.$expr.'}', $allCommands); + + if (empty($commands) || count(preg_grep('{^'.$expr.'$}', $commands)) < 1) { + if (false !== $pos = strrpos($name, ':')) { + // check if a namespace exists and contains commands + $this->findNamespace(substr($name, 0, $pos)); + } + + $message = sprintf('Command "%s" is not defined.', $name); + + if ($alternatives = $this->findAlternatives($name, $allCommands)) { + if (1 == count($alternatives)) { + $message .= "\n\nDid you mean this?\n "; + } else { + $message .= "\n\nDid you mean one of these?\n "; + } + $message .= implode("\n ", $alternatives); + } + + throw new CommandNotFoundException($message, $alternatives); + } + + // filter out aliases for commands which are already on the list + if (count($commands) > 1) { + $commandList = $this->commands; + $commands = array_filter($commands, function ($nameOrAlias) use ($commandList, $commands) { + $commandName = $commandList[$nameOrAlias]->getName(); + + return $commandName === $nameOrAlias || !in_array($commandName, $commands); + }); + } + + $exact = in_array($name, $commands, true); + if (count($commands) > 1 && !$exact) { + $suggestions = $this->getAbbreviationSuggestions(array_values($commands)); + + throw new CommandNotFoundException(sprintf('Command "%s" is ambiguous (%s).', $name, $suggestions), array_values($commands)); + } + + return $this->get($exact ? $name : reset($commands)); + } + + /** + * Gets the commands (registered in the given namespace if provided). + * + * The array keys are the full names and the values the command instances. + * + * @param string $namespace A namespace name + * + * @return Command[] An array of Command instances + */ + public function all($namespace = null) + { + if (null === $namespace) { + return $this->commands; + } + + $commands = array(); + foreach ($this->commands as $name => $command) { + if ($namespace === $this->extractNamespace($name, substr_count($namespace, ':') + 1)) { + $commands[$name] = $command; + } + } + + return $commands; + } + + /** + * Returns an array of possible abbreviations given a set of names. + * + * @param array $names An array of names + * + * @return array An array of abbreviations + */ + public static function getAbbreviations($names) + { + $abbrevs = array(); + foreach ($names as $name) { + for ($len = strlen($name); $len > 0; --$len) { + $abbrev = substr($name, 0, $len); + $abbrevs[$abbrev][] = $name; + } + } + + return $abbrevs; + } + + /** + * Returns a text representation of the Application. + * + * @param string $namespace An optional namespace name + * @param bool $raw Whether to return raw command list + * + * @return string A string representing the Application + * + * @deprecated since version 2.3, to be removed in 3.0. + */ + public function asText($namespace = null, $raw = false) + { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.3 and will be removed in 3.0.', E_USER_DEPRECATED); + + $descriptor = new TextDescriptor(); + $output = new BufferedOutput(BufferedOutput::VERBOSITY_NORMAL, !$raw); + $descriptor->describe($output, $this, array('namespace' => $namespace, 'raw_output' => true)); + + return $output->fetch(); + } + + /** + * Returns an XML representation of the Application. + * + * @param string $namespace An optional namespace name + * @param bool $asDom Whether to return a DOM or an XML string + * + * @return string|\DOMDocument An XML string representing the Application + * + * @deprecated since version 2.3, to be removed in 3.0. + */ + public function asXml($namespace = null, $asDom = false) + { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.3 and will be removed in 3.0.', E_USER_DEPRECATED); + + $descriptor = new XmlDescriptor(); + + if ($asDom) { + return $descriptor->getApplicationDocument($this, $namespace); + } + + $output = new BufferedOutput(); + $descriptor->describe($output, $this, array('namespace' => $namespace)); + + return $output->fetch(); + } + + /** + * Renders a caught exception. + * + * @param \Exception $e An exception instance + * @param OutputInterface $output An OutputInterface instance + */ + public function renderException($e, $output) + { + $output->writeln('', OutputInterface::VERBOSITY_QUIET); + + do { + $title = sprintf(' [%s] ', get_class($e)); + + $len = $this->stringWidth($title); + + $width = $this->getTerminalWidth() ? $this->getTerminalWidth() - 1 : PHP_INT_MAX; + // HHVM only accepts 32 bits integer in str_split, even when PHP_INT_MAX is a 64 bit integer: https://github.com/facebook/hhvm/issues/1327 + if (defined('HHVM_VERSION') && $width > 1 << 31) { + $width = 1 << 31; + } + $formatter = $output->getFormatter(); + $lines = array(); + foreach (preg_split('/\r?\n/', $e->getMessage()) as $line) { + foreach ($this->splitStringByWidth($line, $width - 4) as $line) { + // pre-format lines to get the right string length + $lineLength = $this->stringWidth(preg_replace('/\[[^m]*m/', '', $formatter->format($line))) + 4; + $lines[] = array($line, $lineLength); + + $len = max($lineLength, $len); + } + } + + $messages = array(); + $messages[] = $emptyLine = $formatter->format(sprintf('%s', str_repeat(' ', $len))); + $messages[] = $formatter->format(sprintf('%s%s', $title, str_repeat(' ', max(0, $len - $this->stringWidth($title))))); + foreach ($lines as $line) { + $messages[] = $formatter->format(sprintf(' %s %s', $line[0], str_repeat(' ', $len - $line[1]))); + } + $messages[] = $emptyLine; + $messages[] = ''; + + $output->writeln($messages, OutputInterface::OUTPUT_RAW | OutputInterface::VERBOSITY_QUIET); + + if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { + $output->writeln('Exception trace:', OutputInterface::VERBOSITY_QUIET); + + // exception related properties + $trace = $e->getTrace(); + array_unshift($trace, array( + 'function' => '', + 'file' => $e->getFile() !== null ? $e->getFile() : 'n/a', + 'line' => $e->getLine() !== null ? $e->getLine() : 'n/a', + 'args' => array(), + )); + + for ($i = 0, $count = count($trace); $i < $count; ++$i) { + $class = isset($trace[$i]['class']) ? $trace[$i]['class'] : ''; + $type = isset($trace[$i]['type']) ? $trace[$i]['type'] : ''; + $function = $trace[$i]['function']; + $file = isset($trace[$i]['file']) ? $trace[$i]['file'] : 'n/a'; + $line = isset($trace[$i]['line']) ? $trace[$i]['line'] : 'n/a'; + + $output->writeln(sprintf(' %s%s%s() at %s:%s', $class, $type, $function, $file, $line), OutputInterface::VERBOSITY_QUIET); + } + + $output->writeln('', OutputInterface::VERBOSITY_QUIET); + } + } while ($e = $e->getPrevious()); + + if (null !== $this->runningCommand) { + $output->writeln(sprintf('%s', sprintf($this->runningCommand->getSynopsis(), $this->getName())), OutputInterface::VERBOSITY_QUIET); + $output->writeln('', OutputInterface::VERBOSITY_QUIET); + } + } + + /** + * Tries to figure out the terminal width in which this application runs. + * + * @return int|null + */ + protected function getTerminalWidth() + { + $dimensions = $this->getTerminalDimensions(); + + return $dimensions[0]; + } + + /** + * Tries to figure out the terminal height in which this application runs. + * + * @return int|null + */ + protected function getTerminalHeight() + { + $dimensions = $this->getTerminalDimensions(); + + return $dimensions[1]; + } + + /** + * Tries to figure out the terminal dimensions based on the current environment. + * + * @return array Array containing width and height + */ + public function getTerminalDimensions() + { + if ($this->terminalDimensions) { + return $this->terminalDimensions; + } + + if ('\\' === DIRECTORY_SEPARATOR) { + // extract [w, H] from "wxh (WxH)" + if (preg_match('/^(\d+)x\d+ \(\d+x(\d+)\)$/', trim(getenv('ANSICON')), $matches)) { + return array((int) $matches[1], (int) $matches[2]); + } + // extract [w, h] from "wxh" + if (preg_match('/^(\d+)x(\d+)$/', $this->getConsoleMode(), $matches)) { + return array((int) $matches[1], (int) $matches[2]); + } + } + + if ($sttyString = $this->getSttyColumns()) { + // extract [w, h] from "rows h; columns w;" + if (preg_match('/rows.(\d+);.columns.(\d+);/i', $sttyString, $matches)) { + return array((int) $matches[2], (int) $matches[1]); + } + // extract [w, h] from "; h rows; w columns" + if (preg_match('/;.(\d+).rows;.(\d+).columns/i', $sttyString, $matches)) { + return array((int) $matches[2], (int) $matches[1]); + } + } + + return array(null, null); + } + + /** + * Sets terminal dimensions. + * + * Can be useful to force terminal dimensions for functional tests. + * + * @param int $width The width + * @param int $height The height + * + * @return Application The current application + */ + public function setTerminalDimensions($width, $height) + { + $this->terminalDimensions = array($width, $height); + + return $this; + } + + /** + * Configures the input and output instances based on the user arguments and options. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + */ + protected function configureIO(InputInterface $input, OutputInterface $output) + { + if (true === $input->hasParameterOption(array('--ansi'))) { + $output->setDecorated(true); + } elseif (true === $input->hasParameterOption(array('--no-ansi'))) { + $output->setDecorated(false); + } + + if (true === $input->hasParameterOption(array('--no-interaction', '-n'))) { + $input->setInteractive(false); + } elseif (function_exists('posix_isatty') && $this->getHelperSet()->has('question')) { + $inputStream = $this->getHelperSet()->get('question')->getInputStream(); + if (!@posix_isatty($inputStream) && false === getenv('SHELL_INTERACTIVE')) { + $input->setInteractive(false); + } + } + + if (true === $input->hasParameterOption(array('--quiet', '-q'))) { + $output->setVerbosity(OutputInterface::VERBOSITY_QUIET); + } else { + if ($input->hasParameterOption('-vvv') || $input->hasParameterOption('--verbose=3') || $input->getParameterOption('--verbose') === 3) { + $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG); + } elseif ($input->hasParameterOption('-vv') || $input->hasParameterOption('--verbose=2') || $input->getParameterOption('--verbose') === 2) { + $output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE); + } elseif ($input->hasParameterOption('-v') || $input->hasParameterOption('--verbose=1') || $input->hasParameterOption('--verbose') || $input->getParameterOption('--verbose')) { + $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE); + } + } + } + + /** + * Runs the current command. + * + * If an event dispatcher has been attached to the application, + * events are also dispatched during the life-cycle of the command. + * + * @param Command $command A Command instance + * @param InputInterface $input An Input instance + * @param OutputInterface $output An Output instance + * + * @return int 0 if everything went fine, or an error code + * + * @throws \Exception when the command being run threw an exception + */ + protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output) + { + foreach ($command->getHelperSet() as $helper) { + if ($helper instanceof InputAwareInterface) { + $helper->setInput($input); + } + } + + if (null === $this->dispatcher) { + return $command->run($input, $output); + } + + // bind before the console.command event, so the listeners have access to input options/arguments + try { + $command->mergeApplicationDefinition(); + $input->bind($command->getDefinition()); + } catch (ExceptionInterface $e) { + // ignore invalid options/arguments for now, to allow the event listeners to customize the InputDefinition + } + + $event = new ConsoleCommandEvent($command, $input, $output); + $this->dispatcher->dispatch(ConsoleEvents::COMMAND, $event); + + if ($event->commandShouldRun()) { + try { + $exitCode = $command->run($input, $output); + } catch (\Exception $e) { + $event = new ConsoleExceptionEvent($command, $input, $output, $e, $e->getCode()); + $this->dispatcher->dispatch(ConsoleEvents::EXCEPTION, $event); + + $e = $event->getException(); + + $event = new ConsoleTerminateEvent($command, $input, $output, $e->getCode()); + $this->dispatcher->dispatch(ConsoleEvents::TERMINATE, $event); + + throw $e; + } + } else { + $exitCode = ConsoleCommandEvent::RETURN_CODE_DISABLED; + } + + $event = new ConsoleTerminateEvent($command, $input, $output, $exitCode); + $this->dispatcher->dispatch(ConsoleEvents::TERMINATE, $event); + + return $event->getExitCode(); + } + + /** + * Gets the name of the command based on input. + * + * @param InputInterface $input The input interface + * + * @return string The command name + */ + protected function getCommandName(InputInterface $input) + { + return $input->getFirstArgument(); + } + + /** + * Gets the default input definition. + * + * @return InputDefinition An InputDefinition instance + */ + protected function getDefaultInputDefinition() + { + return new InputDefinition(array( + new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'), + + new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message'), + new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message'), + new InputOption('--verbose', '-v|vv|vvv', InputOption::VALUE_NONE, 'Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug'), + new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this application version'), + new InputOption('--ansi', '', InputOption::VALUE_NONE, 'Force ANSI output'), + new InputOption('--no-ansi', '', InputOption::VALUE_NONE, 'Disable ANSI output'), + new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question'), + )); + } + + /** + * Gets the default commands that should always be available. + * + * @return Command[] An array of default Command instances + */ + protected function getDefaultCommands() + { + return array(new HelpCommand(), new ListCommand()); + } + + /** + * Gets the default helper set with the helpers that should always be available. + * + * @return HelperSet A HelperSet instance + */ + protected function getDefaultHelperSet() + { + return new HelperSet(array( + new FormatterHelper(), + new DialogHelper(false), + new ProgressHelper(false), + new TableHelper(false), + new DebugFormatterHelper(), + new ProcessHelper(), + new QuestionHelper(), + )); + } + + /** + * Runs and parses stty -a if it's available, suppressing any error output. + * + * @return string + */ + private function getSttyColumns() + { + if (!function_exists('proc_open')) { + return; + } + + $descriptorspec = array(1 => array('pipe', 'w'), 2 => array('pipe', 'w')); + $process = proc_open('stty -a | grep columns', $descriptorspec, $pipes, null, null, array('suppress_errors' => true)); + if (is_resource($process)) { + $info = stream_get_contents($pipes[1]); + fclose($pipes[1]); + fclose($pipes[2]); + proc_close($process); + + return $info; + } + } + + /** + * Runs and parses mode CON if it's available, suppressing any error output. + * + * @return string x or null if it could not be parsed + */ + private function getConsoleMode() + { + if (!function_exists('proc_open')) { + return; + } + + $descriptorspec = array(1 => array('pipe', 'w'), 2 => array('pipe', 'w')); + $process = proc_open('mode CON', $descriptorspec, $pipes, null, null, array('suppress_errors' => true)); + if (is_resource($process)) { + $info = stream_get_contents($pipes[1]); + fclose($pipes[1]); + fclose($pipes[2]); + proc_close($process); + + if (preg_match('/--------+\r?\n.+?(\d+)\r?\n.+?(\d+)\r?\n/', $info, $matches)) { + return $matches[2].'x'.$matches[1]; + } + } + } + + /** + * Returns abbreviated suggestions in string format. + * + * @param array $abbrevs Abbreviated suggestions to convert + * + * @return string A formatted string of abbreviated suggestions + */ + private function getAbbreviationSuggestions($abbrevs) + { + return sprintf('%s, %s%s', $abbrevs[0], $abbrevs[1], count($abbrevs) > 2 ? sprintf(' and %d more', count($abbrevs) - 2) : ''); + } + + /** + * Returns the namespace part of the command name. + * + * This method is not part of public API and should not be used directly. + * + * @param string $name The full name of the command + * @param string $limit The maximum number of parts of the namespace + * + * @return string The namespace of the command + */ + public function extractNamespace($name, $limit = null) + { + $parts = explode(':', $name); + array_pop($parts); + + return implode(':', null === $limit ? $parts : array_slice($parts, 0, $limit)); + } + + /** + * Finds alternative of $name among $collection, + * if nothing is found in $collection, try in $abbrevs. + * + * @param string $name The string + * @param array|\Traversable $collection The collection + * + * @return array A sorted array of similar string + */ + private function findAlternatives($name, $collection) + { + $threshold = 1e3; + $alternatives = array(); + + $collectionParts = array(); + foreach ($collection as $item) { + $collectionParts[$item] = explode(':', $item); + } + + foreach (explode(':', $name) as $i => $subname) { + foreach ($collectionParts as $collectionName => $parts) { + $exists = isset($alternatives[$collectionName]); + if (!isset($parts[$i]) && $exists) { + $alternatives[$collectionName] += $threshold; + continue; + } elseif (!isset($parts[$i])) { + continue; + } + + $lev = levenshtein($subname, $parts[$i]); + if ($lev <= strlen($subname) / 3 || '' !== $subname && false !== strpos($parts[$i], $subname)) { + $alternatives[$collectionName] = $exists ? $alternatives[$collectionName] + $lev : $lev; + } elseif ($exists) { + $alternatives[$collectionName] += $threshold; + } + } + } + + foreach ($collection as $item) { + $lev = levenshtein($name, $item); + if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) { + $alternatives[$item] = isset($alternatives[$item]) ? $alternatives[$item] - $lev : $lev; + } + } + + $alternatives = array_filter($alternatives, function ($lev) use ($threshold) { return $lev < 2 * $threshold; }); + asort($alternatives); + + return array_keys($alternatives); + } + + /** + * Sets the default Command name. + * + * @param string $commandName The Command name + */ + public function setDefaultCommand($commandName) + { + $this->defaultCommand = $commandName; + } + + private function stringWidth($string) + { + if (false === $encoding = mb_detect_encoding($string, null, true)) { + return strlen($string); + } + + return mb_strwidth($string, $encoding); + } + + private function splitStringByWidth($string, $width) + { + // str_split is not suitable for multi-byte characters, we should use preg_split to get char array properly. + // additionally, array_slice() is not enough as some character has doubled width. + // we need a function to split string not by character count but by string width + if (false === $encoding = mb_detect_encoding($string, null, true)) { + return str_split($string, $width); + } + + $utf8String = mb_convert_encoding($string, 'utf8', $encoding); + $lines = array(); + $line = ''; + foreach (preg_split('//u', $utf8String) as $char) { + // test if $char could be appended to current line + if (mb_strwidth($line.$char, 'utf8') <= $width) { + $line .= $char; + continue; + } + // if not, push current line to array and make new line + $lines[] = str_pad($line, $width); + $line = $char; + } + if ('' !== $line) { + $lines[] = count($lines) ? str_pad($line, $width) : $line; + } + + mb_convert_variables($encoding, 'utf8', $lines); + + return $lines; + } + + /** + * Returns all namespaces of the command name. + * + * @param string $name The full name of the command + * + * @return array The namespaces of the command + */ + private function extractAllNamespaces($name) + { + // -1 as third argument is needed to skip the command short name when exploding + $parts = explode(':', $name, -1); + $namespaces = array(); + + foreach ($parts as $part) { + if (count($namespaces)) { + $namespaces[] = end($namespaces).':'.$part; + } else { + $namespaces[] = $part; + } + } + + return $namespaces; + } +} diff --git a/vendor/symfony/console/CHANGELOG.md b/vendor/symfony/console/CHANGELOG.md new file mode 100644 index 0000000000..8021068edf --- /dev/null +++ b/vendor/symfony/console/CHANGELOG.md @@ -0,0 +1,73 @@ +CHANGELOG +========= + +2.8.3 +----- + + * remove readline support from the question helper as it caused issues + +2.8.0 +----- + + * use readline for user input in the question helper when available to allow + the use of arrow keys + +2.6.0 +----- + + * added a Process helper + * added a DebugFormatter helper + +2.5.0 +----- + + * deprecated the dialog helper (use the question helper instead) + * deprecated TableHelper in favor of Table + * deprecated ProgressHelper in favor of ProgressBar + * added ConsoleLogger + * added a question helper + * added a way to set the process name of a command + * added a way to set a default command instead of `ListCommand` + +2.4.0 +----- + + * added a way to force terminal dimensions + * added a convenient method to detect verbosity level + * [BC BREAK] made descriptors use output instead of returning a string + +2.3.0 +----- + + * added multiselect support to the select dialog helper + * added Table Helper for tabular data rendering + * added support for events in `Application` + * added a way to normalize EOLs in `ApplicationTester::getDisplay()` and `CommandTester::getDisplay()` + * added a way to set the progress bar progress via the `setCurrent` method + * added support for multiple InputOption shortcuts, written as `'-a|-b|-c'` + * added two additional verbosity levels, VERBOSITY_VERY_VERBOSE and VERBOSITY_DEBUG + +2.2.0 +----- + + * added support for colorization on Windows via ConEmu + * add a method to Dialog Helper to ask for a question and hide the response + * added support for interactive selections in console (DialogHelper::select()) + * added support for autocompletion as you type in Dialog Helper + +2.1.0 +----- + + * added ConsoleOutputInterface + * added the possibility to disable a command (Command::isEnabled()) + * added suggestions when a command does not exist + * added a --raw option to the list command + * added support for STDERR in the console output class (errors are now sent + to STDERR) + * made the defaults (helper set, commands, input definition) in Application + more easily customizable + * added support for the shell even if readline is not available + * added support for process isolation in Symfony shell via + `--process-isolation` switch + * added support for `--`, which disables options parsing after that point + (tokens will be parsed as arguments) diff --git a/vendor/symfony/console/Command/Command.php b/vendor/symfony/console/Command/Command.php new file mode 100644 index 0000000000..6acbe2198f --- /dev/null +++ b/vendor/symfony/console/Command/Command.php @@ -0,0 +1,681 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Command; + +use Symfony\Component\Console\Descriptor\TextDescriptor; +use Symfony\Component\Console\Descriptor\XmlDescriptor; +use Symfony\Component\Console\Exception\ExceptionInterface; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\BufferedOutput; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; + +/** + * Base class for all commands. + * + * @author Fabien Potencier + */ +class Command +{ + private $application; + private $name; + private $processTitle; + private $aliases = array(); + private $definition; + private $help; + private $description; + private $ignoreValidationErrors = false; + private $applicationDefinitionMerged = false; + private $applicationDefinitionMergedWithArgs = false; + private $code; + private $synopsis = array(); + private $usages = array(); + private $helperSet; + + /** + * Constructor. + * + * @param string|null $name The name of the command; passing null means it must be set in configure() + * + * @throws LogicException When the command name is empty + */ + public function __construct($name = null) + { + $this->definition = new InputDefinition(); + + if (null !== $name) { + $this->setName($name); + } + + $this->configure(); + + if (!$this->name) { + throw new LogicException(sprintf('The command defined in "%s" cannot have an empty name.', get_class($this))); + } + } + + /** + * Ignores validation errors. + * + * This is mainly useful for the help command. + */ + public function ignoreValidationErrors() + { + $this->ignoreValidationErrors = true; + } + + /** + * Sets the application instance for this command. + * + * @param Application $application An Application instance + */ + public function setApplication(Application $application = null) + { + $this->application = $application; + if ($application) { + $this->setHelperSet($application->getHelperSet()); + } else { + $this->helperSet = null; + } + } + + /** + * Sets the helper set. + * + * @param HelperSet $helperSet A HelperSet instance + */ + public function setHelperSet(HelperSet $helperSet) + { + $this->helperSet = $helperSet; + } + + /** + * Gets the helper set. + * + * @return HelperSet A HelperSet instance + */ + public function getHelperSet() + { + return $this->helperSet; + } + + /** + * Gets the application instance for this command. + * + * @return Application An Application instance + */ + public function getApplication() + { + return $this->application; + } + + /** + * Checks whether the command is enabled or not in the current environment. + * + * Override this to check for x or y and return false if the command can not + * run properly under the current conditions. + * + * @return bool + */ + public function isEnabled() + { + return true; + } + + /** + * Configures the current command. + */ + protected function configure() + { + } + + /** + * Executes the current command. + * + * This method is not abstract because you can use this class + * as a concrete class. In this case, instead of defining the + * execute() method, you set the code to execute by passing + * a Closure to the setCode() method. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + * + * @return null|int null or 0 if everything went fine, or an error code + * + * @throws LogicException When this abstract method is not implemented + * + * @see setCode() + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + throw new LogicException('You must override the execute() method in the concrete command class.'); + } + + /** + * Interacts with the user. + * + * This method is executed before the InputDefinition is validated. + * This means that this is the only place where the command can + * interactively ask for values of missing required arguments. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + */ + protected function interact(InputInterface $input, OutputInterface $output) + { + } + + /** + * Initializes the command just after the input has been validated. + * + * This is mainly useful when a lot of commands extends one main command + * where some things need to be initialized based on the input arguments and options. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + */ + protected function initialize(InputInterface $input, OutputInterface $output) + { + } + + /** + * Runs the command. + * + * The code to execute is either defined directly with the + * setCode() method or by overriding the execute() method + * in a sub-class. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + * + * @return int The command exit code + * + * @throws \Exception + * + * @see setCode() + * @see execute() + */ + public function run(InputInterface $input, OutputInterface $output) + { + // force the creation of the synopsis before the merge with the app definition + $this->getSynopsis(true); + $this->getSynopsis(false); + + // add the application arguments and options + $this->mergeApplicationDefinition(); + + // bind the input against the command specific arguments/options + try { + $input->bind($this->definition); + } catch (ExceptionInterface $e) { + if (!$this->ignoreValidationErrors) { + throw $e; + } + } + + $this->initialize($input, $output); + + if (null !== $this->processTitle) { + if (function_exists('cli_set_process_title')) { + cli_set_process_title($this->processTitle); + } elseif (function_exists('setproctitle')) { + setproctitle($this->processTitle); + } elseif (OutputInterface::VERBOSITY_VERY_VERBOSE === $output->getVerbosity()) { + $output->writeln('Install the proctitle PECL to be able to change the process title.'); + } + } + + if ($input->isInteractive()) { + $this->interact($input, $output); + } + + // The command name argument is often omitted when a command is executed directly with its run() method. + // It would fail the validation if we didn't make sure the command argument is present, + // since it's required by the application. + if ($input->hasArgument('command') && null === $input->getArgument('command')) { + $input->setArgument('command', $this->getName()); + } + + $input->validate(); + + if ($this->code) { + $statusCode = call_user_func($this->code, $input, $output); + } else { + $statusCode = $this->execute($input, $output); + } + + return is_numeric($statusCode) ? (int) $statusCode : 0; + } + + /** + * Sets the code to execute when running this command. + * + * If this method is used, it overrides the code defined + * in the execute() method. + * + * @param callable $code A callable(InputInterface $input, OutputInterface $output) + * + * @return Command The current instance + * + * @throws InvalidArgumentException + * + * @see execute() + */ + public function setCode($code) + { + if (!is_callable($code)) { + throw new InvalidArgumentException('Invalid callable provided to Command::setCode.'); + } + + if (PHP_VERSION_ID >= 50400 && $code instanceof \Closure) { + $r = new \ReflectionFunction($code); + if (null === $r->getClosureThis()) { + $code = \Closure::bind($code, $this); + } + } + + $this->code = $code; + + return $this; + } + + /** + * Merges the application definition with the command definition. + * + * This method is not part of public API and should not be used directly. + * + * @param bool $mergeArgs Whether to merge or not the Application definition arguments to Command definition arguments + */ + public function mergeApplicationDefinition($mergeArgs = true) + { + if (null === $this->application || (true === $this->applicationDefinitionMerged && ($this->applicationDefinitionMergedWithArgs || !$mergeArgs))) { + return; + } + + $this->definition->addOptions($this->application->getDefinition()->getOptions()); + + if ($mergeArgs) { + $currentArguments = $this->definition->getArguments(); + $this->definition->setArguments($this->application->getDefinition()->getArguments()); + $this->definition->addArguments($currentArguments); + } + + $this->applicationDefinitionMerged = true; + if ($mergeArgs) { + $this->applicationDefinitionMergedWithArgs = true; + } + } + + /** + * Sets an array of argument and option instances. + * + * @param array|InputDefinition $definition An array of argument and option instances or a definition instance + * + * @return Command The current instance + */ + public function setDefinition($definition) + { + if ($definition instanceof InputDefinition) { + $this->definition = $definition; + } else { + $this->definition->setDefinition($definition); + } + + $this->applicationDefinitionMerged = false; + + return $this; + } + + /** + * Gets the InputDefinition attached to this Command. + * + * @return InputDefinition An InputDefinition instance + */ + public function getDefinition() + { + return $this->definition; + } + + /** + * Gets the InputDefinition to be used to create XML and Text representations of this Command. + * + * Can be overridden to provide the original command representation when it would otherwise + * be changed by merging with the application InputDefinition. + * + * This method is not part of public API and should not be used directly. + * + * @return InputDefinition An InputDefinition instance + */ + public function getNativeDefinition() + { + return $this->getDefinition(); + } + + /** + * Adds an argument. + * + * @param string $name The argument name + * @param int $mode The argument mode: InputArgument::REQUIRED or InputArgument::OPTIONAL + * @param string $description A description text + * @param mixed $default The default value (for InputArgument::OPTIONAL mode only) + * + * @return Command The current instance + */ + public function addArgument($name, $mode = null, $description = '', $default = null) + { + $this->definition->addArgument(new InputArgument($name, $mode, $description, $default)); + + return $this; + } + + /** + * Adds an option. + * + * @param string $name The option name + * @param string $shortcut The shortcut (can be null) + * @param int $mode The option mode: One of the InputOption::VALUE_* constants + * @param string $description A description text + * @param mixed $default The default value (must be null for InputOption::VALUE_NONE) + * + * @return Command The current instance + */ + public function addOption($name, $shortcut = null, $mode = null, $description = '', $default = null) + { + $this->definition->addOption(new InputOption($name, $shortcut, $mode, $description, $default)); + + return $this; + } + + /** + * Sets the name of the command. + * + * This method can set both the namespace and the name if + * you separate them by a colon (:) + * + * $command->setName('foo:bar'); + * + * @param string $name The command name + * + * @return Command The current instance + * + * @throws InvalidArgumentException When the name is invalid + */ + public function setName($name) + { + $this->validateName($name); + + $this->name = $name; + + return $this; + } + + /** + * Sets the process title of the command. + * + * This feature should be used only when creating a long process command, + * like a daemon. + * + * PHP 5.5+ or the proctitle PECL library is required + * + * @param string $title The process title + * + * @return Command The current instance + */ + public function setProcessTitle($title) + { + $this->processTitle = $title; + + return $this; + } + + /** + * Returns the command name. + * + * @return string The command name + */ + public function getName() + { + return $this->name; + } + + /** + * Sets the description for the command. + * + * @param string $description The description for the command + * + * @return Command The current instance + */ + public function setDescription($description) + { + $this->description = $description; + + return $this; + } + + /** + * Returns the description for the command. + * + * @return string The description for the command + */ + public function getDescription() + { + return $this->description; + } + + /** + * Sets the help for the command. + * + * @param string $help The help for the command + * + * @return Command The current instance + */ + public function setHelp($help) + { + $this->help = $help; + + return $this; + } + + /** + * Returns the help for the command. + * + * @return string The help for the command + */ + public function getHelp() + { + return $this->help; + } + + /** + * Returns the processed help for the command replacing the %command.name% and + * %command.full_name% patterns with the real values dynamically. + * + * @return string The processed help for the command + */ + public function getProcessedHelp() + { + $name = $this->name; + + $placeholders = array( + '%command.name%', + '%command.full_name%', + ); + $replacements = array( + $name, + $_SERVER['PHP_SELF'].' '.$name, + ); + + return str_replace($placeholders, $replacements, $this->getHelp() ?: $this->getDescription()); + } + + /** + * Sets the aliases for the command. + * + * @param string[] $aliases An array of aliases for the command + * + * @return Command The current instance + * + * @throws InvalidArgumentException When an alias is invalid + */ + public function setAliases($aliases) + { + if (!is_array($aliases) && !$aliases instanceof \Traversable) { + throw new InvalidArgumentException('$aliases must be an array or an instance of \Traversable'); + } + + foreach ($aliases as $alias) { + $this->validateName($alias); + } + + $this->aliases = $aliases; + + return $this; + } + + /** + * Returns the aliases for the command. + * + * @return array An array of aliases for the command + */ + public function getAliases() + { + return $this->aliases; + } + + /** + * Returns the synopsis for the command. + * + * @param bool $short Whether to show the short version of the synopsis (with options folded) or not + * + * @return string The synopsis + */ + public function getSynopsis($short = false) + { + $key = $short ? 'short' : 'long'; + + if (!isset($this->synopsis[$key])) { + $this->synopsis[$key] = trim(sprintf('%s %s', $this->name, $this->definition->getSynopsis($short))); + } + + return $this->synopsis[$key]; + } + + /** + * Add a command usage example. + * + * @param string $usage The usage, it'll be prefixed with the command name + */ + public function addUsage($usage) + { + if (0 !== strpos($usage, $this->name)) { + $usage = sprintf('%s %s', $this->name, $usage); + } + + $this->usages[] = $usage; + + return $this; + } + + /** + * Returns alternative usages of the command. + * + * @return array + */ + public function getUsages() + { + return $this->usages; + } + + /** + * Gets a helper instance by name. + * + * @param string $name The helper name + * + * @return mixed The helper value + * + * @throws LogicException if no HelperSet is defined + * @throws InvalidArgumentException if the helper is not defined + */ + public function getHelper($name) + { + if (null === $this->helperSet) { + throw new LogicException(sprintf('Cannot retrieve helper "%s" because there is no HelperSet defined. Did you forget to add your command to the application or to set the application on the command using the setApplication() method? You can also set the HelperSet directly using the setHelperSet() method.', $name)); + } + + return $this->helperSet->get($name); + } + + /** + * Returns a text representation of the command. + * + * @return string A string representing the command + * + * @deprecated since version 2.3, to be removed in 3.0. + */ + public function asText() + { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.3 and will be removed in 3.0.', E_USER_DEPRECATED); + + $descriptor = new TextDescriptor(); + $output = new BufferedOutput(BufferedOutput::VERBOSITY_NORMAL, true); + $descriptor->describe($output, $this, array('raw_output' => true)); + + return $output->fetch(); + } + + /** + * Returns an XML representation of the command. + * + * @param bool $asDom Whether to return a DOM or an XML string + * + * @return string|\DOMDocument An XML string representing the command + * + * @deprecated since version 2.3, to be removed in 3.0. + */ + public function asXml($asDom = false) + { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.3 and will be removed in 3.0.', E_USER_DEPRECATED); + + $descriptor = new XmlDescriptor(); + + if ($asDom) { + return $descriptor->getCommandDocument($this); + } + + $output = new BufferedOutput(); + $descriptor->describe($output, $this); + + return $output->fetch(); + } + + /** + * Validates a command name. + * + * It must be non-empty and parts can optionally be separated by ":". + * + * @param string $name + * + * @throws InvalidArgumentException When the name is invalid + */ + private function validateName($name) + { + if (!preg_match('/^[^\:]++(\:[^\:]++)*$/', $name)) { + throw new InvalidArgumentException(sprintf('Command name "%s" is invalid.', $name)); + } + } +} diff --git a/vendor/symfony/console/Command/HelpCommand.php b/vendor/symfony/console/Command/HelpCommand.php new file mode 100644 index 0000000000..c0e7b38843 --- /dev/null +++ b/vendor/symfony/console/Command/HelpCommand.php @@ -0,0 +1,93 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Command; + +use Symfony\Component\Console\Helper\DescriptorHelper; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * HelpCommand displays the help for a given command. + * + * @author Fabien Potencier + */ +class HelpCommand extends Command +{ + private $command; + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this->ignoreValidationErrors(); + + $this + ->setName('help') + ->setDefinition(array( + new InputArgument('command_name', InputArgument::OPTIONAL, 'The command name', 'help'), + new InputOption('xml', null, InputOption::VALUE_NONE, 'To output help as XML'), + new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'), + new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command help'), + )) + ->setDescription('Displays help for a command') + ->setHelp(<<<'EOF' +The %command.name% command displays help for a given command: + + php %command.full_name% list + +You can also output the help in other formats by using the --format option: + + php %command.full_name% --format=xml list + +To display the list of available commands, please use the list command. +EOF + ) + ; + } + + /** + * Sets the command. + * + * @param Command $command The command to set + */ + public function setCommand(Command $command) + { + $this->command = $command; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + if (null === $this->command) { + $this->command = $this->getApplication()->find($input->getArgument('command_name')); + } + + if ($input->getOption('xml')) { + @trigger_error('The --xml option was deprecated in version 2.7 and will be removed in version 3.0. Use the --format option instead.', E_USER_DEPRECATED); + + $input->setOption('format', 'xml'); + } + + $helper = new DescriptorHelper(); + $helper->describe($output, $this->command, array( + 'format' => $input->getOption('format'), + 'raw_text' => $input->getOption('raw'), + )); + + $this->command = null; + } +} diff --git a/vendor/symfony/console/Command/ListCommand.php b/vendor/symfony/console/Command/ListCommand.php new file mode 100644 index 0000000000..5e1b926aed --- /dev/null +++ b/vendor/symfony/console/Command/ListCommand.php @@ -0,0 +1,97 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Command; + +use Symfony\Component\Console\Helper\DescriptorHelper; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputDefinition; + +/** + * ListCommand displays the list of all available commands for the application. + * + * @author Fabien Potencier + */ +class ListCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('list') + ->setDefinition($this->createDefinition()) + ->setDescription('Lists commands') + ->setHelp(<<<'EOF' +The %command.name% command lists all commands: + + php %command.full_name% + +You can also display the commands for a specific namespace: + + php %command.full_name% test + +You can also output the information in other formats by using the --format option: + + php %command.full_name% --format=xml + +It's also possible to get raw list of commands (useful for embedding command runner): + + php %command.full_name% --raw +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + public function getNativeDefinition() + { + return $this->createDefinition(); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + if ($input->getOption('xml')) { + @trigger_error('The --xml option was deprecated in version 2.7 and will be removed in version 3.0. Use the --format option instead.', E_USER_DEPRECATED); + + $input->setOption('format', 'xml'); + } + + $helper = new DescriptorHelper(); + $helper->describe($output, $this->getApplication(), array( + 'format' => $input->getOption('format'), + 'raw_text' => $input->getOption('raw'), + 'namespace' => $input->getArgument('namespace'), + )); + } + + /** + * {@inheritdoc} + */ + private function createDefinition() + { + return new InputDefinition(array( + new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name'), + new InputOption('xml', null, InputOption::VALUE_NONE, 'To output list as XML'), + new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command list'), + new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'), + )); + } +} diff --git a/vendor/symfony/console/ConsoleEvents.php b/vendor/symfony/console/ConsoleEvents.php new file mode 100644 index 0000000000..1ed41b7daa --- /dev/null +++ b/vendor/symfony/console/ConsoleEvents.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console; + +/** + * Contains all events dispatched by an Application. + * + * @author Francesco Levorato + */ +final class ConsoleEvents +{ + /** + * The COMMAND event allows you to attach listeners before any command is + * executed by the console. It also allows you to modify the command, input and output + * before they are handled to the command. + * + * The event listener method receives a Symfony\Component\Console\Event\ConsoleCommandEvent + * instance. + * + * @Event + * + * @var string + */ + const COMMAND = 'console.command'; + + /** + * The TERMINATE event allows you to attach listeners after a command is + * executed by the console. + * + * The event listener method receives a Symfony\Component\Console\Event\ConsoleTerminateEvent + * instance. + * + * @Event + * + * @var string + */ + const TERMINATE = 'console.terminate'; + + /** + * The EXCEPTION event occurs when an uncaught exception appears. + * + * This event allows you to deal with the exception or + * to modify the thrown exception. The event listener method receives + * a Symfony\Component\Console\Event\ConsoleExceptionEvent + * instance. + * + * @Event + * + * @var string + */ + const EXCEPTION = 'console.exception'; +} diff --git a/vendor/symfony/console/Descriptor/ApplicationDescription.php b/vendor/symfony/console/Descriptor/ApplicationDescription.php new file mode 100644 index 0000000000..89961b9cae --- /dev/null +++ b/vendor/symfony/console/Descriptor/ApplicationDescription.php @@ -0,0 +1,160 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Exception\CommandNotFoundException; + +/** + * @author Jean-François Simon + * + * @internal + */ +class ApplicationDescription +{ + const GLOBAL_NAMESPACE = '_global'; + + /** + * @var Application + */ + private $application; + + /** + * @var null|string + */ + private $namespace; + + /** + * @var array + */ + private $namespaces; + + /** + * @var Command[] + */ + private $commands; + + /** + * @var Command[] + */ + private $aliases; + + /** + * Constructor. + * + * @param Application $application + * @param string|null $namespace + */ + public function __construct(Application $application, $namespace = null) + { + $this->application = $application; + $this->namespace = $namespace; + } + + /** + * @return array + */ + public function getNamespaces() + { + if (null === $this->namespaces) { + $this->inspectApplication(); + } + + return $this->namespaces; + } + + /** + * @return Command[] + */ + public function getCommands() + { + if (null === $this->commands) { + $this->inspectApplication(); + } + + return $this->commands; + } + + /** + * @param string $name + * + * @return Command + * + * @throws CommandNotFoundException + */ + public function getCommand($name) + { + if (!isset($this->commands[$name]) && !isset($this->aliases[$name])) { + throw new CommandNotFoundException(sprintf('Command %s does not exist.', $name)); + } + + return isset($this->commands[$name]) ? $this->commands[$name] : $this->aliases[$name]; + } + + private function inspectApplication() + { + $this->commands = array(); + $this->namespaces = array(); + + $all = $this->application->all($this->namespace ? $this->application->findNamespace($this->namespace) : null); + foreach ($this->sortCommands($all) as $namespace => $commands) { + $names = array(); + + /** @var Command $command */ + foreach ($commands as $name => $command) { + if (!$command->getName()) { + continue; + } + + if ($command->getName() === $name) { + $this->commands[$name] = $command; + } else { + $this->aliases[$name] = $command; + } + + $names[] = $name; + } + + $this->namespaces[$namespace] = array('id' => $namespace, 'commands' => $names); + } + } + + /** + * @param array $commands + * + * @return array + */ + private function sortCommands(array $commands) + { + $namespacedCommands = array(); + $globalCommands = array(); + foreach ($commands as $name => $command) { + $key = $this->application->extractNamespace($name, 1); + if (!$key) { + $globalCommands['_global'][$name] = $command; + } else { + $namespacedCommands[$key][$name] = $command; + } + } + ksort($namespacedCommands); + $namespacedCommands = array_merge($globalCommands, $namespacedCommands); + + foreach ($namespacedCommands as &$commandsSet) { + ksort($commandsSet); + } + // unset reference to keep scope clear + unset($commandsSet); + + return $namespacedCommands; + } +} diff --git a/vendor/symfony/console/Descriptor/Descriptor.php b/vendor/symfony/console/Descriptor/Descriptor.php new file mode 100644 index 0000000000..43a7a0a1fe --- /dev/null +++ b/vendor/symfony/console/Descriptor/Descriptor.php @@ -0,0 +1,122 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * @author Jean-François Simon + * + * @internal + */ +abstract class Descriptor implements DescriptorInterface +{ + /** + * @var OutputInterface + */ + private $output; + + /** + * {@inheritdoc} + */ + public function describe(OutputInterface $output, $object, array $options = array()) + { + $this->output = $output; + + switch (true) { + case $object instanceof InputArgument: + $this->describeInputArgument($object, $options); + break; + case $object instanceof InputOption: + $this->describeInputOption($object, $options); + break; + case $object instanceof InputDefinition: + $this->describeInputDefinition($object, $options); + break; + case $object instanceof Command: + $this->describeCommand($object, $options); + break; + case $object instanceof Application: + $this->describeApplication($object, $options); + break; + default: + throw new InvalidArgumentException(sprintf('Object of type "%s" is not describable.', get_class($object))); + } + } + + /** + * Writes content to output. + * + * @param string $content + * @param bool $decorated + */ + protected function write($content, $decorated = false) + { + $this->output->write($content, false, $decorated ? OutputInterface::OUTPUT_NORMAL : OutputInterface::OUTPUT_RAW); + } + + /** + * Describes an InputArgument instance. + * + * @param InputArgument $argument + * @param array $options + * + * @return string|mixed + */ + abstract protected function describeInputArgument(InputArgument $argument, array $options = array()); + + /** + * Describes an InputOption instance. + * + * @param InputOption $option + * @param array $options + * + * @return string|mixed + */ + abstract protected function describeInputOption(InputOption $option, array $options = array()); + + /** + * Describes an InputDefinition instance. + * + * @param InputDefinition $definition + * @param array $options + * + * @return string|mixed + */ + abstract protected function describeInputDefinition(InputDefinition $definition, array $options = array()); + + /** + * Describes a Command instance. + * + * @param Command $command + * @param array $options + * + * @return string|mixed + */ + abstract protected function describeCommand(Command $command, array $options = array()); + + /** + * Describes an Application instance. + * + * @param Application $application + * @param array $options + * + * @return string|mixed + */ + abstract protected function describeApplication(Application $application, array $options = array()); +} diff --git a/vendor/symfony/console/Descriptor/DescriptorInterface.php b/vendor/symfony/console/Descriptor/DescriptorInterface.php new file mode 100644 index 0000000000..3929b6d9ed --- /dev/null +++ b/vendor/symfony/console/Descriptor/DescriptorInterface.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Descriptor interface. + * + * @author Jean-François Simon + */ +interface DescriptorInterface +{ + /** + * Describes an InputArgument instance. + * + * @param OutputInterface $output + * @param object $object + * @param array $options + */ + public function describe(OutputInterface $output, $object, array $options = array()); +} diff --git a/vendor/symfony/console/Descriptor/JsonDescriptor.php b/vendor/symfony/console/Descriptor/JsonDescriptor.php new file mode 100644 index 0000000000..87e38fdb89 --- /dev/null +++ b/vendor/symfony/console/Descriptor/JsonDescriptor.php @@ -0,0 +1,166 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; + +/** + * JSON descriptor. + * + * @author Jean-François Simon + * + * @internal + */ +class JsonDescriptor extends Descriptor +{ + /** + * {@inheritdoc} + */ + protected function describeInputArgument(InputArgument $argument, array $options = array()) + { + $this->writeData($this->getInputArgumentData($argument), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeInputOption(InputOption $option, array $options = array()) + { + $this->writeData($this->getInputOptionData($option), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeInputDefinition(InputDefinition $definition, array $options = array()) + { + $this->writeData($this->getInputDefinitionData($definition), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeCommand(Command $command, array $options = array()) + { + $this->writeData($this->getCommandData($command), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeApplication(Application $application, array $options = array()) + { + $describedNamespace = isset($options['namespace']) ? $options['namespace'] : null; + $description = new ApplicationDescription($application, $describedNamespace); + $commands = array(); + + foreach ($description->getCommands() as $command) { + $commands[] = $this->getCommandData($command); + } + + $data = $describedNamespace + ? array('commands' => $commands, 'namespace' => $describedNamespace) + : array('commands' => $commands, 'namespaces' => array_values($description->getNamespaces())); + + $this->writeData($data, $options); + } + + /** + * Writes data as json. + * + * @param array $data + * @param array $options + * + * @return array|string + */ + private function writeData(array $data, array $options) + { + $this->write(json_encode($data, isset($options['json_encoding']) ? $options['json_encoding'] : 0)); + } + + /** + * @param InputArgument $argument + * + * @return array + */ + private function getInputArgumentData(InputArgument $argument) + { + return array( + 'name' => $argument->getName(), + 'is_required' => $argument->isRequired(), + 'is_array' => $argument->isArray(), + 'description' => preg_replace('/\s*[\r\n]\s*/', ' ', $argument->getDescription()), + 'default' => $argument->getDefault(), + ); + } + + /** + * @param InputOption $option + * + * @return array + */ + private function getInputOptionData(InputOption $option) + { + return array( + 'name' => '--'.$option->getName(), + 'shortcut' => $option->getShortcut() ? '-'.implode('|-', explode('|', $option->getShortcut())) : '', + 'accept_value' => $option->acceptValue(), + 'is_value_required' => $option->isValueRequired(), + 'is_multiple' => $option->isArray(), + 'description' => preg_replace('/\s*[\r\n]\s*/', ' ', $option->getDescription()), + 'default' => $option->getDefault(), + ); + } + + /** + * @param InputDefinition $definition + * + * @return array + */ + private function getInputDefinitionData(InputDefinition $definition) + { + $inputArguments = array(); + foreach ($definition->getArguments() as $name => $argument) { + $inputArguments[$name] = $this->getInputArgumentData($argument); + } + + $inputOptions = array(); + foreach ($definition->getOptions() as $name => $option) { + $inputOptions[$name] = $this->getInputOptionData($option); + } + + return array('arguments' => $inputArguments, 'options' => $inputOptions); + } + + /** + * @param Command $command + * + * @return array + */ + private function getCommandData(Command $command) + { + $command->getSynopsis(); + $command->mergeApplicationDefinition(false); + + return array( + 'name' => $command->getName(), + 'usage' => array_merge(array($command->getSynopsis()), $command->getUsages(), $command->getAliases()), + 'description' => $command->getDescription(), + 'help' => $command->getProcessedHelp(), + 'definition' => $this->getInputDefinitionData($command->getNativeDefinition()), + ); + } +} diff --git a/vendor/symfony/console/Descriptor/MarkdownDescriptor.php b/vendor/symfony/console/Descriptor/MarkdownDescriptor.php new file mode 100644 index 0000000000..d3d76a4201 --- /dev/null +++ b/vendor/symfony/console/Descriptor/MarkdownDescriptor.php @@ -0,0 +1,143 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; + +/** + * Markdown descriptor. + * + * @author Jean-François Simon + * + * @internal + */ +class MarkdownDescriptor extends Descriptor +{ + /** + * {@inheritdoc} + */ + protected function describeInputArgument(InputArgument $argument, array $options = array()) + { + $this->write( + '**'.$argument->getName().':**'."\n\n" + .'* Name: '.($argument->getName() ?: '')."\n" + .'* Is required: '.($argument->isRequired() ? 'yes' : 'no')."\n" + .'* Is array: '.($argument->isArray() ? 'yes' : 'no')."\n" + .'* Description: '.preg_replace('/\s*[\r\n]\s*/', "\n ", $argument->getDescription() ?: '')."\n" + .'* Default: `'.str_replace("\n", '', var_export($argument->getDefault(), true)).'`' + ); + } + + /** + * {@inheritdoc} + */ + protected function describeInputOption(InputOption $option, array $options = array()) + { + $this->write( + '**'.$option->getName().':**'."\n\n" + .'* Name: `--'.$option->getName().'`'."\n" + .'* Shortcut: '.($option->getShortcut() ? '`-'.implode('|-', explode('|', $option->getShortcut())).'`' : '')."\n" + .'* Accept value: '.($option->acceptValue() ? 'yes' : 'no')."\n" + .'* Is value required: '.($option->isValueRequired() ? 'yes' : 'no')."\n" + .'* Is multiple: '.($option->isArray() ? 'yes' : 'no')."\n" + .'* Description: '.preg_replace('/\s*[\r\n]\s*/', "\n ", $option->getDescription() ?: '')."\n" + .'* Default: `'.str_replace("\n", '', var_export($option->getDefault(), true)).'`' + ); + } + + /** + * {@inheritdoc} + */ + protected function describeInputDefinition(InputDefinition $definition, array $options = array()) + { + if ($showArguments = count($definition->getArguments()) > 0) { + $this->write('### Arguments:'); + foreach ($definition->getArguments() as $argument) { + $this->write("\n\n"); + $this->write($this->describeInputArgument($argument)); + } + } + + if (count($definition->getOptions()) > 0) { + if ($showArguments) { + $this->write("\n\n"); + } + + $this->write('### Options:'); + foreach ($definition->getOptions() as $option) { + $this->write("\n\n"); + $this->write($this->describeInputOption($option)); + } + } + } + + /** + * {@inheritdoc} + */ + protected function describeCommand(Command $command, array $options = array()) + { + $command->getSynopsis(); + $command->mergeApplicationDefinition(false); + + $this->write( + $command->getName()."\n" + .str_repeat('-', strlen($command->getName()))."\n\n" + .'* Description: '.($command->getDescription() ?: '')."\n" + .'* Usage:'."\n\n" + .array_reduce(array_merge(array($command->getSynopsis()), $command->getAliases(), $command->getUsages()), function ($carry, $usage) { + return $carry .= ' * `'.$usage.'`'."\n"; + }) + ); + + if ($help = $command->getProcessedHelp()) { + $this->write("\n"); + $this->write($help); + } + + if ($command->getNativeDefinition()) { + $this->write("\n\n"); + $this->describeInputDefinition($command->getNativeDefinition()); + } + } + + /** + * {@inheritdoc} + */ + protected function describeApplication(Application $application, array $options = array()) + { + $describedNamespace = isset($options['namespace']) ? $options['namespace'] : null; + $description = new ApplicationDescription($application, $describedNamespace); + + $this->write($application->getName()."\n".str_repeat('=', strlen($application->getName()))); + + foreach ($description->getNamespaces() as $namespace) { + if (ApplicationDescription::GLOBAL_NAMESPACE !== $namespace['id']) { + $this->write("\n\n"); + $this->write('**'.$namespace['id'].':**'); + } + + $this->write("\n\n"); + $this->write(implode("\n", array_map(function ($commandName) { + return '* '.$commandName; + }, $namespace['commands']))); + } + + foreach ($description->getCommands() as $command) { + $this->write("\n\n"); + $this->write($this->describeCommand($command)); + } + } +} diff --git a/vendor/symfony/console/Descriptor/TextDescriptor.php b/vendor/symfony/console/Descriptor/TextDescriptor.php new file mode 100644 index 0000000000..64b5397167 --- /dev/null +++ b/vendor/symfony/console/Descriptor/TextDescriptor.php @@ -0,0 +1,287 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; + +/** + * Text descriptor. + * + * @author Jean-François Simon + * + * @internal + */ +class TextDescriptor extends Descriptor +{ + /** + * {@inheritdoc} + */ + protected function describeInputArgument(InputArgument $argument, array $options = array()) + { + if (null !== $argument->getDefault() && (!is_array($argument->getDefault()) || count($argument->getDefault()))) { + $default = sprintf(' [default: %s]', $this->formatDefaultValue($argument->getDefault())); + } else { + $default = ''; + } + + $totalWidth = isset($options['total_width']) ? $options['total_width'] : strlen($argument->getName()); + $spacingWidth = $totalWidth - strlen($argument->getName()) + 2; + + $this->writeText(sprintf(' %s%s%s%s', + $argument->getName(), + str_repeat(' ', $spacingWidth), + // + 17 = 2 spaces + + + 2 spaces + preg_replace('/\s*[\r\n]\s*/', "\n".str_repeat(' ', $totalWidth + 17), $argument->getDescription()), + $default + ), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeInputOption(InputOption $option, array $options = array()) + { + if ($option->acceptValue() && null !== $option->getDefault() && (!is_array($option->getDefault()) || count($option->getDefault()))) { + $default = sprintf(' [default: %s]', $this->formatDefaultValue($option->getDefault())); + } else { + $default = ''; + } + + $value = ''; + if ($option->acceptValue()) { + $value = '='.strtoupper($option->getName()); + + if ($option->isValueOptional()) { + $value = '['.$value.']'; + } + } + + $totalWidth = isset($options['total_width']) ? $options['total_width'] : $this->calculateTotalWidthForOptions(array($option)); + $synopsis = sprintf('%s%s', + $option->getShortcut() ? sprintf('-%s, ', $option->getShortcut()) : ' ', + sprintf('--%s%s', $option->getName(), $value) + ); + + $spacingWidth = $totalWidth - strlen($synopsis) + 2; + + $this->writeText(sprintf(' %s%s%s%s%s', + $synopsis, + str_repeat(' ', $spacingWidth), + // + 17 = 2 spaces + + + 2 spaces + preg_replace('/\s*[\r\n]\s*/', "\n".str_repeat(' ', $totalWidth + 17), $option->getDescription()), + $default, + $option->isArray() ? ' (multiple values allowed)' : '' + ), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeInputDefinition(InputDefinition $definition, array $options = array()) + { + $totalWidth = $this->calculateTotalWidthForOptions($definition->getOptions()); + foreach ($definition->getArguments() as $argument) { + $totalWidth = max($totalWidth, strlen($argument->getName())); + } + + if ($definition->getArguments()) { + $this->writeText('Arguments:', $options); + $this->writeText("\n"); + foreach ($definition->getArguments() as $argument) { + $this->describeInputArgument($argument, array_merge($options, array('total_width' => $totalWidth))); + $this->writeText("\n"); + } + } + + if ($definition->getArguments() && $definition->getOptions()) { + $this->writeText("\n"); + } + + if ($definition->getOptions()) { + $laterOptions = array(); + + $this->writeText('Options:', $options); + foreach ($definition->getOptions() as $option) { + if (strlen($option->getShortcut()) > 1) { + $laterOptions[] = $option; + continue; + } + $this->writeText("\n"); + $this->describeInputOption($option, array_merge($options, array('total_width' => $totalWidth))); + } + foreach ($laterOptions as $option) { + $this->writeText("\n"); + $this->describeInputOption($option, array_merge($options, array('total_width' => $totalWidth))); + } + } + } + + /** + * {@inheritdoc} + */ + protected function describeCommand(Command $command, array $options = array()) + { + $command->getSynopsis(true); + $command->getSynopsis(false); + $command->mergeApplicationDefinition(false); + + $this->writeText('Usage:', $options); + foreach (array_merge(array($command->getSynopsis(true)), $command->getAliases(), $command->getUsages()) as $usage) { + $this->writeText("\n"); + $this->writeText(' '.$usage, $options); + } + $this->writeText("\n"); + + $definition = $command->getNativeDefinition(); + if ($definition->getOptions() || $definition->getArguments()) { + $this->writeText("\n"); + $this->describeInputDefinition($definition, $options); + $this->writeText("\n"); + } + + if ($help = $command->getProcessedHelp()) { + $this->writeText("\n"); + $this->writeText('Help:', $options); + $this->writeText("\n"); + $this->writeText(' '.str_replace("\n", "\n ", $help), $options); + $this->writeText("\n"); + } + } + + /** + * {@inheritdoc} + */ + protected function describeApplication(Application $application, array $options = array()) + { + $describedNamespace = isset($options['namespace']) ? $options['namespace'] : null; + $description = new ApplicationDescription($application, $describedNamespace); + + if (isset($options['raw_text']) && $options['raw_text']) { + $width = $this->getColumnWidth($description->getCommands()); + + foreach ($description->getCommands() as $command) { + $this->writeText(sprintf("%-{$width}s %s", $command->getName(), $command->getDescription()), $options); + $this->writeText("\n"); + } + } else { + if ('' != $help = $application->getHelp()) { + $this->writeText("$help\n\n", $options); + } + + $this->writeText("Usage:\n", $options); + $this->writeText(" command [options] [arguments]\n\n", $options); + + $this->describeInputDefinition(new InputDefinition($application->getDefinition()->getOptions()), $options); + + $this->writeText("\n"); + $this->writeText("\n"); + + $width = $this->getColumnWidth($description->getCommands()); + + if ($describedNamespace) { + $this->writeText(sprintf('Available commands for the "%s" namespace:', $describedNamespace), $options); + } else { + $this->writeText('Available commands:', $options); + } + + // add commands by namespace + foreach ($description->getNamespaces() as $namespace) { + if (!$describedNamespace && ApplicationDescription::GLOBAL_NAMESPACE !== $namespace['id']) { + $this->writeText("\n"); + $this->writeText(' '.$namespace['id'].'', $options); + } + + foreach ($namespace['commands'] as $name) { + $this->writeText("\n"); + $spacingWidth = $width - strlen($name); + $this->writeText(sprintf(' %s%s%s', $name, str_repeat(' ', $spacingWidth), $description->getCommand($name)->getDescription()), $options); + } + } + + $this->writeText("\n"); + } + } + + /** + * {@inheritdoc} + */ + private function writeText($content, array $options = array()) + { + $this->write( + isset($options['raw_text']) && $options['raw_text'] ? strip_tags($content) : $content, + isset($options['raw_output']) ? !$options['raw_output'] : true + ); + } + + /** + * Formats input option/argument default value. + * + * @param mixed $default + * + * @return string + */ + private function formatDefaultValue($default) + { + if (PHP_VERSION_ID < 50400) { + return str_replace(array('\/', '\\\\'), array('/', '\\'), json_encode($default)); + } + + return str_replace('\\\\', '\\', json_encode($default, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); + } + + /** + * @param Command[] $commands + * + * @return int + */ + private function getColumnWidth(array $commands) + { + $widths = array(); + + foreach ($commands as $command) { + $widths[] = strlen($command->getName()); + foreach ($command->getAliases() as $alias) { + $widths[] = strlen($alias); + } + } + + return max($widths) + 2; + } + + /** + * @param InputOption[] $options + * + * @return int + */ + private function calculateTotalWidthForOptions($options) + { + $totalWidth = 0; + foreach ($options as $option) { + // "-" + shortcut + ", --" + name + $nameLength = 1 + max(strlen($option->getShortcut()), 1) + 4 + strlen($option->getName()); + + if ($option->acceptValue()) { + $valueLength = 1 + strlen($option->getName()); // = + value + $valueLength += $option->isValueOptional() ? 2 : 0; // [ + ] + + $nameLength += $valueLength; + } + $totalWidth = max($totalWidth, $nameLength); + } + + return $totalWidth; + } +} diff --git a/vendor/symfony/console/Descriptor/XmlDescriptor.php b/vendor/symfony/console/Descriptor/XmlDescriptor.php new file mode 100644 index 0000000000..b5676beb37 --- /dev/null +++ b/vendor/symfony/console/Descriptor/XmlDescriptor.php @@ -0,0 +1,263 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; + +/** + * XML descriptor. + * + * @author Jean-François Simon + * + * @internal + */ +class XmlDescriptor extends Descriptor +{ + /** + * @param InputDefinition $definition + * + * @return \DOMDocument + */ + public function getInputDefinitionDocument(InputDefinition $definition) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($definitionXML = $dom->createElement('definition')); + + $definitionXML->appendChild($argumentsXML = $dom->createElement('arguments')); + foreach ($definition->getArguments() as $argument) { + $this->appendDocument($argumentsXML, $this->getInputArgumentDocument($argument)); + } + + $definitionXML->appendChild($optionsXML = $dom->createElement('options')); + foreach ($definition->getOptions() as $option) { + $this->appendDocument($optionsXML, $this->getInputOptionDocument($option)); + } + + return $dom; + } + + /** + * @param Command $command + * + * @return \DOMDocument + */ + public function getCommandDocument(Command $command) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($commandXML = $dom->createElement('command')); + + $command->getSynopsis(); + $command->mergeApplicationDefinition(false); + + $commandXML->setAttribute('id', $command->getName()); + $commandXML->setAttribute('name', $command->getName()); + + $commandXML->appendChild($usagesXML = $dom->createElement('usages')); + + foreach (array_merge(array($command->getSynopsis()), $command->getAliases(), $command->getUsages()) as $usage) { + $usagesXML->appendChild($dom->createElement('usage', $usage)); + } + + $commandXML->appendChild($descriptionXML = $dom->createElement('description')); + $descriptionXML->appendChild($dom->createTextNode(str_replace("\n", "\n ", $command->getDescription()))); + + $commandXML->appendChild($helpXML = $dom->createElement('help')); + $helpXML->appendChild($dom->createTextNode(str_replace("\n", "\n ", $command->getProcessedHelp()))); + + $definitionXML = $this->getInputDefinitionDocument($command->getNativeDefinition()); + $this->appendDocument($commandXML, $definitionXML->getElementsByTagName('definition')->item(0)); + + return $dom; + } + + /** + * @param Application $application + * @param string|null $namespace + * + * @return \DOMDocument + */ + public function getApplicationDocument(Application $application, $namespace = null) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($rootXml = $dom->createElement('symfony')); + + if ($application->getName() !== 'UNKNOWN') { + $rootXml->setAttribute('name', $application->getName()); + if ($application->getVersion() !== 'UNKNOWN') { + $rootXml->setAttribute('version', $application->getVersion()); + } + } + + $rootXml->appendChild($commandsXML = $dom->createElement('commands')); + + $description = new ApplicationDescription($application, $namespace); + + if ($namespace) { + $commandsXML->setAttribute('namespace', $namespace); + } + + foreach ($description->getCommands() as $command) { + $this->appendDocument($commandsXML, $this->getCommandDocument($command)); + } + + if (!$namespace) { + $rootXml->appendChild($namespacesXML = $dom->createElement('namespaces')); + + foreach ($description->getNamespaces() as $namespaceDescription) { + $namespacesXML->appendChild($namespaceArrayXML = $dom->createElement('namespace')); + $namespaceArrayXML->setAttribute('id', $namespaceDescription['id']); + + foreach ($namespaceDescription['commands'] as $name) { + $namespaceArrayXML->appendChild($commandXML = $dom->createElement('command')); + $commandXML->appendChild($dom->createTextNode($name)); + } + } + } + + return $dom; + } + + /** + * {@inheritdoc} + */ + protected function describeInputArgument(InputArgument $argument, array $options = array()) + { + $this->writeDocument($this->getInputArgumentDocument($argument)); + } + + /** + * {@inheritdoc} + */ + protected function describeInputOption(InputOption $option, array $options = array()) + { + $this->writeDocument($this->getInputOptionDocument($option)); + } + + /** + * {@inheritdoc} + */ + protected function describeInputDefinition(InputDefinition $definition, array $options = array()) + { + $this->writeDocument($this->getInputDefinitionDocument($definition)); + } + + /** + * {@inheritdoc} + */ + protected function describeCommand(Command $command, array $options = array()) + { + $this->writeDocument($this->getCommandDocument($command)); + } + + /** + * {@inheritdoc} + */ + protected function describeApplication(Application $application, array $options = array()) + { + $this->writeDocument($this->getApplicationDocument($application, isset($options['namespace']) ? $options['namespace'] : null)); + } + + /** + * Appends document children to parent node. + * + * @param \DOMNode $parentNode + * @param \DOMNode $importedParent + */ + private function appendDocument(\DOMNode $parentNode, \DOMNode $importedParent) + { + foreach ($importedParent->childNodes as $childNode) { + $parentNode->appendChild($parentNode->ownerDocument->importNode($childNode, true)); + } + } + + /** + * Writes DOM document. + * + * @param \DOMDocument $dom + * + * @return \DOMDocument|string + */ + private function writeDocument(\DOMDocument $dom) + { + $dom->formatOutput = true; + $this->write($dom->saveXML()); + } + + /** + * @param InputArgument $argument + * + * @return \DOMDocument + */ + private function getInputArgumentDocument(InputArgument $argument) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + + $dom->appendChild($objectXML = $dom->createElement('argument')); + $objectXML->setAttribute('name', $argument->getName()); + $objectXML->setAttribute('is_required', $argument->isRequired() ? 1 : 0); + $objectXML->setAttribute('is_array', $argument->isArray() ? 1 : 0); + $objectXML->appendChild($descriptionXML = $dom->createElement('description')); + $descriptionXML->appendChild($dom->createTextNode($argument->getDescription())); + + $objectXML->appendChild($defaultsXML = $dom->createElement('defaults')); + $defaults = is_array($argument->getDefault()) ? $argument->getDefault() : (is_bool($argument->getDefault()) ? array(var_export($argument->getDefault(), true)) : ($argument->getDefault() ? array($argument->getDefault()) : array())); + foreach ($defaults as $default) { + $defaultsXML->appendChild($defaultXML = $dom->createElement('default')); + $defaultXML->appendChild($dom->createTextNode($default)); + } + + return $dom; + } + + /** + * @param InputOption $option + * + * @return \DOMDocument + */ + private function getInputOptionDocument(InputOption $option) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + + $dom->appendChild($objectXML = $dom->createElement('option')); + $objectXML->setAttribute('name', '--'.$option->getName()); + $pos = strpos($option->getShortcut(), '|'); + if (false !== $pos) { + $objectXML->setAttribute('shortcut', '-'.substr($option->getShortcut(), 0, $pos)); + $objectXML->setAttribute('shortcuts', '-'.implode('|-', explode('|', $option->getShortcut()))); + } else { + $objectXML->setAttribute('shortcut', $option->getShortcut() ? '-'.$option->getShortcut() : ''); + } + $objectXML->setAttribute('accept_value', $option->acceptValue() ? 1 : 0); + $objectXML->setAttribute('is_value_required', $option->isValueRequired() ? 1 : 0); + $objectXML->setAttribute('is_multiple', $option->isArray() ? 1 : 0); + $objectXML->appendChild($descriptionXML = $dom->createElement('description')); + $descriptionXML->appendChild($dom->createTextNode($option->getDescription())); + + if ($option->acceptValue()) { + $defaults = is_array($option->getDefault()) ? $option->getDefault() : (is_bool($option->getDefault()) ? array(var_export($option->getDefault(), true)) : ($option->getDefault() ? array($option->getDefault()) : array())); + $objectXML->appendChild($defaultsXML = $dom->createElement('defaults')); + + if (!empty($defaults)) { + foreach ($defaults as $default) { + $defaultsXML->appendChild($defaultXML = $dom->createElement('default')); + $defaultXML->appendChild($dom->createTextNode($default)); + } + } + } + + return $dom; + } +} diff --git a/vendor/symfony/console/Event/ConsoleCommandEvent.php b/vendor/symfony/console/Event/ConsoleCommandEvent.php new file mode 100644 index 0000000000..92adf1ef96 --- /dev/null +++ b/vendor/symfony/console/Event/ConsoleCommandEvent.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Event; + +/** + * Allows to do things before the command is executed, like skipping the command or changing the input. + * + * @author Fabien Potencier + */ +class ConsoleCommandEvent extends ConsoleEvent +{ + /** + * The return code for skipped commands, this will also be passed into the terminate event. + */ + const RETURN_CODE_DISABLED = 113; + + /** + * Indicates if the command should be run or skipped. + * + * @var bool + */ + private $commandShouldRun = true; + + /** + * Disables the command, so it won't be run. + * + * @return bool + */ + public function disableCommand() + { + return $this->commandShouldRun = false; + } + + /** + * Enables the command. + * + * @return bool + */ + public function enableCommand() + { + return $this->commandShouldRun = true; + } + + /** + * Returns true if the command is runnable, false otherwise. + * + * @return bool + */ + public function commandShouldRun() + { + return $this->commandShouldRun; + } +} diff --git a/vendor/symfony/console/Event/ConsoleEvent.php b/vendor/symfony/console/Event/ConsoleEvent.php new file mode 100644 index 0000000000..ab620c4609 --- /dev/null +++ b/vendor/symfony/console/Event/ConsoleEvent.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Event; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\EventDispatcher\Event; + +/** + * Allows to inspect input and output of a command. + * + * @author Francesco Levorato + */ +class ConsoleEvent extends Event +{ + protected $command; + + private $input; + private $output; + + public function __construct(Command $command, InputInterface $input, OutputInterface $output) + { + $this->command = $command; + $this->input = $input; + $this->output = $output; + } + + /** + * Gets the command that is executed. + * + * @return Command A Command instance + */ + public function getCommand() + { + return $this->command; + } + + /** + * Gets the input instance. + * + * @return InputInterface An InputInterface instance + */ + public function getInput() + { + return $this->input; + } + + /** + * Gets the output instance. + * + * @return OutputInterface An OutputInterface instance + */ + public function getOutput() + { + return $this->output; + } +} diff --git a/vendor/symfony/console/Event/ConsoleExceptionEvent.php b/vendor/symfony/console/Event/ConsoleExceptionEvent.php new file mode 100644 index 0000000000..603b7eed78 --- /dev/null +++ b/vendor/symfony/console/Event/ConsoleExceptionEvent.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Event; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Allows to handle exception thrown in a command. + * + * @author Fabien Potencier + */ +class ConsoleExceptionEvent extends ConsoleEvent +{ + private $exception; + private $exitCode; + + public function __construct(Command $command, InputInterface $input, OutputInterface $output, \Exception $exception, $exitCode) + { + parent::__construct($command, $input, $output); + + $this->setException($exception); + $this->exitCode = (int) $exitCode; + } + + /** + * Returns the thrown exception. + * + * @return \Exception The thrown exception + */ + public function getException() + { + return $this->exception; + } + + /** + * Replaces the thrown exception. + * + * This exception will be thrown if no response is set in the event. + * + * @param \Exception $exception The thrown exception + */ + public function setException(\Exception $exception) + { + $this->exception = $exception; + } + + /** + * Gets the exit code. + * + * @return int The command exit code + */ + public function getExitCode() + { + return $this->exitCode; + } +} diff --git a/vendor/symfony/console/Event/ConsoleTerminateEvent.php b/vendor/symfony/console/Event/ConsoleTerminateEvent.php new file mode 100644 index 0000000000..b6a5d7c0dc --- /dev/null +++ b/vendor/symfony/console/Event/ConsoleTerminateEvent.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Event; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Allows to manipulate the exit code of a command after its execution. + * + * @author Francesco Levorato + */ +class ConsoleTerminateEvent extends ConsoleEvent +{ + /** + * The exit code of the command. + * + * @var int + */ + private $exitCode; + + public function __construct(Command $command, InputInterface $input, OutputInterface $output, $exitCode) + { + parent::__construct($command, $input, $output); + + $this->setExitCode($exitCode); + } + + /** + * Sets the exit code. + * + * @param int $exitCode The command exit code + */ + public function setExitCode($exitCode) + { + $this->exitCode = (int) $exitCode; + } + + /** + * Gets the exit code. + * + * @return int The command exit code + */ + public function getExitCode() + { + return $this->exitCode; + } +} diff --git a/vendor/symfony/console/Exception/CommandNotFoundException.php b/vendor/symfony/console/Exception/CommandNotFoundException.php new file mode 100644 index 0000000000..ce6fefe34a --- /dev/null +++ b/vendor/symfony/console/Exception/CommandNotFoundException.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * Represents an incorrect command name typed in the console. + * + * @author Jérôme Tamarelle + */ +class CommandNotFoundException extends \InvalidArgumentException implements ExceptionInterface +{ + private $alternatives; + + /** + * @param string $message Exception message to throw. + * @param array $alternatives List of similar defined names. + * @param int $code Exception code. + * @param Exception $previous previous exception used for the exception chaining. + */ + public function __construct($message, array $alternatives = array(), $code = 0, \Exception $previous = null) + { + parent::__construct($message, $code, $previous); + + $this->alternatives = $alternatives; + } + + /** + * @return array A list of similar defined names. + */ + public function getAlternatives() + { + return $this->alternatives; + } +} diff --git a/vendor/symfony/console/Exception/ExceptionInterface.php b/vendor/symfony/console/Exception/ExceptionInterface.php new file mode 100644 index 0000000000..491cc4c645 --- /dev/null +++ b/vendor/symfony/console/Exception/ExceptionInterface.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * ExceptionInterface. + * + * @author Jérôme Tamarelle + */ +interface ExceptionInterface +{ +} diff --git a/vendor/symfony/console/Exception/InvalidArgumentException.php b/vendor/symfony/console/Exception/InvalidArgumentException.php new file mode 100644 index 0000000000..07cc0b61d6 --- /dev/null +++ b/vendor/symfony/console/Exception/InvalidArgumentException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * @author Jérôme Tamarelle + */ +class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/console/Exception/InvalidOptionException.php b/vendor/symfony/console/Exception/InvalidOptionException.php new file mode 100644 index 0000000000..b2eec61658 --- /dev/null +++ b/vendor/symfony/console/Exception/InvalidOptionException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * Represents an incorrect option name typed in the console. + * + * @author Jérôme Tamarelle + */ +class InvalidOptionException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/console/Exception/LogicException.php b/vendor/symfony/console/Exception/LogicException.php new file mode 100644 index 0000000000..fc37b8d8ae --- /dev/null +++ b/vendor/symfony/console/Exception/LogicException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * @author Jérôme Tamarelle + */ +class LogicException extends \LogicException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/console/Exception/RuntimeException.php b/vendor/symfony/console/Exception/RuntimeException.php new file mode 100644 index 0000000000..51d7d80ac6 --- /dev/null +++ b/vendor/symfony/console/Exception/RuntimeException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * @author Jérôme Tamarelle + */ +class RuntimeException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/console/Formatter/OutputFormatter.php b/vendor/symfony/console/Formatter/OutputFormatter.php new file mode 100644 index 0000000000..56cd5e568f --- /dev/null +++ b/vendor/symfony/console/Formatter/OutputFormatter.php @@ -0,0 +1,240 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * Formatter class for console output. + * + * @author Konstantin Kudryashov + */ +class OutputFormatter implements OutputFormatterInterface +{ + private $decorated; + private $styles = array(); + private $styleStack; + + /** + * Escapes "<" special char in given text. + * + * @param string $text Text to escape + * + * @return string Escaped text + */ + public static function escape($text) + { + $text = preg_replace('/([^\\\\]?) FormatterStyle" instances + */ + public function __construct($decorated = false, array $styles = array()) + { + $this->decorated = (bool) $decorated; + + $this->setStyle('error', new OutputFormatterStyle('white', 'red')); + $this->setStyle('info', new OutputFormatterStyle('green')); + $this->setStyle('comment', new OutputFormatterStyle('yellow')); + $this->setStyle('question', new OutputFormatterStyle('black', 'cyan')); + + foreach ($styles as $name => $style) { + $this->setStyle($name, $style); + } + + $this->styleStack = new OutputFormatterStyleStack(); + } + + /** + * Sets the decorated flag. + * + * @param bool $decorated Whether to decorate the messages or not + */ + public function setDecorated($decorated) + { + $this->decorated = (bool) $decorated; + } + + /** + * Gets the decorated flag. + * + * @return bool true if the output will decorate messages, false otherwise + */ + public function isDecorated() + { + return $this->decorated; + } + + /** + * Sets a new style. + * + * @param string $name The style name + * @param OutputFormatterStyleInterface $style The style instance + */ + public function setStyle($name, OutputFormatterStyleInterface $style) + { + $this->styles[strtolower($name)] = $style; + } + + /** + * Checks if output formatter has style with specified name. + * + * @param string $name + * + * @return bool + */ + public function hasStyle($name) + { + return isset($this->styles[strtolower($name)]); + } + + /** + * Gets style options from style with specified name. + * + * @param string $name + * + * @return OutputFormatterStyleInterface + * + * @throws InvalidArgumentException When style isn't defined + */ + public function getStyle($name) + { + if (!$this->hasStyle($name)) { + throw new InvalidArgumentException(sprintf('Undefined style: %s', $name)); + } + + return $this->styles[strtolower($name)]; + } + + /** + * Formats a message according to the given styles. + * + * @param string $message The message to style + * + * @return string The styled message + */ + public function format($message) + { + $message = (string) $message; + $offset = 0; + $output = ''; + $tagRegex = '[a-z][a-z0-9_=;-]*+'; + preg_match_all("#<(($tagRegex) | /($tagRegex)?)>#ix", $message, $matches, PREG_OFFSET_CAPTURE); + foreach ($matches[0] as $i => $match) { + $pos = $match[1]; + $text = $match[0]; + + if (0 != $pos && '\\' == $message[$pos - 1]) { + continue; + } + + // add the text up to the next tag + $output .= $this->applyCurrentStyle(substr($message, $offset, $pos - $offset)); + $offset = $pos + strlen($text); + + // opening tag? + if ($open = '/' != $text[1]) { + $tag = $matches[1][$i][0]; + } else { + $tag = isset($matches[3][$i][0]) ? $matches[3][$i][0] : ''; + } + + if (!$open && !$tag) { + // + $this->styleStack->pop(); + } elseif (false === $style = $this->createStyleFromString(strtolower($tag))) { + $output .= $this->applyCurrentStyle($text); + } elseif ($open) { + $this->styleStack->push($style); + } else { + $this->styleStack->pop($style); + } + } + + $output .= $this->applyCurrentStyle(substr($message, $offset)); + + if (false !== strpos($output, '<<')) { + return strtr($output, array('\\<' => '<', '<<' => '\\')); + } + + return str_replace('\\<', '<', $output); + } + + /** + * @return OutputFormatterStyleStack + */ + public function getStyleStack() + { + return $this->styleStack; + } + + /** + * Tries to create new style instance from string. + * + * @param string $string + * + * @return OutputFormatterStyle|bool false if string is not format string + */ + private function createStyleFromString($string) + { + if (isset($this->styles[$string])) { + return $this->styles[$string]; + } + + if (!preg_match_all('/([^=]+)=([^;]+)(;|$)/', strtolower($string), $matches, PREG_SET_ORDER)) { + return false; + } + + $style = new OutputFormatterStyle(); + foreach ($matches as $match) { + array_shift($match); + + if ('fg' == $match[0]) { + $style->setForeground($match[1]); + } elseif ('bg' == $match[0]) { + $style->setBackground($match[1]); + } else { + try { + $style->setOption($match[1]); + } catch (\InvalidArgumentException $e) { + return false; + } + } + } + + return $style; + } + + /** + * Applies current style from stack to text, if must be applied. + * + * @param string $text Input text + * + * @return string Styled text + */ + private function applyCurrentStyle($text) + { + return $this->isDecorated() && strlen($text) > 0 ? $this->styleStack->getCurrent()->apply($text) : $text; + } +} diff --git a/vendor/symfony/console/Formatter/OutputFormatterInterface.php b/vendor/symfony/console/Formatter/OutputFormatterInterface.php new file mode 100644 index 0000000000..5a52ba096b --- /dev/null +++ b/vendor/symfony/console/Formatter/OutputFormatterInterface.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +/** + * Formatter interface for console output. + * + * @author Konstantin Kudryashov + */ +interface OutputFormatterInterface +{ + /** + * Sets the decorated flag. + * + * @param bool $decorated Whether to decorate the messages or not + */ + public function setDecorated($decorated); + + /** + * Gets the decorated flag. + * + * @return bool true if the output will decorate messages, false otherwise + */ + public function isDecorated(); + + /** + * Sets a new style. + * + * @param string $name The style name + * @param OutputFormatterStyleInterface $style The style instance + */ + public function setStyle($name, OutputFormatterStyleInterface $style); + + /** + * Checks if output formatter has style with specified name. + * + * @param string $name + * + * @return bool + */ + public function hasStyle($name); + + /** + * Gets style options from style with specified name. + * + * @param string $name + * + * @return OutputFormatterStyleInterface + */ + public function getStyle($name); + + /** + * Formats a message according to the given styles. + * + * @param string $message The message to style + * + * @return string The styled message + */ + public function format($message); +} diff --git a/vendor/symfony/console/Formatter/OutputFormatterStyle.php b/vendor/symfony/console/Formatter/OutputFormatterStyle.php new file mode 100644 index 0000000000..c7c6b4a019 --- /dev/null +++ b/vendor/symfony/console/Formatter/OutputFormatterStyle.php @@ -0,0 +1,221 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * Formatter style class for defining styles. + * + * @author Konstantin Kudryashov + */ +class OutputFormatterStyle implements OutputFormatterStyleInterface +{ + private static $availableForegroundColors = array( + 'black' => array('set' => 30, 'unset' => 39), + 'red' => array('set' => 31, 'unset' => 39), + 'green' => array('set' => 32, 'unset' => 39), + 'yellow' => array('set' => 33, 'unset' => 39), + 'blue' => array('set' => 34, 'unset' => 39), + 'magenta' => array('set' => 35, 'unset' => 39), + 'cyan' => array('set' => 36, 'unset' => 39), + 'white' => array('set' => 37, 'unset' => 39), + 'default' => array('set' => 39, 'unset' => 39), + ); + private static $availableBackgroundColors = array( + 'black' => array('set' => 40, 'unset' => 49), + 'red' => array('set' => 41, 'unset' => 49), + 'green' => array('set' => 42, 'unset' => 49), + 'yellow' => array('set' => 43, 'unset' => 49), + 'blue' => array('set' => 44, 'unset' => 49), + 'magenta' => array('set' => 45, 'unset' => 49), + 'cyan' => array('set' => 46, 'unset' => 49), + 'white' => array('set' => 47, 'unset' => 49), + 'default' => array('set' => 49, 'unset' => 49), + ); + private static $availableOptions = array( + 'bold' => array('set' => 1, 'unset' => 22), + 'underscore' => array('set' => 4, 'unset' => 24), + 'blink' => array('set' => 5, 'unset' => 25), + 'reverse' => array('set' => 7, 'unset' => 27), + 'conceal' => array('set' => 8, 'unset' => 28), + ); + + private $foreground; + private $background; + private $options = array(); + + /** + * Initializes output formatter style. + * + * @param string|null $foreground The style foreground color name + * @param string|null $background The style background color name + * @param array $options The style options + */ + public function __construct($foreground = null, $background = null, array $options = array()) + { + if (null !== $foreground) { + $this->setForeground($foreground); + } + if (null !== $background) { + $this->setBackground($background); + } + if (count($options)) { + $this->setOptions($options); + } + } + + /** + * Sets style foreground color. + * + * @param string|null $color The color name + * + * @throws InvalidArgumentException When the color name isn't defined + */ + public function setForeground($color = null) + { + if (null === $color) { + $this->foreground = null; + + return; + } + + if (!isset(static::$availableForegroundColors[$color])) { + throw new InvalidArgumentException(sprintf( + 'Invalid foreground color specified: "%s". Expected one of (%s)', + $color, + implode(', ', array_keys(static::$availableForegroundColors)) + )); + } + + $this->foreground = static::$availableForegroundColors[$color]; + } + + /** + * Sets style background color. + * + * @param string|null $color The color name + * + * @throws InvalidArgumentException When the color name isn't defined + */ + public function setBackground($color = null) + { + if (null === $color) { + $this->background = null; + + return; + } + + if (!isset(static::$availableBackgroundColors[$color])) { + throw new InvalidArgumentException(sprintf( + 'Invalid background color specified: "%s". Expected one of (%s)', + $color, + implode(', ', array_keys(static::$availableBackgroundColors)) + )); + } + + $this->background = static::$availableBackgroundColors[$color]; + } + + /** + * Sets some specific style option. + * + * @param string $option The option name + * + * @throws InvalidArgumentException When the option name isn't defined + */ + public function setOption($option) + { + if (!isset(static::$availableOptions[$option])) { + throw new InvalidArgumentException(sprintf( + 'Invalid option specified: "%s". Expected one of (%s)', + $option, + implode(', ', array_keys(static::$availableOptions)) + )); + } + + if (!in_array(static::$availableOptions[$option], $this->options)) { + $this->options[] = static::$availableOptions[$option]; + } + } + + /** + * Unsets some specific style option. + * + * @param string $option The option name + * + * @throws InvalidArgumentException When the option name isn't defined + */ + public function unsetOption($option) + { + if (!isset(static::$availableOptions[$option])) { + throw new InvalidArgumentException(sprintf( + 'Invalid option specified: "%s". Expected one of (%s)', + $option, + implode(', ', array_keys(static::$availableOptions)) + )); + } + + $pos = array_search(static::$availableOptions[$option], $this->options); + if (false !== $pos) { + unset($this->options[$pos]); + } + } + + /** + * Sets multiple style options at once. + * + * @param array $options + */ + public function setOptions(array $options) + { + $this->options = array(); + + foreach ($options as $option) { + $this->setOption($option); + } + } + + /** + * Applies the style to a given text. + * + * @param string $text The text to style + * + * @return string + */ + public function apply($text) + { + $setCodes = array(); + $unsetCodes = array(); + + if (null !== $this->foreground) { + $setCodes[] = $this->foreground['set']; + $unsetCodes[] = $this->foreground['unset']; + } + if (null !== $this->background) { + $setCodes[] = $this->background['set']; + $unsetCodes[] = $this->background['unset']; + } + if (count($this->options)) { + foreach ($this->options as $option) { + $setCodes[] = $option['set']; + $unsetCodes[] = $option['unset']; + } + } + + if (0 === count($setCodes)) { + return $text; + } + + return sprintf("\033[%sm%s\033[%sm", implode(';', $setCodes), $text, implode(';', $unsetCodes)); + } +} diff --git a/vendor/symfony/console/Formatter/OutputFormatterStyleInterface.php b/vendor/symfony/console/Formatter/OutputFormatterStyleInterface.php new file mode 100644 index 0000000000..c36fda8070 --- /dev/null +++ b/vendor/symfony/console/Formatter/OutputFormatterStyleInterface.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +/** + * Formatter style interface for defining styles. + * + * @author Konstantin Kudryashov + */ +interface OutputFormatterStyleInterface +{ + /** + * Sets style foreground color. + * + * @param string $color The color name + */ + public function setForeground($color = null); + + /** + * Sets style background color. + * + * @param string $color The color name + */ + public function setBackground($color = null); + + /** + * Sets some specific style option. + * + * @param string $option The option name + */ + public function setOption($option); + + /** + * Unsets some specific style option. + * + * @param string $option The option name + */ + public function unsetOption($option); + + /** + * Sets multiple style options at once. + * + * @param array $options + */ + public function setOptions(array $options); + + /** + * Applies the style to a given text. + * + * @param string $text The text to style + * + * @return string + */ + public function apply($text); +} diff --git a/vendor/symfony/console/Formatter/OutputFormatterStyleStack.php b/vendor/symfony/console/Formatter/OutputFormatterStyleStack.php new file mode 100644 index 0000000000..e5d14ea3fb --- /dev/null +++ b/vendor/symfony/console/Formatter/OutputFormatterStyleStack.php @@ -0,0 +1,123 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * @author Jean-François Simon + */ +class OutputFormatterStyleStack +{ + /** + * @var OutputFormatterStyleInterface[] + */ + private $styles; + + /** + * @var OutputFormatterStyleInterface + */ + private $emptyStyle; + + /** + * Constructor. + * + * @param OutputFormatterStyleInterface|null $emptyStyle + */ + public function __construct(OutputFormatterStyleInterface $emptyStyle = null) + { + $this->emptyStyle = $emptyStyle ?: new OutputFormatterStyle(); + $this->reset(); + } + + /** + * Resets stack (ie. empty internal arrays). + */ + public function reset() + { + $this->styles = array(); + } + + /** + * Pushes a style in the stack. + * + * @param OutputFormatterStyleInterface $style + */ + public function push(OutputFormatterStyleInterface $style) + { + $this->styles[] = $style; + } + + /** + * Pops a style from the stack. + * + * @param OutputFormatterStyleInterface|null $style + * + * @return OutputFormatterStyleInterface + * + * @throws InvalidArgumentException When style tags incorrectly nested + */ + public function pop(OutputFormatterStyleInterface $style = null) + { + if (empty($this->styles)) { + return $this->emptyStyle; + } + + if (null === $style) { + return array_pop($this->styles); + } + + foreach (array_reverse($this->styles, true) as $index => $stackedStyle) { + if ($style->apply('') === $stackedStyle->apply('')) { + $this->styles = array_slice($this->styles, 0, $index); + + return $stackedStyle; + } + } + + throw new InvalidArgumentException('Incorrectly nested style tag found.'); + } + + /** + * Computes current style with stacks top codes. + * + * @return OutputFormatterStyle + */ + public function getCurrent() + { + if (empty($this->styles)) { + return $this->emptyStyle; + } + + return $this->styles[count($this->styles) - 1]; + } + + /** + * @param OutputFormatterStyleInterface $emptyStyle + * + * @return OutputFormatterStyleStack + */ + public function setEmptyStyle(OutputFormatterStyleInterface $emptyStyle) + { + $this->emptyStyle = $emptyStyle; + + return $this; + } + + /** + * @return OutputFormatterStyleInterface + */ + public function getEmptyStyle() + { + return $this->emptyStyle; + } +} diff --git a/vendor/symfony/console/Helper/DebugFormatterHelper.php b/vendor/symfony/console/Helper/DebugFormatterHelper.php new file mode 100644 index 0000000000..1119b795c8 --- /dev/null +++ b/vendor/symfony/console/Helper/DebugFormatterHelper.php @@ -0,0 +1,127 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +/** + * Helps outputting debug information when running an external program from a command. + * + * An external program can be a Process, an HTTP request, or anything else. + * + * @author Fabien Potencier + */ +class DebugFormatterHelper extends Helper +{ + private $colors = array('black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white', 'default'); + private $started = array(); + private $count = -1; + + /** + * Starts a debug formatting session. + * + * @param string $id The id of the formatting session + * @param string $message The message to display + * @param string $prefix The prefix to use + * + * @return string + */ + public function start($id, $message, $prefix = 'RUN') + { + $this->started[$id] = array('border' => ++$this->count % count($this->colors)); + + return sprintf("%s %s %s\n", $this->getBorder($id), $prefix, $message); + } + + /** + * Adds progress to a formatting session. + * + * @param string $id The id of the formatting session + * @param string $buffer The message to display + * @param bool $error Whether to consider the buffer as error + * @param string $prefix The prefix for output + * @param string $errorPrefix The prefix for error output + * + * @return string + */ + public function progress($id, $buffer, $error = false, $prefix = 'OUT', $errorPrefix = 'ERR') + { + $message = ''; + + if ($error) { + if (isset($this->started[$id]['out'])) { + $message .= "\n"; + unset($this->started[$id]['out']); + } + if (!isset($this->started[$id]['err'])) { + $message .= sprintf('%s %s ', $this->getBorder($id), $errorPrefix); + $this->started[$id]['err'] = true; + } + + $message .= str_replace("\n", sprintf("\n%s %s ", $this->getBorder($id), $errorPrefix), $buffer); + } else { + if (isset($this->started[$id]['err'])) { + $message .= "\n"; + unset($this->started[$id]['err']); + } + if (!isset($this->started[$id]['out'])) { + $message .= sprintf('%s %s ', $this->getBorder($id), $prefix); + $this->started[$id]['out'] = true; + } + + $message .= str_replace("\n", sprintf("\n%s %s ", $this->getBorder($id), $prefix), $buffer); + } + + return $message; + } + + /** + * Stops a formatting session. + * + * @param string $id The id of the formatting session + * @param string $message The message to display + * @param bool $successful Whether to consider the result as success + * @param string $prefix The prefix for the end output + * + * @return string + */ + public function stop($id, $message, $successful, $prefix = 'RES') + { + $trailingEOL = isset($this->started[$id]['out']) || isset($this->started[$id]['err']) ? "\n" : ''; + + if ($successful) { + return sprintf("%s%s %s %s\n", $trailingEOL, $this->getBorder($id), $prefix, $message); + } + + $message = sprintf("%s%s %s %s\n", $trailingEOL, $this->getBorder($id), $prefix, $message); + + unset($this->started[$id]['out'], $this->started[$id]['err']); + + return $message; + } + + /** + * @param string $id The id of the formatting session + * + * @return string + */ + private function getBorder($id) + { + return sprintf(' ', $this->colors[$this->started[$id]['border']]); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'debug_formatter'; + } +} diff --git a/vendor/symfony/console/Helper/DescriptorHelper.php b/vendor/symfony/console/Helper/DescriptorHelper.php new file mode 100644 index 0000000000..a53b476b17 --- /dev/null +++ b/vendor/symfony/console/Helper/DescriptorHelper.php @@ -0,0 +1,97 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Descriptor\DescriptorInterface; +use Symfony\Component\Console\Descriptor\JsonDescriptor; +use Symfony\Component\Console\Descriptor\MarkdownDescriptor; +use Symfony\Component\Console\Descriptor\TextDescriptor; +use Symfony\Component\Console\Descriptor\XmlDescriptor; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * This class adds helper method to describe objects in various formats. + * + * @author Jean-François Simon + */ +class DescriptorHelper extends Helper +{ + /** + * @var DescriptorInterface[] + */ + private $descriptors = array(); + + /** + * Constructor. + */ + public function __construct() + { + $this + ->register('txt', new TextDescriptor()) + ->register('xml', new XmlDescriptor()) + ->register('json', new JsonDescriptor()) + ->register('md', new MarkdownDescriptor()) + ; + } + + /** + * Describes an object if supported. + * + * Available options are: + * * format: string, the output format name + * * raw_text: boolean, sets output type as raw + * + * @param OutputInterface $output + * @param object $object + * @param array $options + * + * @throws InvalidArgumentException when the given format is not supported + */ + public function describe(OutputInterface $output, $object, array $options = array()) + { + $options = array_merge(array( + 'raw_text' => false, + 'format' => 'txt', + ), $options); + + if (!isset($this->descriptors[$options['format']])) { + throw new InvalidArgumentException(sprintf('Unsupported format "%s".', $options['format'])); + } + + $descriptor = $this->descriptors[$options['format']]; + $descriptor->describe($output, $object, $options); + } + + /** + * Registers a descriptor. + * + * @param string $format + * @param DescriptorInterface $descriptor + * + * @return DescriptorHelper + */ + public function register($format, DescriptorInterface $descriptor) + { + $this->descriptors[$format] = $descriptor; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'descriptor'; + } +} diff --git a/vendor/symfony/console/Helper/DialogHelper.php b/vendor/symfony/console/Helper/DialogHelper.php new file mode 100644 index 0000000000..9ce9f66168 --- /dev/null +++ b/vendor/symfony/console/Helper/DialogHelper.php @@ -0,0 +1,502 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Formatter\OutputFormatterStyle; + +/** + * The Dialog class provides helpers to interact with the user. + * + * @author Fabien Potencier + * + * @deprecated since version 2.5, to be removed in 3.0. + * Use {@link \Symfony\Component\Console\Helper\QuestionHelper} instead. + */ +class DialogHelper extends InputAwareHelper +{ + private $inputStream; + private static $shell; + private static $stty; + + public function __construct($triggerDeprecationError = true) + { + if ($triggerDeprecationError) { + @trigger_error('"Symfony\Component\Console\Helper\DialogHelper" is deprecated since version 2.5 and will be removed in 3.0. Use "Symfony\Component\Console\Helper\QuestionHelper" instead.', E_USER_DEPRECATED); + } + } + + /** + * Asks the user to select a value. + * + * @param OutputInterface $output An Output instance + * @param string|array $question The question to ask + * @param array $choices List of choices to pick from + * @param bool|string $default The default answer if the user enters nothing + * @param bool|int $attempts Max number of times to ask before giving up (false by default, which means infinite) + * @param string $errorMessage Message which will be shown if invalid value from choice list would be picked + * @param bool $multiselect Select more than one value separated by comma + * + * @return int|string|array The selected value or values (the key of the choices array) + * + * @throws InvalidArgumentException + */ + public function select(OutputInterface $output, $question, $choices, $default = null, $attempts = false, $errorMessage = 'Value "%s" is invalid', $multiselect = false) + { + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + + $width = max(array_map('strlen', array_keys($choices))); + + $messages = (array) $question; + foreach ($choices as $key => $value) { + $messages[] = sprintf(" [%-{$width}s] %s", $key, $value); + } + + $output->writeln($messages); + + $result = $this->askAndValidate($output, '> ', function ($picked) use ($choices, $errorMessage, $multiselect) { + // Collapse all spaces. + $selectedChoices = str_replace(' ', '', $picked); + + if ($multiselect) { + // Check for a separated comma values + if (!preg_match('/^[a-zA-Z0-9_-]+(?:,[a-zA-Z0-9_-]+)*$/', $selectedChoices, $matches)) { + throw new InvalidArgumentException(sprintf($errorMessage, $picked)); + } + $selectedChoices = explode(',', $selectedChoices); + } else { + $selectedChoices = array($picked); + } + + $multiselectChoices = array(); + + foreach ($selectedChoices as $value) { + if (empty($choices[$value])) { + throw new InvalidArgumentException(sprintf($errorMessage, $value)); + } + $multiselectChoices[] = $value; + } + + if ($multiselect) { + return $multiselectChoices; + } + + return $picked; + }, $attempts, $default); + + return $result; + } + + /** + * Asks a question to the user. + * + * @param OutputInterface $output An Output instance + * @param string|array $question The question to ask + * @param string $default The default answer if none is given by the user + * @param array $autocomplete List of values to autocomplete + * + * @return string The user answer + * + * @throws RuntimeException If there is no data to read in the input stream + */ + public function ask(OutputInterface $output, $question, $default = null, array $autocomplete = null) + { + if ($this->input && !$this->input->isInteractive()) { + return $default; + } + + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + + $output->write($question); + + $inputStream = $this->inputStream ?: STDIN; + + if (null === $autocomplete || !$this->hasSttyAvailable()) { + $ret = fgets($inputStream, 4096); + if (false === $ret) { + throw new RuntimeException('Aborted'); + } + $ret = trim($ret); + } else { + $ret = ''; + + $i = 0; + $ofs = -1; + $matches = $autocomplete; + $numMatches = count($matches); + + $sttyMode = shell_exec('stty -g'); + + // Disable icanon (so we can fread each keypress) and echo (we'll do echoing here instead) + shell_exec('stty -icanon -echo'); + + // Add highlighted text style + $output->getFormatter()->setStyle('hl', new OutputFormatterStyle('black', 'white')); + + // Read a keypress + while (!feof($inputStream)) { + $c = fread($inputStream, 1); + + // Backspace Character + if ("\177" === $c) { + if (0 === $numMatches && 0 !== $i) { + --$i; + // Move cursor backwards + $output->write("\033[1D"); + } + + if ($i === 0) { + $ofs = -1; + $matches = $autocomplete; + $numMatches = count($matches); + } else { + $numMatches = 0; + } + + // Pop the last character off the end of our string + $ret = substr($ret, 0, $i); + } elseif ("\033" === $c) { + // Did we read an escape sequence? + $c .= fread($inputStream, 2); + + // A = Up Arrow. B = Down Arrow + if (isset($c[2]) && ('A' === $c[2] || 'B' === $c[2])) { + if ('A' === $c[2] && -1 === $ofs) { + $ofs = 0; + } + + if (0 === $numMatches) { + continue; + } + + $ofs += ('A' === $c[2]) ? -1 : 1; + $ofs = ($numMatches + $ofs) % $numMatches; + } + } elseif (ord($c) < 32) { + if ("\t" === $c || "\n" === $c) { + if ($numMatches > 0 && -1 !== $ofs) { + $ret = $matches[$ofs]; + // Echo out remaining chars for current match + $output->write(substr($ret, $i)); + $i = strlen($ret); + } + + if ("\n" === $c) { + $output->write($c); + break; + } + + $numMatches = 0; + } + + continue; + } else { + $output->write($c); + $ret .= $c; + ++$i; + + $numMatches = 0; + $ofs = 0; + + foreach ($autocomplete as $value) { + // If typed characters match the beginning chunk of value (e.g. [AcmeDe]moBundle) + if (0 === strpos($value, $ret) && $i !== strlen($value)) { + $matches[$numMatches++] = $value; + } + } + } + + // Erase characters from cursor to end of line + $output->write("\033[K"); + + if ($numMatches > 0 && -1 !== $ofs) { + // Save cursor position + $output->write("\0337"); + // Write highlighted text + $output->write(''.substr($matches[$ofs], $i).''); + // Restore cursor position + $output->write("\0338"); + } + } + + // Reset stty so it behaves normally again + shell_exec(sprintf('stty %s', $sttyMode)); + } + + return strlen($ret) > 0 ? $ret : $default; + } + + /** + * Asks a confirmation to the user. + * + * The question will be asked until the user answers by nothing, yes, or no. + * + * @param OutputInterface $output An Output instance + * @param string|array $question The question to ask + * @param bool $default The default answer if the user enters nothing + * + * @return bool true if the user has confirmed, false otherwise + */ + public function askConfirmation(OutputInterface $output, $question, $default = true) + { + $answer = 'z'; + while ($answer && !in_array(strtolower($answer[0]), array('y', 'n'))) { + $answer = $this->ask($output, $question); + } + + if (false === $default) { + return $answer && 'y' == strtolower($answer[0]); + } + + return !$answer || 'y' == strtolower($answer[0]); + } + + /** + * Asks a question to the user, the response is hidden. + * + * @param OutputInterface $output An Output instance + * @param string|array $question The question + * @param bool $fallback In case the response can not be hidden, whether to fallback on non-hidden question or not + * + * @return string The answer + * + * @throws RuntimeException In case the fallback is deactivated and the response can not be hidden + */ + public function askHiddenResponse(OutputInterface $output, $question, $fallback = true) + { + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + + if ('\\' === DIRECTORY_SEPARATOR) { + $exe = __DIR__.'/../Resources/bin/hiddeninput.exe'; + + // handle code running from a phar + if ('phar:' === substr(__FILE__, 0, 5)) { + $tmpExe = sys_get_temp_dir().'/hiddeninput.exe'; + copy($exe, $tmpExe); + $exe = $tmpExe; + } + + $output->write($question); + $value = rtrim(shell_exec($exe)); + $output->writeln(''); + + if (isset($tmpExe)) { + unlink($tmpExe); + } + + return $value; + } + + if ($this->hasSttyAvailable()) { + $output->write($question); + + $sttyMode = shell_exec('stty -g'); + + shell_exec('stty -echo'); + $value = fgets($this->inputStream ?: STDIN, 4096); + shell_exec(sprintf('stty %s', $sttyMode)); + + if (false === $value) { + throw new RuntimeException('Aborted'); + } + + $value = trim($value); + $output->writeln(''); + + return $value; + } + + if (false !== $shell = $this->getShell()) { + $output->write($question); + $readCmd = $shell === 'csh' ? 'set mypassword = $<' : 'read -r mypassword'; + $command = sprintf("/usr/bin/env %s -c 'stty -echo; %s; stty echo; echo \$mypassword'", $shell, $readCmd); + $value = rtrim(shell_exec($command)); + $output->writeln(''); + + return $value; + } + + if ($fallback) { + return $this->ask($output, $question); + } + + throw new RuntimeException('Unable to hide the response'); + } + + /** + * Asks for a value and validates the response. + * + * The validator receives the data to validate. It must return the + * validated data when the data is valid and throw an exception + * otherwise. + * + * @param OutputInterface $output An Output instance + * @param string|array $question The question to ask + * @param callable $validator A PHP callback + * @param int|false $attempts Max number of times to ask before giving up (false by default, which means infinite) + * @param string $default The default answer if none is given by the user + * @param array $autocomplete List of values to autocomplete + * + * @return mixed + * + * @throws \Exception When any of the validators return an error + */ + public function askAndValidate(OutputInterface $output, $question, $validator, $attempts = false, $default = null, array $autocomplete = null) + { + $that = $this; + + $interviewer = function () use ($output, $question, $default, $autocomplete, $that) { + return $that->ask($output, $question, $default, $autocomplete); + }; + + return $this->validateAttempts($interviewer, $output, $validator, $attempts); + } + + /** + * Asks for a value, hide and validates the response. + * + * The validator receives the data to validate. It must return the + * validated data when the data is valid and throw an exception + * otherwise. + * + * @param OutputInterface $output An Output instance + * @param string|array $question The question to ask + * @param callable $validator A PHP callback + * @param int|false $attempts Max number of times to ask before giving up (false by default, which means infinite) + * @param bool $fallback In case the response can not be hidden, whether to fallback on non-hidden question or not + * + * @return string The response + * + * @throws \Exception When any of the validators return an error + * @throws RuntimeException In case the fallback is deactivated and the response can not be hidden + */ + public function askHiddenResponseAndValidate(OutputInterface $output, $question, $validator, $attempts = false, $fallback = true) + { + $that = $this; + + $interviewer = function () use ($output, $question, $fallback, $that) { + return $that->askHiddenResponse($output, $question, $fallback); + }; + + return $this->validateAttempts($interviewer, $output, $validator, $attempts); + } + + /** + * Sets the input stream to read from when interacting with the user. + * + * This is mainly useful for testing purpose. + * + * @param resource $stream The input stream + */ + public function setInputStream($stream) + { + $this->inputStream = $stream; + } + + /** + * Returns the helper's input stream. + * + * @return resource|null The input stream or null if the default STDIN is used + */ + public function getInputStream() + { + return $this->inputStream; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'dialog'; + } + + /** + * Return a valid Unix shell. + * + * @return string|bool The valid shell name, false in case no valid shell is found + */ + private function getShell() + { + if (null !== self::$shell) { + return self::$shell; + } + + self::$shell = false; + + if (file_exists('/usr/bin/env')) { + // handle other OSs with bash/zsh/ksh/csh if available to hide the answer + $test = "/usr/bin/env %s -c 'echo OK' 2> /dev/null"; + foreach (array('bash', 'zsh', 'ksh', 'csh') as $sh) { + if ('OK' === rtrim(shell_exec(sprintf($test, $sh)))) { + self::$shell = $sh; + break; + } + } + } + + return self::$shell; + } + + private function hasSttyAvailable() + { + if (null !== self::$stty) { + return self::$stty; + } + + exec('stty 2>&1', $output, $exitcode); + + return self::$stty = $exitcode === 0; + } + + /** + * Validate an attempt. + * + * @param callable $interviewer A callable that will ask for a question and return the result + * @param OutputInterface $output An Output instance + * @param callable $validator A PHP callback + * @param int|false $attempts Max number of times to ask before giving up; false will ask infinitely + * + * @return string The validated response + * + * @throws \Exception In case the max number of attempts has been reached and no valid response has been given + */ + private function validateAttempts($interviewer, OutputInterface $output, $validator, $attempts) + { + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + + $e = null; + while (false === $attempts || $attempts--) { + if (null !== $e) { + $output->writeln($this->getHelperSet()->get('formatter')->formatBlock($e->getMessage(), 'error')); + } + + try { + return call_user_func($validator, $interviewer()); + } catch (\Exception $e) { + } + } + + throw $e; + } +} diff --git a/vendor/symfony/console/Helper/FormatterHelper.php b/vendor/symfony/console/Helper/FormatterHelper.php new file mode 100644 index 0000000000..ac736f982e --- /dev/null +++ b/vendor/symfony/console/Helper/FormatterHelper.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Formatter\OutputFormatter; + +/** + * The Formatter class provides helpers to format messages. + * + * @author Fabien Potencier + */ +class FormatterHelper extends Helper +{ + /** + * Formats a message within a section. + * + * @param string $section The section name + * @param string $message The message + * @param string $style The style to apply to the section + * + * @return string The format section + */ + public function formatSection($section, $message, $style = 'info') + { + return sprintf('<%s>[%s] %s', $style, $section, $style, $message); + } + + /** + * Formats a message as a block of text. + * + * @param string|array $messages The message to write in the block + * @param string $style The style to apply to the whole block + * @param bool $large Whether to return a large block + * + * @return string The formatter message + */ + public function formatBlock($messages, $style, $large = false) + { + if (!is_array($messages)) { + $messages = array($messages); + } + + $len = 0; + $lines = array(); + foreach ($messages as $message) { + $message = OutputFormatter::escape($message); + $lines[] = sprintf($large ? ' %s ' : ' %s ', $message); + $len = max($this->strlen($message) + ($large ? 4 : 2), $len); + } + + $messages = $large ? array(str_repeat(' ', $len)) : array(); + for ($i = 0; isset($lines[$i]); ++$i) { + $messages[] = $lines[$i].str_repeat(' ', $len - $this->strlen($lines[$i])); + } + if ($large) { + $messages[] = str_repeat(' ', $len); + } + + for ($i = 0; isset($messages[$i]); ++$i) { + $messages[$i] = sprintf('<%s>%s', $style, $messages[$i], $style); + } + + return implode("\n", $messages); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'formatter'; + } +} diff --git a/vendor/symfony/console/Helper/Helper.php b/vendor/symfony/console/Helper/Helper.php new file mode 100644 index 0000000000..43f9a16946 --- /dev/null +++ b/vendor/symfony/console/Helper/Helper.php @@ -0,0 +1,119 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * Helper is the base class for all helper classes. + * + * @author Fabien Potencier + */ +abstract class Helper implements HelperInterface +{ + protected $helperSet = null; + + /** + * Sets the helper set associated with this helper. + * + * @param HelperSet $helperSet A HelperSet instance + */ + public function setHelperSet(HelperSet $helperSet = null) + { + $this->helperSet = $helperSet; + } + + /** + * Gets the helper set associated with this helper. + * + * @return HelperSet A HelperSet instance + */ + public function getHelperSet() + { + return $this->helperSet; + } + + /** + * Returns the length of a string, using mb_strwidth if it is available. + * + * @param string $string The string to check its length + * + * @return int The length of the string + */ + public static function strlen($string) + { + if (false === $encoding = mb_detect_encoding($string, null, true)) { + return strlen($string); + } + + return mb_strwidth($string, $encoding); + } + + public static function formatTime($secs) + { + static $timeFormats = array( + array(0, '< 1 sec'), + array(1, '1 sec'), + array(2, 'secs', 1), + array(60, '1 min'), + array(120, 'mins', 60), + array(3600, '1 hr'), + array(7200, 'hrs', 3600), + array(86400, '1 day'), + array(172800, 'days', 86400), + ); + + foreach ($timeFormats as $index => $format) { + if ($secs >= $format[0]) { + if ((isset($timeFormats[$index + 1]) && $secs < $timeFormats[$index + 1][0]) + || $index == count($timeFormats) - 1 + ) { + if (2 == count($format)) { + return $format[1]; + } + + return floor($secs / $format[2]).' '.$format[1]; + } + } + } + } + + public static function formatMemory($memory) + { + if ($memory >= 1024 * 1024 * 1024) { + return sprintf('%.1f GiB', $memory / 1024 / 1024 / 1024); + } + + if ($memory >= 1024 * 1024) { + return sprintf('%.1f MiB', $memory / 1024 / 1024); + } + + if ($memory >= 1024) { + return sprintf('%d KiB', $memory / 1024); + } + + return sprintf('%d B', $memory); + } + + public static function strlenWithoutDecoration(OutputFormatterInterface $formatter, $string) + { + $isDecorated = $formatter->isDecorated(); + $formatter->setDecorated(false); + // remove <...> formatting + $string = $formatter->format($string); + // remove already formatted characters + $string = preg_replace("/\033\[[^m]*m/", '', $string); + $formatter->setDecorated($isDecorated); + + return self::strlen($string); + } +} diff --git a/vendor/symfony/console/Helper/HelperInterface.php b/vendor/symfony/console/Helper/HelperInterface.php new file mode 100644 index 0000000000..5a923e0a88 --- /dev/null +++ b/vendor/symfony/console/Helper/HelperInterface.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +/** + * HelperInterface is the interface all helpers must implement. + * + * @author Fabien Potencier + */ +interface HelperInterface +{ + /** + * Sets the helper set associated with this helper. + * + * @param HelperSet $helperSet A HelperSet instance + */ + public function setHelperSet(HelperSet $helperSet = null); + + /** + * Gets the helper set associated with this helper. + * + * @return HelperSet A HelperSet instance + */ + public function getHelperSet(); + + /** + * Returns the canonical name of this helper. + * + * @return string The canonical name + */ + public function getName(); +} diff --git a/vendor/symfony/console/Helper/HelperSet.php b/vendor/symfony/console/Helper/HelperSet.php new file mode 100644 index 0000000000..27fedcf7b5 --- /dev/null +++ b/vendor/symfony/console/Helper/HelperSet.php @@ -0,0 +1,117 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * HelperSet represents a set of helpers to be used with a command. + * + * @author Fabien Potencier + */ +class HelperSet implements \IteratorAggregate +{ + private $helpers = array(); + private $command; + + /** + * Constructor. + * + * @param Helper[] $helpers An array of helper. + */ + public function __construct(array $helpers = array()) + { + foreach ($helpers as $alias => $helper) { + $this->set($helper, is_int($alias) ? null : $alias); + } + } + + /** + * Sets a helper. + * + * @param HelperInterface $helper The helper instance + * @param string $alias An alias + */ + public function set(HelperInterface $helper, $alias = null) + { + $this->helpers[$helper->getName()] = $helper; + if (null !== $alias) { + $this->helpers[$alias] = $helper; + } + + $helper->setHelperSet($this); + } + + /** + * Returns true if the helper if defined. + * + * @param string $name The helper name + * + * @return bool true if the helper is defined, false otherwise + */ + public function has($name) + { + return isset($this->helpers[$name]); + } + + /** + * Gets a helper value. + * + * @param string $name The helper name + * + * @return HelperInterface The helper instance + * + * @throws InvalidArgumentException if the helper is not defined + */ + public function get($name) + { + if (!$this->has($name)) { + throw new InvalidArgumentException(sprintf('The helper "%s" is not defined.', $name)); + } + + if ('dialog' === $name && $this->helpers[$name] instanceof DialogHelper) { + @trigger_error('"Symfony\Component\Console\Helper\DialogHelper" is deprecated since version 2.5 and will be removed in 3.0. Use "Symfony\Component\Console\Helper\QuestionHelper" instead.', E_USER_DEPRECATED); + } elseif ('progress' === $name && $this->helpers[$name] instanceof ProgressHelper) { + @trigger_error('"Symfony\Component\Console\Helper\ProgressHelper" is deprecated since version 2.5 and will be removed in 3.0. Use "Symfony\Component\Console\Helper\ProgressBar" instead.', E_USER_DEPRECATED); + } elseif ('table' === $name && $this->helpers[$name] instanceof TableHelper) { + @trigger_error('"Symfony\Component\Console\Helper\TableHelper" is deprecated since version 2.5 and will be removed in 3.0. Use "Symfony\Component\Console\Helper\Table" instead.', E_USER_DEPRECATED); + } + + return $this->helpers[$name]; + } + + /** + * Sets the command associated with this helper set. + * + * @param Command $command A Command instance + */ + public function setCommand(Command $command = null) + { + $this->command = $command; + } + + /** + * Gets the command associated with this helper set. + * + * @return Command A Command instance + */ + public function getCommand() + { + return $this->command; + } + + public function getIterator() + { + return new \ArrayIterator($this->helpers); + } +} diff --git a/vendor/symfony/console/Helper/InputAwareHelper.php b/vendor/symfony/console/Helper/InputAwareHelper.php new file mode 100644 index 0000000000..4261767423 --- /dev/null +++ b/vendor/symfony/console/Helper/InputAwareHelper.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputAwareInterface; + +/** + * An implementation of InputAwareInterface for Helpers. + * + * @author Wouter J + */ +abstract class InputAwareHelper extends Helper implements InputAwareInterface +{ + protected $input; + + /** + * {@inheritdoc} + */ + public function setInput(InputInterface $input) + { + $this->input = $input; + } +} diff --git a/vendor/symfony/console/Helper/ProcessHelper.php b/vendor/symfony/console/Helper/ProcessHelper.php new file mode 100644 index 0000000000..a811eb48e6 --- /dev/null +++ b/vendor/symfony/console/Helper/ProcessHelper.php @@ -0,0 +1,151 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Process\Exception\ProcessFailedException; +use Symfony\Component\Process\Process; +use Symfony\Component\Process\ProcessBuilder; + +/** + * The ProcessHelper class provides helpers to run external processes. + * + * @author Fabien Potencier + */ +class ProcessHelper extends Helper +{ + /** + * Runs an external process. + * + * @param OutputInterface $output An OutputInterface instance + * @param string|array|Process $cmd An instance of Process or an array of arguments to escape and run or a command to run + * @param string|null $error An error message that must be displayed if something went wrong + * @param callable|null $callback A PHP callback to run whenever there is some + * output available on STDOUT or STDERR + * @param int $verbosity The threshold for verbosity + * + * @return Process The process that ran + */ + public function run(OutputInterface $output, $cmd, $error = null, $callback = null, $verbosity = OutputInterface::VERBOSITY_VERY_VERBOSE) + { + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + + $formatter = $this->getHelperSet()->get('debug_formatter'); + + if (is_array($cmd)) { + $process = ProcessBuilder::create($cmd)->getProcess(); + } elseif ($cmd instanceof Process) { + $process = $cmd; + } else { + $process = new Process($cmd); + } + + if ($verbosity <= $output->getVerbosity()) { + $output->write($formatter->start(spl_object_hash($process), $this->escapeString($process->getCommandLine()))); + } + + if ($output->isDebug()) { + $callback = $this->wrapCallback($output, $process, $callback); + } + + $process->run($callback); + + if ($verbosity <= $output->getVerbosity()) { + $message = $process->isSuccessful() ? 'Command ran successfully' : sprintf('%s Command did not run successfully', $process->getExitCode()); + $output->write($formatter->stop(spl_object_hash($process), $message, $process->isSuccessful())); + } + + if (!$process->isSuccessful() && null !== $error) { + $output->writeln(sprintf('%s', $this->escapeString($error))); + } + + return $process; + } + + /** + * Runs the process. + * + * This is identical to run() except that an exception is thrown if the process + * exits with a non-zero exit code. + * + * @param OutputInterface $output An OutputInterface instance + * @param string|Process $cmd An instance of Process or a command to run + * @param string|null $error An error message that must be displayed if something went wrong + * @param callable|null $callback A PHP callback to run whenever there is some + * output available on STDOUT or STDERR + * + * @return Process The process that ran + * + * @throws ProcessFailedException + * + * @see run() + */ + public function mustRun(OutputInterface $output, $cmd, $error = null, $callback = null) + { + $process = $this->run($output, $cmd, $error, $callback); + + if (!$process->isSuccessful()) { + throw new ProcessFailedException($process); + } + + return $process; + } + + /** + * Wraps a Process callback to add debugging output. + * + * @param OutputInterface $output An OutputInterface interface + * @param Process $process The Process + * @param callable|null $callback A PHP callable + * + * @return callable + */ + public function wrapCallback(OutputInterface $output, Process $process, $callback = null) + { + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + + $formatter = $this->getHelperSet()->get('debug_formatter'); + + $that = $this; + + return function ($type, $buffer) use ($output, $process, $callback, $formatter, $that) { + $output->write($formatter->progress(spl_object_hash($process), $that->escapeString($buffer), Process::ERR === $type)); + + if (null !== $callback) { + call_user_func($callback, $type, $buffer); + } + }; + } + + /** + * This method is public for PHP 5.3 compatibility, it should be private. + * + * @internal + */ + public function escapeString($str) + { + return str_replace('<', '\\<', $str); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'process'; + } +} diff --git a/vendor/symfony/console/Helper/ProgressBar.php b/vendor/symfony/console/Helper/ProgressBar.php new file mode 100644 index 0000000000..e7717a528f --- /dev/null +++ b/vendor/symfony/console/Helper/ProgressBar.php @@ -0,0 +1,621 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Exception\LogicException; + +/** + * The ProgressBar provides helpers to display progress output. + * + * @author Fabien Potencier + * @author Chris Jones + */ +class ProgressBar +{ + // options + private $barWidth = 28; + private $barChar; + private $emptyBarChar = '-'; + private $progressChar = '>'; + private $format; + private $internalFormat; + private $redrawFreq = 1; + + /** + * @var OutputInterface + */ + private $output; + private $step = 0; + private $max; + private $startTime; + private $stepWidth; + private $percent = 0.0; + private $formatLineCount; + private $messages; + private $overwrite = true; + + private static $formatters; + private static $formats; + + /** + * Constructor. + * + * @param OutputInterface $output An OutputInterface instance + * @param int $max Maximum steps (0 if unknown) + */ + public function __construct(OutputInterface $output, $max = 0) + { + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + + $this->output = $output; + $this->setMaxSteps($max); + + if (!$this->output->isDecorated()) { + // disable overwrite when output does not support ANSI codes. + $this->overwrite = false; + + // set a reasonable redraw frequency so output isn't flooded + $this->setRedrawFrequency($max / 10); + } + + $this->startTime = time(); + } + + /** + * Sets a placeholder formatter for a given name. + * + * This method also allow you to override an existing placeholder. + * + * @param string $name The placeholder name (including the delimiter char like %) + * @param callable $callable A PHP callable + */ + public static function setPlaceholderFormatterDefinition($name, $callable) + { + if (!self::$formatters) { + self::$formatters = self::initPlaceholderFormatters(); + } + + self::$formatters[$name] = $callable; + } + + /** + * Gets the placeholder formatter for a given name. + * + * @param string $name The placeholder name (including the delimiter char like %) + * + * @return callable|null A PHP callable + */ + public static function getPlaceholderFormatterDefinition($name) + { + if (!self::$formatters) { + self::$formatters = self::initPlaceholderFormatters(); + } + + return isset(self::$formatters[$name]) ? self::$formatters[$name] : null; + } + + /** + * Sets a format for a given name. + * + * This method also allow you to override an existing format. + * + * @param string $name The format name + * @param string $format A format string + */ + public static function setFormatDefinition($name, $format) + { + if (!self::$formats) { + self::$formats = self::initFormats(); + } + + self::$formats[$name] = $format; + } + + /** + * Gets the format for a given name. + * + * @param string $name The format name + * + * @return string|null A format string + */ + public static function getFormatDefinition($name) + { + if (!self::$formats) { + self::$formats = self::initFormats(); + } + + return isset(self::$formats[$name]) ? self::$formats[$name] : null; + } + + public function setMessage($message, $name = 'message') + { + $this->messages[$name] = $message; + } + + public function getMessage($name = 'message') + { + return $this->messages[$name]; + } + + /** + * Gets the progress bar start time. + * + * @return int The progress bar start time + */ + public function getStartTime() + { + return $this->startTime; + } + + /** + * Gets the progress bar maximal steps. + * + * @return int The progress bar max steps + */ + public function getMaxSteps() + { + return $this->max; + } + + /** + * Gets the progress bar step. + * + * @deprecated since version 2.6, to be removed in 3.0. Use {@link getProgress()} instead. + * + * @return int The progress bar step + */ + public function getStep() + { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.6 and will be removed in 3.0. Use the getProgress() method instead.', E_USER_DEPRECATED); + + return $this->getProgress(); + } + + /** + * Gets the current step position. + * + * @return int The progress bar step + */ + public function getProgress() + { + return $this->step; + } + + /** + * Gets the progress bar step width. + * + * @internal This method is public for PHP 5.3 compatibility, it should not be used. + * + * @return int The progress bar step width + */ + public function getStepWidth() + { + return $this->stepWidth; + } + + /** + * Gets the current progress bar percent. + * + * @return float The current progress bar percent + */ + public function getProgressPercent() + { + return $this->percent; + } + + /** + * Sets the progress bar width. + * + * @param int $size The progress bar size + */ + public function setBarWidth($size) + { + $this->barWidth = (int) $size; + } + + /** + * Gets the progress bar width. + * + * @return int The progress bar size + */ + public function getBarWidth() + { + return $this->barWidth; + } + + /** + * Sets the bar character. + * + * @param string $char A character + */ + public function setBarCharacter($char) + { + $this->barChar = $char; + } + + /** + * Gets the bar character. + * + * @return string A character + */ + public function getBarCharacter() + { + if (null === $this->barChar) { + return $this->max ? '=' : $this->emptyBarChar; + } + + return $this->barChar; + } + + /** + * Sets the empty bar character. + * + * @param string $char A character + */ + public function setEmptyBarCharacter($char) + { + $this->emptyBarChar = $char; + } + + /** + * Gets the empty bar character. + * + * @return string A character + */ + public function getEmptyBarCharacter() + { + return $this->emptyBarChar; + } + + /** + * Sets the progress bar character. + * + * @param string $char A character + */ + public function setProgressCharacter($char) + { + $this->progressChar = $char; + } + + /** + * Gets the progress bar character. + * + * @return string A character + */ + public function getProgressCharacter() + { + return $this->progressChar; + } + + /** + * Sets the progress bar format. + * + * @param string $format The format + */ + public function setFormat($format) + { + $this->format = null; + $this->internalFormat = $format; + } + + /** + * Sets the redraw frequency. + * + * @param int|float $freq The frequency in steps + */ + public function setRedrawFrequency($freq) + { + $this->redrawFreq = max((int) $freq, 1); + } + + /** + * Starts the progress output. + * + * @param int|null $max Number of steps to complete the bar (0 if indeterminate), null to leave unchanged + */ + public function start($max = null) + { + $this->startTime = time(); + $this->step = 0; + $this->percent = 0.0; + + if (null !== $max) { + $this->setMaxSteps($max); + } + + $this->display(); + } + + /** + * Advances the progress output X steps. + * + * @param int $step Number of steps to advance + * + * @throws LogicException + */ + public function advance($step = 1) + { + $this->setProgress($this->step + $step); + } + + /** + * Sets the current progress. + * + * @deprecated since version 2.6, to be removed in 3.0. Use {@link setProgress()} instead. + * + * @param int $step The current progress + * + * @throws LogicException + */ + public function setCurrent($step) + { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.6 and will be removed in 3.0. Use the setProgress() method instead.', E_USER_DEPRECATED); + + $this->setProgress($step); + } + + /** + * Sets whether to overwrite the progressbar, false for new line. + * + * @param bool $overwrite + */ + public function setOverwrite($overwrite) + { + $this->overwrite = (bool) $overwrite; + } + + /** + * Sets the current progress. + * + * @param int $step The current progress + * + * @throws LogicException + */ + public function setProgress($step) + { + $step = (int) $step; + if ($step < $this->step) { + throw new LogicException('You can\'t regress the progress bar.'); + } + + if ($this->max && $step > $this->max) { + $this->max = $step; + } + + $prevPeriod = (int) ($this->step / $this->redrawFreq); + $currPeriod = (int) ($step / $this->redrawFreq); + $this->step = $step; + $this->percent = $this->max ? (float) $this->step / $this->max : 0; + if ($prevPeriod !== $currPeriod || $this->max === $step) { + $this->display(); + } + } + + /** + * Finishes the progress output. + */ + public function finish() + { + if (!$this->max) { + $this->max = $this->step; + } + + if ($this->step === $this->max && !$this->overwrite) { + // prevent double 100% output + return; + } + + $this->setProgress($this->max); + } + + /** + * Outputs the current progress string. + */ + public function display() + { + if (OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity()) { + return; + } + + if (null === $this->format) { + $this->setRealFormat($this->internalFormat ?: $this->determineBestFormat()); + } + + // these 3 variables can be removed in favor of using $this in the closure when support for PHP 5.3 will be dropped. + $self = $this; + $output = $this->output; + $messages = $this->messages; + $this->overwrite(preg_replace_callback("{%([a-z\-_]+)(?:\:([^%]+))?%}i", function ($matches) use ($self, $output, $messages) { + if ($formatter = $self::getPlaceholderFormatterDefinition($matches[1])) { + $text = call_user_func($formatter, $self, $output); + } elseif (isset($messages[$matches[1]])) { + $text = $messages[$matches[1]]; + } else { + return $matches[0]; + } + + if (isset($matches[2])) { + $text = sprintf('%'.$matches[2], $text); + } + + return $text; + }, $this->format)); + } + + /** + * Removes the progress bar from the current line. + * + * This is useful if you wish to write some output + * while a progress bar is running. + * Call display() to show the progress bar again. + */ + public function clear() + { + if (!$this->overwrite) { + return; + } + + if (null === $this->format) { + $this->setRealFormat($this->internalFormat ?: $this->determineBestFormat()); + } + + $this->overwrite(''); + } + + /** + * Sets the progress bar format. + * + * @param string $format The format + */ + private function setRealFormat($format) + { + // try to use the _nomax variant if available + if (!$this->max && null !== self::getFormatDefinition($format.'_nomax')) { + $this->format = self::getFormatDefinition($format.'_nomax'); + } elseif (null !== self::getFormatDefinition($format)) { + $this->format = self::getFormatDefinition($format); + } else { + $this->format = $format; + } + + $this->formatLineCount = substr_count($this->format, "\n"); + } + + /** + * Sets the progress bar maximal steps. + * + * @param int $max The progress bar max steps + */ + private function setMaxSteps($max) + { + $this->max = max(0, (int) $max); + $this->stepWidth = $this->max ? Helper::strlen($this->max) : 4; + } + + /** + * Overwrites a previous message to the output. + * + * @param string $message The message + */ + private function overwrite($message) + { + if ($this->overwrite) { + // Move the cursor to the beginning of the line + $this->output->write("\x0D"); + + // Erase the line + $this->output->write("\x1B[2K"); + + // Erase previous lines + if ($this->formatLineCount > 0) { + $this->output->write(str_repeat("\x1B[1A\x1B[2K", $this->formatLineCount)); + } + } elseif ($this->step > 0) { + $this->output->writeln(''); + } + + $this->output->write($message); + } + + private function determineBestFormat() + { + switch ($this->output->getVerbosity()) { + // OutputInterface::VERBOSITY_QUIET: display is disabled anyway + case OutputInterface::VERBOSITY_VERBOSE: + return $this->max ? 'verbose' : 'verbose_nomax'; + case OutputInterface::VERBOSITY_VERY_VERBOSE: + return $this->max ? 'very_verbose' : 'very_verbose_nomax'; + case OutputInterface::VERBOSITY_DEBUG: + return $this->max ? 'debug' : 'debug_nomax'; + default: + return $this->max ? 'normal' : 'normal_nomax'; + } + } + + private static function initPlaceholderFormatters() + { + return array( + 'bar' => function (ProgressBar $bar, OutputInterface $output) { + $completeBars = floor($bar->getMaxSteps() > 0 ? $bar->getProgressPercent() * $bar->getBarWidth() : $bar->getProgress() % $bar->getBarWidth()); + $display = str_repeat($bar->getBarCharacter(), $completeBars); + if ($completeBars < $bar->getBarWidth()) { + $emptyBars = $bar->getBarWidth() - $completeBars - Helper::strlenWithoutDecoration($output->getFormatter(), $bar->getProgressCharacter()); + $display .= $bar->getProgressCharacter().str_repeat($bar->getEmptyBarCharacter(), $emptyBars); + } + + return $display; + }, + 'elapsed' => function (ProgressBar $bar) { + return Helper::formatTime(time() - $bar->getStartTime()); + }, + 'remaining' => function (ProgressBar $bar) { + if (!$bar->getMaxSteps()) { + throw new LogicException('Unable to display the remaining time if the maximum number of steps is not set.'); + } + + if (!$bar->getProgress()) { + $remaining = 0; + } else { + $remaining = round((time() - $bar->getStartTime()) / $bar->getProgress() * ($bar->getMaxSteps() - $bar->getProgress())); + } + + return Helper::formatTime($remaining); + }, + 'estimated' => function (ProgressBar $bar) { + if (!$bar->getMaxSteps()) { + throw new LogicException('Unable to display the estimated time if the maximum number of steps is not set.'); + } + + if (!$bar->getProgress()) { + $estimated = 0; + } else { + $estimated = round((time() - $bar->getStartTime()) / $bar->getProgress() * $bar->getMaxSteps()); + } + + return Helper::formatTime($estimated); + }, + 'memory' => function (ProgressBar $bar) { + return Helper::formatMemory(memory_get_usage(true)); + }, + 'current' => function (ProgressBar $bar) { + return str_pad($bar->getProgress(), $bar->getStepWidth(), ' ', STR_PAD_LEFT); + }, + 'max' => function (ProgressBar $bar) { + return $bar->getMaxSteps(); + }, + 'percent' => function (ProgressBar $bar) { + return floor($bar->getProgressPercent() * 100); + }, + ); + } + + private static function initFormats() + { + return array( + 'normal' => ' %current%/%max% [%bar%] %percent:3s%%', + 'normal_nomax' => ' %current% [%bar%]', + + 'verbose' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%', + 'verbose_nomax' => ' %current% [%bar%] %elapsed:6s%', + + 'very_verbose' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s%', + 'very_verbose_nomax' => ' %current% [%bar%] %elapsed:6s%', + + 'debug' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s% %memory:6s%', + 'debug_nomax' => ' %current% [%bar%] %elapsed:6s% %memory:6s%', + ); + } +} diff --git a/vendor/symfony/console/Helper/ProgressHelper.php b/vendor/symfony/console/Helper/ProgressHelper.php new file mode 100644 index 0000000000..96b6202c9b --- /dev/null +++ b/vendor/symfony/console/Helper/ProgressHelper.php @@ -0,0 +1,471 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Output\NullOutput; +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Exception\LogicException; + +/** + * The Progress class provides helpers to display progress output. + * + * @author Chris Jones + * @author Fabien Potencier + * + * @deprecated since version 2.5, to be removed in 3.0 + * Use {@link ProgressBar} instead. + */ +class ProgressHelper extends Helper +{ + const FORMAT_QUIET = ' %percent%%'; + const FORMAT_NORMAL = ' %current%/%max% [%bar%] %percent%%'; + const FORMAT_VERBOSE = ' %current%/%max% [%bar%] %percent%% Elapsed: %elapsed%'; + const FORMAT_QUIET_NOMAX = ' %current%'; + const FORMAT_NORMAL_NOMAX = ' %current% [%bar%]'; + const FORMAT_VERBOSE_NOMAX = ' %current% [%bar%] Elapsed: %elapsed%'; + + // options + private $barWidth = 28; + private $barChar = '='; + private $emptyBarChar = '-'; + private $progressChar = '>'; + private $format = null; + private $redrawFreq = 1; + + private $lastMessagesLength; + private $barCharOriginal; + + /** + * @var OutputInterface + */ + private $output; + + /** + * Current step. + * + * @var int + */ + private $current; + + /** + * Maximum number of steps. + * + * @var int + */ + private $max; + + /** + * Start time of the progress bar. + * + * @var int + */ + private $startTime; + + /** + * List of formatting variables. + * + * @var array + */ + private $defaultFormatVars = array( + 'current', + 'max', + 'bar', + 'percent', + 'elapsed', + ); + + /** + * Available formatting variables. + * + * @var array + */ + private $formatVars; + + /** + * Stored format part widths (used for padding). + * + * @var array + */ + private $widths = array( + 'current' => 4, + 'max' => 4, + 'percent' => 3, + 'elapsed' => 6, + ); + + /** + * Various time formats. + * + * @var array + */ + private $timeFormats = array( + array(0, '???'), + array(2, '1 sec'), + array(59, 'secs', 1), + array(60, '1 min'), + array(3600, 'mins', 60), + array(5400, '1 hr'), + array(86400, 'hrs', 3600), + array(129600, '1 day'), + array(604800, 'days', 86400), + ); + + public function __construct($triggerDeprecationError = true) + { + if ($triggerDeprecationError) { + @trigger_error('The '.__CLASS__.' class is deprecated since version 2.5 and will be removed in 3.0. Use the Symfony\Component\Console\Helper\ProgressBar class instead.', E_USER_DEPRECATED); + } + } + + /** + * Sets the progress bar width. + * + * @param int $size The progress bar size + */ + public function setBarWidth($size) + { + $this->barWidth = (int) $size; + } + + /** + * Sets the bar character. + * + * @param string $char A character + */ + public function setBarCharacter($char) + { + $this->barChar = $char; + } + + /** + * Sets the empty bar character. + * + * @param string $char A character + */ + public function setEmptyBarCharacter($char) + { + $this->emptyBarChar = $char; + } + + /** + * Sets the progress bar character. + * + * @param string $char A character + */ + public function setProgressCharacter($char) + { + $this->progressChar = $char; + } + + /** + * Sets the progress bar format. + * + * @param string $format The format + */ + public function setFormat($format) + { + $this->format = $format; + } + + /** + * Sets the redraw frequency. + * + * @param int $freq The frequency in steps + */ + public function setRedrawFrequency($freq) + { + $this->redrawFreq = (int) $freq; + } + + /** + * Starts the progress output. + * + * @param OutputInterface $output An Output instance + * @param int|null $max Maximum steps + */ + public function start(OutputInterface $output, $max = null) + { + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + + $this->startTime = time(); + $this->current = 0; + $this->max = (int) $max; + + // Disabling output when it does not support ANSI codes as it would result in a broken display anyway. + $this->output = $output->isDecorated() ? $output : new NullOutput(); + $this->lastMessagesLength = 0; + $this->barCharOriginal = ''; + + if (null === $this->format) { + switch ($output->getVerbosity()) { + case OutputInterface::VERBOSITY_QUIET: + $this->format = self::FORMAT_QUIET_NOMAX; + if ($this->max > 0) { + $this->format = self::FORMAT_QUIET; + } + break; + case OutputInterface::VERBOSITY_VERBOSE: + case OutputInterface::VERBOSITY_VERY_VERBOSE: + case OutputInterface::VERBOSITY_DEBUG: + $this->format = self::FORMAT_VERBOSE_NOMAX; + if ($this->max > 0) { + $this->format = self::FORMAT_VERBOSE; + } + break; + default: + $this->format = self::FORMAT_NORMAL_NOMAX; + if ($this->max > 0) { + $this->format = self::FORMAT_NORMAL; + } + break; + } + } + + $this->initialize(); + } + + /** + * Advances the progress output X steps. + * + * @param int $step Number of steps to advance + * @param bool $redraw Whether to redraw or not + * + * @throws LogicException + */ + public function advance($step = 1, $redraw = false) + { + $this->setCurrent($this->current + $step, $redraw); + } + + /** + * Sets the current progress. + * + * @param int $current The current progress + * @param bool $redraw Whether to redraw or not + * + * @throws LogicException + */ + public function setCurrent($current, $redraw = false) + { + if (null === $this->startTime) { + throw new LogicException('You must start the progress bar before calling setCurrent().'); + } + + $current = (int) $current; + + if ($current < $this->current) { + throw new LogicException('You can\'t regress the progress bar'); + } + + if (0 === $this->current) { + $redraw = true; + } + + $prevPeriod = (int) ($this->current / $this->redrawFreq); + + $this->current = $current; + + $currPeriod = (int) ($this->current / $this->redrawFreq); + if ($redraw || $prevPeriod !== $currPeriod || $this->max === $this->current) { + $this->display(); + } + } + + /** + * Outputs the current progress string. + * + * @param bool $finish Forces the end result + * + * @throws LogicException + */ + public function display($finish = false) + { + if (null === $this->startTime) { + throw new LogicException('You must start the progress bar before calling display().'); + } + + $message = $this->format; + foreach ($this->generate($finish) as $name => $value) { + $message = str_replace("%{$name}%", $value, $message); + } + $this->overwrite($this->output, $message); + } + + /** + * Removes the progress bar from the current line. + * + * This is useful if you wish to write some output + * while a progress bar is running. + * Call display() to show the progress bar again. + */ + public function clear() + { + $this->overwrite($this->output, ''); + } + + /** + * Finishes the progress output. + */ + public function finish() + { + if (null === $this->startTime) { + throw new LogicException('You must start the progress bar before calling finish().'); + } + + if (null !== $this->startTime) { + if (!$this->max) { + $this->barChar = $this->barCharOriginal; + $this->display(true); + } + $this->startTime = null; + $this->output->writeln(''); + $this->output = null; + } + } + + /** + * Initializes the progress helper. + */ + private function initialize() + { + $this->formatVars = array(); + foreach ($this->defaultFormatVars as $var) { + if (false !== strpos($this->format, "%{$var}%")) { + $this->formatVars[$var] = true; + } + } + + if ($this->max > 0) { + $this->widths['max'] = $this->strlen($this->max); + $this->widths['current'] = $this->widths['max']; + } else { + $this->barCharOriginal = $this->barChar; + $this->barChar = $this->emptyBarChar; + } + } + + /** + * Generates the array map of format variables to values. + * + * @param bool $finish Forces the end result + * + * @return array Array of format vars and values + */ + private function generate($finish = false) + { + $vars = array(); + $percent = 0; + if ($this->max > 0) { + $percent = (float) $this->current / $this->max; + } + + if (isset($this->formatVars['bar'])) { + $completeBars = 0; + + if ($this->max > 0) { + $completeBars = floor($percent * $this->barWidth); + } else { + if (!$finish) { + $completeBars = floor($this->current % $this->barWidth); + } else { + $completeBars = $this->barWidth; + } + } + + $emptyBars = $this->barWidth - $completeBars - $this->strlen($this->progressChar); + $bar = str_repeat($this->barChar, $completeBars); + if ($completeBars < $this->barWidth) { + $bar .= $this->progressChar; + $bar .= str_repeat($this->emptyBarChar, $emptyBars); + } + + $vars['bar'] = $bar; + } + + if (isset($this->formatVars['elapsed'])) { + $elapsed = time() - $this->startTime; + $vars['elapsed'] = str_pad($this->humaneTime($elapsed), $this->widths['elapsed'], ' ', STR_PAD_LEFT); + } + + if (isset($this->formatVars['current'])) { + $vars['current'] = str_pad($this->current, $this->widths['current'], ' ', STR_PAD_LEFT); + } + + if (isset($this->formatVars['max'])) { + $vars['max'] = $this->max; + } + + if (isset($this->formatVars['percent'])) { + $vars['percent'] = str_pad(floor($percent * 100), $this->widths['percent'], ' ', STR_PAD_LEFT); + } + + return $vars; + } + + /** + * Converts seconds into human-readable format. + * + * @param int $secs Number of seconds + * + * @return string Time in readable format + */ + private function humaneTime($secs) + { + $text = ''; + foreach ($this->timeFormats as $format) { + if ($secs < $format[0]) { + if (count($format) == 2) { + $text = $format[1]; + break; + } else { + $text = ceil($secs / $format[2]).' '.$format[1]; + break; + } + } + } + + return $text; + } + + /** + * Overwrites a previous message to the output. + * + * @param OutputInterface $output An Output instance + * @param string $message The message + */ + private function overwrite(OutputInterface $output, $message) + { + $length = $this->strlen($message); + + // append whitespace to match the last line's length + if (null !== $this->lastMessagesLength && $this->lastMessagesLength > $length) { + $message = str_pad($message, $this->lastMessagesLength, "\x20", STR_PAD_RIGHT); + } + + // carriage return + $output->write("\x0D"); + $output->write($message); + + $this->lastMessagesLength = $this->strlen($message); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'progress'; + } +} diff --git a/vendor/symfony/console/Helper/ProgressIndicator.php b/vendor/symfony/console/Helper/ProgressIndicator.php new file mode 100644 index 0000000000..ccf9771bcf --- /dev/null +++ b/vendor/symfony/console/Helper/ProgressIndicator.php @@ -0,0 +1,324 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * @author Kevin Bond + */ +class ProgressIndicator +{ + private $output; + private $startTime; + private $format; + private $message; + private $indicatorValues; + private $indicatorCurrent; + private $indicatorChangeInterval; + private $indicatorUpdateTime; + private $lastMessagesLength; + private $started = false; + + private static $formatters; + private static $formats; + + /** + * @param OutputInterface $output + * @param string|null $format Indicator format + * @param int $indicatorChangeInterval Change interval in milliseconds + * @param array|null $indicatorValues Animated indicator characters + */ + public function __construct(OutputInterface $output, $format = null, $indicatorChangeInterval = 100, $indicatorValues = null) + { + $this->output = $output; + + if (null === $format) { + $format = $this->determineBestFormat(); + } + + if (null === $indicatorValues) { + $indicatorValues = array('-', '\\', '|', '/'); + } + + $indicatorValues = array_values($indicatorValues); + + if (2 > count($indicatorValues)) { + throw new InvalidArgumentException('Must have at least 2 indicator value characters.'); + } + + $this->format = self::getFormatDefinition($format); + $this->indicatorChangeInterval = $indicatorChangeInterval; + $this->indicatorValues = $indicatorValues; + $this->startTime = time(); + } + + /** + * Sets the current indicator message. + * + * @param string|null $message + */ + public function setMessage($message) + { + $this->message = $message; + + $this->display(); + } + + /** + * Gets the current indicator message. + * + * @return string|null + * + * @internal for PHP 5.3 compatibility + */ + public function getMessage() + { + return $this->message; + } + + /** + * Gets the progress bar start time. + * + * @return int The progress bar start time + * + * @internal for PHP 5.3 compatibility + */ + public function getStartTime() + { + return $this->startTime; + } + + /** + * Gets the current animated indicator character. + * + * @return string + * + * @internal for PHP 5.3 compatibility + */ + public function getCurrentValue() + { + return $this->indicatorValues[$this->indicatorCurrent % count($this->indicatorValues)]; + } + + /** + * Starts the indicator output. + * + * @param $message + */ + public function start($message) + { + if ($this->started) { + throw new LogicException('Progress indicator already started.'); + } + + $this->message = $message; + $this->started = true; + $this->lastMessagesLength = 0; + $this->startTime = time(); + $this->indicatorUpdateTime = $this->getCurrentTimeInMilliseconds() + $this->indicatorChangeInterval; + $this->indicatorCurrent = 0; + + $this->display(); + } + + /** + * Advances the indicator. + */ + public function advance() + { + if (!$this->started) { + throw new LogicException('Progress indicator has not yet been started.'); + } + + if (!$this->output->isDecorated()) { + return; + } + + $currentTime = $this->getCurrentTimeInMilliseconds(); + + if ($currentTime < $this->indicatorUpdateTime) { + return; + } + + $this->indicatorUpdateTime = $currentTime + $this->indicatorChangeInterval; + ++$this->indicatorCurrent; + + $this->display(); + } + + /** + * Finish the indicator with message. + * + * @param $message + */ + public function finish($message) + { + if (!$this->started) { + throw new LogicException('Progress indicator has not yet been started.'); + } + + $this->message = $message; + $this->display(); + $this->output->writeln(''); + $this->started = false; + } + + /** + * Gets the format for a given name. + * + * @param string $name The format name + * + * @return string|null A format string + */ + public static function getFormatDefinition($name) + { + if (!self::$formats) { + self::$formats = self::initFormats(); + } + + return isset(self::$formats[$name]) ? self::$formats[$name] : null; + } + + /** + * Sets a placeholder formatter for a given name. + * + * This method also allow you to override an existing placeholder. + * + * @param string $name The placeholder name (including the delimiter char like %) + * @param callable $callable A PHP callable + */ + public static function setPlaceholderFormatterDefinition($name, $callable) + { + if (!self::$formatters) { + self::$formatters = self::initPlaceholderFormatters(); + } + + self::$formatters[$name] = $callable; + } + + /** + * Gets the placeholder formatter for a given name. + * + * @param string $name The placeholder name (including the delimiter char like %) + * + * @return callable|null A PHP callable + */ + public static function getPlaceholderFormatterDefinition($name) + { + if (!self::$formatters) { + self::$formatters = self::initPlaceholderFormatters(); + } + + return isset(self::$formatters[$name]) ? self::$formatters[$name] : null; + } + + private function display() + { + if (OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity()) { + return; + } + + $self = $this; + + $this->overwrite(preg_replace_callback("{%([a-z\-_]+)(?:\:([^%]+))?%}i", function ($matches) use ($self) { + if ($formatter = $self::getPlaceholderFormatterDefinition($matches[1])) { + return call_user_func($formatter, $self); + } + + return $matches[0]; + }, $this->format)); + } + + private function determineBestFormat() + { + switch ($this->output->getVerbosity()) { + // OutputInterface::VERBOSITY_QUIET: display is disabled anyway + case OutputInterface::VERBOSITY_VERBOSE: + return $this->output->isDecorated() ? 'verbose' : 'verbose_no_ansi'; + case OutputInterface::VERBOSITY_VERY_VERBOSE: + case OutputInterface::VERBOSITY_DEBUG: + return $this->output->isDecorated() ? 'very_verbose' : 'very_verbose_no_ansi'; + default: + return $this->output->isDecorated() ? 'normal' : 'normal_no_ansi'; + } + } + + /** + * Overwrites a previous message to the output. + * + * @param string $message The message + */ + private function overwrite($message) + { + // append whitespace to match the line's length + if (null !== $this->lastMessagesLength) { + if ($this->lastMessagesLength > Helper::strlenWithoutDecoration($this->output->getFormatter(), $message)) { + $message = str_pad($message, $this->lastMessagesLength, "\x20", STR_PAD_RIGHT); + } + } + + if ($this->output->isDecorated()) { + $this->output->write("\x0D"); + $this->output->write($message); + } else { + $this->output->writeln($message); + } + + $this->lastMessagesLength = 0; + + $len = Helper::strlenWithoutDecoration($this->output->getFormatter(), $message); + + if ($len > $this->lastMessagesLength) { + $this->lastMessagesLength = $len; + } + } + + private function getCurrentTimeInMilliseconds() + { + return round(microtime(true) * 1000); + } + + private static function initPlaceholderFormatters() + { + return array( + 'indicator' => function (ProgressIndicator $indicator) { + return $indicator->getCurrentValue(); + }, + 'message' => function (ProgressIndicator $indicator) { + return $indicator->getMessage(); + }, + 'elapsed' => function (ProgressIndicator $indicator) { + return Helper::formatTime(time() - $indicator->getStartTime()); + }, + 'memory' => function () { + return Helper::formatMemory(memory_get_usage(true)); + }, + ); + } + + private static function initFormats() + { + return array( + 'normal' => ' %indicator% %message%', + 'normal_no_ansi' => ' %message%', + + 'verbose' => ' %indicator% %message% (%elapsed:6s%)', + 'verbose_no_ansi' => ' %message% (%elapsed:6s%)', + + 'very_verbose' => ' %indicator% %message% (%elapsed:6s%, %memory:6s%)', + 'very_verbose_no_ansi' => ' %message% (%elapsed:6s%, %memory:6s%)', + ); + } +} diff --git a/vendor/symfony/console/Helper/QuestionHelper.php b/vendor/symfony/console/Helper/QuestionHelper.php new file mode 100644 index 0000000000..5bb30df8ad --- /dev/null +++ b/vendor/symfony/console/Helper/QuestionHelper.php @@ -0,0 +1,449 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Formatter\OutputFormatterStyle; +use Symfony\Component\Console\Question\Question; +use Symfony\Component\Console\Question\ChoiceQuestion; + +/** + * The QuestionHelper class provides helpers to interact with the user. + * + * @author Fabien Potencier + */ +class QuestionHelper extends Helper +{ + private $inputStream; + private static $shell; + private static $stty; + + /** + * Asks a question to the user. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + * @param Question $question The question to ask + * + * @return string The user answer + * + * @throws RuntimeException If there is no data to read in the input stream + */ + public function ask(InputInterface $input, OutputInterface $output, Question $question) + { + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + + if (!$input->isInteractive()) { + return $question->getDefault(); + } + + if (!$question->getValidator()) { + return $this->doAsk($output, $question); + } + + $that = $this; + + $interviewer = function () use ($output, $question, $that) { + return $that->doAsk($output, $question); + }; + + return $this->validateAttempts($interviewer, $output, $question); + } + + /** + * Sets the input stream to read from when interacting with the user. + * + * This is mainly useful for testing purpose. + * + * @param resource $stream The input stream + * + * @throws InvalidArgumentException In case the stream is not a resource + */ + public function setInputStream($stream) + { + if (!is_resource($stream)) { + throw new InvalidArgumentException('Input stream must be a valid resource.'); + } + + $this->inputStream = $stream; + } + + /** + * Returns the helper's input stream. + * + * @return resource + */ + public function getInputStream() + { + return $this->inputStream; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'question'; + } + + /** + * Asks the question to the user. + * + * This method is public for PHP 5.3 compatibility, it should be private. + * + * @param OutputInterface $output + * @param Question $question + * + * @return bool|mixed|null|string + * + * @throws \Exception + * @throws \RuntimeException + */ + public function doAsk(OutputInterface $output, Question $question) + { + $this->writePrompt($output, $question); + + $inputStream = $this->inputStream ?: STDIN; + $autocomplete = $question->getAutocompleterValues(); + + if (null === $autocomplete || !$this->hasSttyAvailable()) { + $ret = false; + if ($question->isHidden()) { + try { + $ret = trim($this->getHiddenResponse($output, $inputStream)); + } catch (\RuntimeException $e) { + if (!$question->isHiddenFallback()) { + throw $e; + } + } + } + + if (false === $ret) { + $ret = fgets($inputStream, 4096); + if (false === $ret) { + throw new \RuntimeException('Aborted'); + } + $ret = trim($ret); + } + } else { + $ret = trim($this->autocomplete($output, $question, $inputStream)); + } + + $ret = strlen($ret) > 0 ? $ret : $question->getDefault(); + + if ($normalizer = $question->getNormalizer()) { + return $normalizer($ret); + } + + return $ret; + } + + /** + * Outputs the question prompt. + * + * @param OutputInterface $output + * @param Question $question + */ + protected function writePrompt(OutputInterface $output, Question $question) + { + $message = $question->getQuestion(); + + if ($question instanceof ChoiceQuestion) { + $maxWidth = max(array_map(array($this, 'strlen'), array_keys($question->getChoices()))); + + $messages = (array) $question->getQuestion(); + foreach ($question->getChoices() as $key => $value) { + $width = $maxWidth - $this->strlen($key); + $messages[] = ' ['.$key.str_repeat(' ', $width).'] '.$value; + } + + $output->writeln($messages); + + $message = $question->getPrompt(); + } + + $output->write($message); + } + + /** + * Outputs an error message. + * + * @param OutputInterface $output + * @param \Exception $error + */ + protected function writeError(OutputInterface $output, \Exception $error) + { + if (null !== $this->getHelperSet() && $this->getHelperSet()->has('formatter')) { + $message = $this->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error'); + } else { + $message = ''.$error->getMessage().''; + } + + $output->writeln($message); + } + + /** + * Autocompletes a question. + * + * @param OutputInterface $output + * @param Question $question + * + * @return string + */ + private function autocomplete(OutputInterface $output, Question $question, $inputStream) + { + $autocomplete = $question->getAutocompleterValues(); + $ret = ''; + + $i = 0; + $ofs = -1; + $matches = $autocomplete; + $numMatches = count($matches); + + $sttyMode = shell_exec('stty -g'); + + // Disable icanon (so we can fread each keypress) and echo (we'll do echoing here instead) + shell_exec('stty -icanon -echo'); + + // Add highlighted text style + $output->getFormatter()->setStyle('hl', new OutputFormatterStyle('black', 'white')); + + // Read a keypress + while (!feof($inputStream)) { + $c = fread($inputStream, 1); + + // Backspace Character + if ("\177" === $c) { + if (0 === $numMatches && 0 !== $i) { + --$i; + // Move cursor backwards + $output->write("\033[1D"); + } + + if ($i === 0) { + $ofs = -1; + $matches = $autocomplete; + $numMatches = count($matches); + } else { + $numMatches = 0; + } + + // Pop the last character off the end of our string + $ret = substr($ret, 0, $i); + } elseif ("\033" === $c) { + // Did we read an escape sequence? + $c .= fread($inputStream, 2); + + // A = Up Arrow. B = Down Arrow + if (isset($c[2]) && ('A' === $c[2] || 'B' === $c[2])) { + if ('A' === $c[2] && -1 === $ofs) { + $ofs = 0; + } + + if (0 === $numMatches) { + continue; + } + + $ofs += ('A' === $c[2]) ? -1 : 1; + $ofs = ($numMatches + $ofs) % $numMatches; + } + } elseif (ord($c) < 32) { + if ("\t" === $c || "\n" === $c) { + if ($numMatches > 0 && -1 !== $ofs) { + $ret = $matches[$ofs]; + // Echo out remaining chars for current match + $output->write(substr($ret, $i)); + $i = strlen($ret); + } + + if ("\n" === $c) { + $output->write($c); + break; + } + + $numMatches = 0; + } + + continue; + } else { + $output->write($c); + $ret .= $c; + ++$i; + + $numMatches = 0; + $ofs = 0; + + foreach ($autocomplete as $value) { + // If typed characters match the beginning chunk of value (e.g. [AcmeDe]moBundle) + if (0 === strpos($value, $ret) && $i !== strlen($value)) { + $matches[$numMatches++] = $value; + } + } + } + + // Erase characters from cursor to end of line + $output->write("\033[K"); + + if ($numMatches > 0 && -1 !== $ofs) { + // Save cursor position + $output->write("\0337"); + // Write highlighted text + $output->write(''.substr($matches[$ofs], $i).''); + // Restore cursor position + $output->write("\0338"); + } + } + + // Reset stty so it behaves normally again + shell_exec(sprintf('stty %s', $sttyMode)); + + return $ret; + } + + /** + * Gets a hidden response from user. + * + * @param OutputInterface $output An Output instance + * + * @return string The answer + * + * @throws RuntimeException In case the fallback is deactivated and the response cannot be hidden + */ + private function getHiddenResponse(OutputInterface $output, $inputStream) + { + if ('\\' === DIRECTORY_SEPARATOR) { + $exe = __DIR__.'/../Resources/bin/hiddeninput.exe'; + + // handle code running from a phar + if ('phar:' === substr(__FILE__, 0, 5)) { + $tmpExe = sys_get_temp_dir().'/hiddeninput.exe'; + copy($exe, $tmpExe); + $exe = $tmpExe; + } + + $value = rtrim(shell_exec($exe)); + $output->writeln(''); + + if (isset($tmpExe)) { + unlink($tmpExe); + } + + return $value; + } + + if ($this->hasSttyAvailable()) { + $sttyMode = shell_exec('stty -g'); + + shell_exec('stty -echo'); + $value = fgets($inputStream, 4096); + shell_exec(sprintf('stty %s', $sttyMode)); + + if (false === $value) { + throw new RuntimeException('Aborted'); + } + + $value = trim($value); + $output->writeln(''); + + return $value; + } + + if (false !== $shell = $this->getShell()) { + $readCmd = $shell === 'csh' ? 'set mypassword = $<' : 'read -r mypassword'; + $command = sprintf("/usr/bin/env %s -c 'stty -echo; %s; stty echo; echo \$mypassword'", $shell, $readCmd); + $value = rtrim(shell_exec($command)); + $output->writeln(''); + + return $value; + } + + throw new RuntimeException('Unable to hide the response.'); + } + + /** + * Validates an attempt. + * + * @param callable $interviewer A callable that will ask for a question and return the result + * @param OutputInterface $output An Output instance + * @param Question $question A Question instance + * + * @return string The validated response + * + * @throws \Exception In case the max number of attempts has been reached and no valid response has been given + */ + private function validateAttempts($interviewer, OutputInterface $output, Question $question) + { + $error = null; + $attempts = $question->getMaxAttempts(); + while (null === $attempts || $attempts--) { + if (null !== $error) { + $this->writeError($output, $error); + } + + try { + return call_user_func($question->getValidator(), $interviewer()); + } catch (\Exception $error) { + } + } + + throw $error; + } + + /** + * Returns a valid unix shell. + * + * @return string|bool The valid shell name, false in case no valid shell is found + */ + private function getShell() + { + if (null !== self::$shell) { + return self::$shell; + } + + self::$shell = false; + + if (file_exists('/usr/bin/env')) { + // handle other OSs with bash/zsh/ksh/csh if available to hide the answer + $test = "/usr/bin/env %s -c 'echo OK' 2> /dev/null"; + foreach (array('bash', 'zsh', 'ksh', 'csh') as $sh) { + if ('OK' === rtrim(shell_exec(sprintf($test, $sh)))) { + self::$shell = $sh; + break; + } + } + } + + return self::$shell; + } + + /** + * Returns whether Stty is available or not. + * + * @return bool + */ + private function hasSttyAvailable() + { + if (null !== self::$stty) { + return self::$stty; + } + + exec('stty 2>&1', $output, $exitcode); + + return self::$stty = $exitcode === 0; + } +} diff --git a/vendor/symfony/console/Helper/SymfonyQuestionHelper.php b/vendor/symfony/console/Helper/SymfonyQuestionHelper.php new file mode 100644 index 0000000000..942278bdf1 --- /dev/null +++ b/vendor/symfony/console/Helper/SymfonyQuestionHelper.php @@ -0,0 +1,107 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Exception\LogicException; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\ChoiceQuestion; +use Symfony\Component\Console\Question\ConfirmationQuestion; +use Symfony\Component\Console\Question\Question; +use Symfony\Component\Console\Style\SymfonyStyle; + +/** + * Symfony Style Guide compliant question helper. + * + * @author Kevin Bond + */ +class SymfonyQuestionHelper extends QuestionHelper +{ + /** + * {@inheritdoc} + */ + public function ask(InputInterface $input, OutputInterface $output, Question $question) + { + $validator = $question->getValidator(); + $question->setValidator(function ($value) use ($validator) { + if (null !== $validator) { + $value = $validator($value); + } + + // make required + if (!is_array($value) && !is_bool($value) && 0 === strlen($value)) { + throw new LogicException('A value is required.'); + } + + return $value; + }); + + return parent::ask($input, $output, $question); + } + + /** + * {@inheritdoc} + */ + protected function writePrompt(OutputInterface $output, Question $question) + { + $text = $question->getQuestion(); + $default = $question->getDefault(); + + switch (true) { + case null === $default: + $text = sprintf(' %s:', $text); + + break; + + case $question instanceof ConfirmationQuestion: + $text = sprintf(' %s (yes/no) [%s]:', $text, $default ? 'yes' : 'no'); + + break; + + case $question instanceof ChoiceQuestion: + $choices = $question->getChoices(); + $text = sprintf(' %s [%s]:', $text, $choices[$default]); + + break; + + default: + $text = sprintf(' %s [%s]:', $text, $default); + } + + $output->writeln($text); + + if ($question instanceof ChoiceQuestion) { + $width = max(array_map('strlen', array_keys($question->getChoices()))); + + foreach ($question->getChoices() as $key => $value) { + $output->writeln(sprintf(" [%-${width}s] %s", $key, $value)); + } + } + + $output->write(' > '); + } + + /** + * {@inheritdoc} + */ + protected function writeError(OutputInterface $output, \Exception $error) + { + if ($output instanceof SymfonyStyle) { + $output->newLine(); + $output->error($error->getMessage()); + + return; + } + + parent::writeError($output, $error); + } +} diff --git a/vendor/symfony/console/Helper/Table.php b/vendor/symfony/console/Helper/Table.php new file mode 100644 index 0000000000..13e4c3cf6b --- /dev/null +++ b/vendor/symfony/console/Helper/Table.php @@ -0,0 +1,663 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * Provides helpers to display a table. + * + * @author Fabien Potencier + * @author Саша Стаменковић + * @author Abdellatif Ait boudad + * @author Max Grigorian + */ +class Table +{ + /** + * Table headers. + * + * @var array + */ + private $headers = array(); + + /** + * Table rows. + * + * @var array + */ + private $rows = array(); + + /** + * Column widths cache. + * + * @var array + */ + private $columnWidths = array(); + + /** + * Number of columns cache. + * + * @var array + */ + private $numberOfColumns; + + /** + * @var OutputInterface + */ + private $output; + + /** + * @var TableStyle + */ + private $style; + + /** + * @var array + */ + private $columnStyles = array(); + + private static $styles; + + public function __construct(OutputInterface $output) + { + $this->output = $output; + + if (!self::$styles) { + self::$styles = self::initStyles(); + } + + $this->setStyle('default'); + } + + /** + * Sets a style definition. + * + * @param string $name The style name + * @param TableStyle $style A TableStyle instance + */ + public static function setStyleDefinition($name, TableStyle $style) + { + if (!self::$styles) { + self::$styles = self::initStyles(); + } + + self::$styles[$name] = $style; + } + + /** + * Gets a style definition by name. + * + * @param string $name The style name + * + * @return TableStyle A TableStyle instance + */ + public static function getStyleDefinition($name) + { + if (!self::$styles) { + self::$styles = self::initStyles(); + } + + if (!self::$styles[$name]) { + throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name)); + } + + return self::$styles[$name]; + } + + /** + * Sets table style. + * + * @param TableStyle|string $name The style name or a TableStyle instance + * + * @return Table + */ + public function setStyle($name) + { + if ($name instanceof TableStyle) { + $this->style = $name; + } elseif (isset(self::$styles[$name])) { + $this->style = self::$styles[$name]; + } else { + throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name)); + } + + return $this; + } + + /** + * Gets the current table style. + * + * @return TableStyle + */ + public function getStyle() + { + return $this->style; + } + + /** + * Sets table column style. + * + * @param int $columnIndex Column index + * @param TableStyle|string $name The style name or a TableStyle instance + * + * @return Table + */ + public function setColumnStyle($columnIndex, $name) + { + $columnIndex = intval($columnIndex); + + if ($name instanceof TableStyle) { + $this->columnStyles[$columnIndex] = $name; + } elseif (isset(self::$styles[$name])) { + $this->columnStyles[$columnIndex] = self::$styles[$name]; + } else { + throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name)); + } + + return $this; + } + + /** + * Gets the current style for a column. + * + * If style was not set, it returns the global table style. + * + * @param int $columnIndex Column index + * + * @return TableStyle + */ + public function getColumnStyle($columnIndex) + { + if (isset($this->columnStyles[$columnIndex])) { + return $this->columnStyles[$columnIndex]; + } + + return $this->getStyle(); + } + + public function setHeaders(array $headers) + { + $headers = array_values($headers); + if (!empty($headers) && !is_array($headers[0])) { + $headers = array($headers); + } + + $this->headers = $headers; + + return $this; + } + + public function setRows(array $rows) + { + $this->rows = array(); + + return $this->addRows($rows); + } + + public function addRows(array $rows) + { + foreach ($rows as $row) { + $this->addRow($row); + } + + return $this; + } + + public function addRow($row) + { + if ($row instanceof TableSeparator) { + $this->rows[] = $row; + + return $this; + } + + if (!is_array($row)) { + throw new InvalidArgumentException('A row must be an array or a TableSeparator instance.'); + } + + $this->rows[] = array_values($row); + + return $this; + } + + public function setRow($column, array $row) + { + $this->rows[$column] = $row; + + return $this; + } + + /** + * Renders table to output. + * + * Example: + * +---------------+-----------------------+------------------+ + * | ISBN | Title | Author | + * +---------------+-----------------------+------------------+ + * | 99921-58-10-7 | Divine Comedy | Dante Alighieri | + * | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | + * | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien | + * +---------------+-----------------------+------------------+ + */ + public function render() + { + $this->calculateNumberOfColumns(); + $rows = $this->buildTableRows($this->rows); + $headers = $this->buildTableRows($this->headers); + + $this->calculateColumnsWidth(array_merge($headers, $rows)); + + $this->renderRowSeparator(); + if (!empty($headers)) { + foreach ($headers as $header) { + $this->renderRow($header, $this->style->getCellHeaderFormat()); + $this->renderRowSeparator(); + } + } + foreach ($rows as $row) { + if ($row instanceof TableSeparator) { + $this->renderRowSeparator(); + } else { + $this->renderRow($row, $this->style->getCellRowFormat()); + } + } + if (!empty($rows)) { + $this->renderRowSeparator(); + } + + $this->cleanup(); + } + + /** + * Renders horizontal header separator. + * + * Example: +-----+-----------+-------+ + */ + private function renderRowSeparator() + { + if (0 === $count = $this->numberOfColumns) { + return; + } + + if (!$this->style->getHorizontalBorderChar() && !$this->style->getCrossingChar()) { + return; + } + + $markup = $this->style->getCrossingChar(); + for ($column = 0; $column < $count; ++$column) { + $markup .= str_repeat($this->style->getHorizontalBorderChar(), $this->columnWidths[$column]).$this->style->getCrossingChar(); + } + + $this->output->writeln(sprintf($this->style->getBorderFormat(), $markup)); + } + + /** + * Renders vertical column separator. + */ + private function renderColumnSeparator() + { + return sprintf($this->style->getBorderFormat(), $this->style->getVerticalBorderChar()); + } + + /** + * Renders table row. + * + * Example: | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | + * + * @param array $row + * @param string $cellFormat + */ + private function renderRow(array $row, $cellFormat) + { + if (empty($row)) { + return; + } + + $rowContent = $this->renderColumnSeparator(); + foreach ($this->getRowColumns($row) as $column) { + $rowContent .= $this->renderCell($row, $column, $cellFormat); + $rowContent .= $this->renderColumnSeparator(); + } + $this->output->writeln($rowContent); + } + + /** + * Renders table cell with padding. + * + * @param array $row + * @param int $column + * @param string $cellFormat + */ + private function renderCell(array $row, $column, $cellFormat) + { + $cell = isset($row[$column]) ? $row[$column] : ''; + $width = $this->columnWidths[$column]; + if ($cell instanceof TableCell && $cell->getColspan() > 1) { + // add the width of the following columns(numbers of colspan). + foreach (range($column + 1, $column + $cell->getColspan() - 1) as $nextColumn) { + $width += $this->getColumnSeparatorWidth() + $this->columnWidths[$nextColumn]; + } + } + + // str_pad won't work properly with multi-byte strings, we need to fix the padding + if (false !== $encoding = mb_detect_encoding($cell, null, true)) { + $width += strlen($cell) - mb_strwidth($cell, $encoding); + } + + $style = $this->getColumnStyle($column); + + if ($cell instanceof TableSeparator) { + return sprintf($style->getBorderFormat(), str_repeat($style->getHorizontalBorderChar(), $width)); + } + + $width += Helper::strlen($cell) - Helper::strlenWithoutDecoration($this->output->getFormatter(), $cell); + $content = sprintf($style->getCellRowContentFormat(), $cell); + + return sprintf($cellFormat, str_pad($content, $width, $style->getPaddingChar(), $style->getPadType())); + } + + /** + * Calculate number of columns for this table. + */ + private function calculateNumberOfColumns() + { + if (null !== $this->numberOfColumns) { + return; + } + + $columns = array(0); + foreach (array_merge($this->headers, $this->rows) as $row) { + if ($row instanceof TableSeparator) { + continue; + } + + $columns[] = $this->getNumberOfColumns($row); + } + + $this->numberOfColumns = max($columns); + } + + private function buildTableRows($rows) + { + $unmergedRows = array(); + for ($rowKey = 0; $rowKey < count($rows); ++$rowKey) { + $rows = $this->fillNextRows($rows, $rowKey); + + // Remove any new line breaks and replace it with a new line + foreach ($rows[$rowKey] as $column => $cell) { + if (!strstr($cell, "\n")) { + continue; + } + $lines = explode("\n", $cell); + foreach ($lines as $lineKey => $line) { + if ($cell instanceof TableCell) { + $line = new TableCell($line, array('colspan' => $cell->getColspan())); + } + if (0 === $lineKey) { + $rows[$rowKey][$column] = $line; + } else { + $unmergedRows[$rowKey][$lineKey][$column] = $line; + } + } + } + } + + $tableRows = array(); + foreach ($rows as $rowKey => $row) { + $tableRows[] = $this->fillCells($row); + if (isset($unmergedRows[$rowKey])) { + $tableRows = array_merge($tableRows, $unmergedRows[$rowKey]); + } + } + + return $tableRows; + } + + /** + * fill rows that contains rowspan > 1. + * + * @param array $rows + * @param int $line + * + * @return array + */ + private function fillNextRows($rows, $line) + { + $unmergedRows = array(); + foreach ($rows[$line] as $column => $cell) { + if ($cell instanceof TableCell && $cell->getRowspan() > 1) { + $nbLines = $cell->getRowspan() - 1; + $lines = array($cell); + if (strstr($cell, "\n")) { + $lines = explode("\n", $cell); + $nbLines = count($lines) > $nbLines ? substr_count($cell, "\n") : $nbLines; + + $rows[$line][$column] = new TableCell($lines[0], array('colspan' => $cell->getColspan())); + unset($lines[0]); + } + + // create a two dimensional array (rowspan x colspan) + $unmergedRows = array_replace_recursive(array_fill($line + 1, $nbLines, ''), $unmergedRows); + foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) { + $value = isset($lines[$unmergedRowKey - $line]) ? $lines[$unmergedRowKey - $line] : ''; + $unmergedRows[$unmergedRowKey][$column] = new TableCell($value, array('colspan' => $cell->getColspan())); + } + } + } + + foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) { + // we need to know if $unmergedRow will be merged or inserted into $rows + if (isset($rows[$unmergedRowKey]) && is_array($rows[$unmergedRowKey]) && ($this->getNumberOfColumns($rows[$unmergedRowKey]) + $this->getNumberOfColumns($unmergedRows[$unmergedRowKey]) <= $this->numberOfColumns)) { + foreach ($unmergedRow as $cellKey => $cell) { + // insert cell into row at cellKey position + array_splice($rows[$unmergedRowKey], $cellKey, 0, array($cell)); + } + } else { + $row = $this->copyRow($rows, $unmergedRowKey - 1); + foreach ($unmergedRow as $column => $cell) { + if (!empty($cell)) { + $row[$column] = $unmergedRow[$column]; + } + } + array_splice($rows, $unmergedRowKey, 0, array($row)); + } + } + + return $rows; + } + + /** + * fill cells for a row that contains colspan > 1. + * + * @param array $row + * + * @return array + */ + private function fillCells($row) + { + $newRow = array(); + foreach ($row as $column => $cell) { + $newRow[] = $cell; + if ($cell instanceof TableCell && $cell->getColspan() > 1) { + foreach (range($column + 1, $column + $cell->getColspan() - 1) as $position) { + // insert empty value at column position + $newRow[] = ''; + } + } + } + + return $newRow ?: $row; + } + + /** + * @param array $rows + * @param int $line + * + * @return array + */ + private function copyRow($rows, $line) + { + $row = $rows[$line]; + foreach ($row as $cellKey => $cellValue) { + $row[$cellKey] = ''; + if ($cellValue instanceof TableCell) { + $row[$cellKey] = new TableCell('', array('colspan' => $cellValue->getColspan())); + } + } + + return $row; + } + + /** + * Gets number of columns by row. + * + * @param array $row + * + * @return int + */ + private function getNumberOfColumns(array $row) + { + $columns = count($row); + foreach ($row as $column) { + $columns += $column instanceof TableCell ? ($column->getColspan() - 1) : 0; + } + + return $columns; + } + + /** + * Gets list of columns for the given row. + * + * @param array $row + * + * @return array + */ + private function getRowColumns($row) + { + $columns = range(0, $this->numberOfColumns - 1); + foreach ($row as $cellKey => $cell) { + if ($cell instanceof TableCell && $cell->getColspan() > 1) { + // exclude grouped columns. + $columns = array_diff($columns, range($cellKey + 1, $cellKey + $cell->getColspan() - 1)); + } + } + + return $columns; + } + + /** + * Calculates columns widths. + * + * @param array $rows + */ + private function calculateColumnsWidth($rows) + { + for ($column = 0; $column < $this->numberOfColumns; ++$column) { + $lengths = array(); + foreach ($rows as $row) { + if ($row instanceof TableSeparator) { + continue; + } + + foreach ($row as $i => $cell) { + if ($cell instanceof TableCell) { + $textLength = strlen($cell); + if ($textLength > 0) { + $contentColumns = str_split($cell, ceil($textLength / $cell->getColspan())); + foreach ($contentColumns as $position => $content) { + $row[$i + $position] = $content; + } + } + } + } + + $lengths[] = $this->getCellWidth($row, $column); + } + + $this->columnWidths[$column] = max($lengths) + strlen($this->style->getCellRowContentFormat()) - 2; + } + } + + /** + * Gets column width. + * + * @return int + */ + private function getColumnSeparatorWidth() + { + return strlen(sprintf($this->style->getBorderFormat(), $this->style->getVerticalBorderChar())); + } + + /** + * Gets cell width. + * + * @param array $row + * @param int $column + * + * @return int + */ + private function getCellWidth(array $row, $column) + { + if (isset($row[$column])) { + $cell = $row[$column]; + $cellWidth = Helper::strlenWithoutDecoration($this->output->getFormatter(), $cell); + + return $cellWidth; + } + + return 0; + } + + /** + * Called after rendering to cleanup cache data. + */ + private function cleanup() + { + $this->columnWidths = array(); + $this->numberOfColumns = null; + } + + private static function initStyles() + { + $borderless = new TableStyle(); + $borderless + ->setHorizontalBorderChar('=') + ->setVerticalBorderChar(' ') + ->setCrossingChar(' ') + ; + + $compact = new TableStyle(); + $compact + ->setHorizontalBorderChar('') + ->setVerticalBorderChar(' ') + ->setCrossingChar('') + ->setCellRowContentFormat('%s') + ; + + $styleGuide = new TableStyle(); + $styleGuide + ->setHorizontalBorderChar('-') + ->setVerticalBorderChar(' ') + ->setCrossingChar(' ') + ->setCellHeaderFormat('%s') + ; + + return array( + 'default' => new TableStyle(), + 'borderless' => $borderless, + 'compact' => $compact, + 'symfony-style-guide' => $styleGuide, + ); + } +} diff --git a/vendor/symfony/console/Helper/TableCell.php b/vendor/symfony/console/Helper/TableCell.php new file mode 100644 index 0000000000..69442d4249 --- /dev/null +++ b/vendor/symfony/console/Helper/TableCell.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * @author Abdellatif Ait boudad + */ +class TableCell +{ + /** + * @var string + */ + private $value; + + /** + * @var array + */ + private $options = array( + 'rowspan' => 1, + 'colspan' => 1, + ); + + /** + * @param string $value + * @param array $options + */ + public function __construct($value = '', array $options = array()) + { + $this->value = $value; + + // check option names + if ($diff = array_diff(array_keys($options), array_keys($this->options))) { + throw new InvalidArgumentException(sprintf('The TableCell does not support the following options: \'%s\'.', implode('\', \'', $diff))); + } + + $this->options = array_merge($this->options, $options); + } + + /** + * Returns the cell value. + * + * @return string + */ + public function __toString() + { + return $this->value; + } + + /** + * Gets number of colspan. + * + * @return int + */ + public function getColspan() + { + return (int) $this->options['colspan']; + } + + /** + * Gets number of rowspan. + * + * @return int + */ + public function getRowspan() + { + return (int) $this->options['rowspan']; + } +} diff --git a/vendor/symfony/console/Helper/TableHelper.php b/vendor/symfony/console/Helper/TableHelper.php new file mode 100644 index 0000000000..3c7a1a7865 --- /dev/null +++ b/vendor/symfony/console/Helper/TableHelper.php @@ -0,0 +1,269 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\NullOutput; +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * Provides helpers to display table output. + * + * @author Саша Стаменковић + * @author Fabien Potencier + * + * @deprecated since version 2.5, to be removed in 3.0 + * Use {@link Table} instead. + */ +class TableHelper extends Helper +{ + const LAYOUT_DEFAULT = 0; + const LAYOUT_BORDERLESS = 1; + const LAYOUT_COMPACT = 2; + + /** + * @var Table + */ + private $table; + + public function __construct($triggerDeprecationError = true) + { + if ($triggerDeprecationError) { + @trigger_error('The '.__CLASS__.' class is deprecated since version 2.5 and will be removed in 3.0. Use the Symfony\Component\Console\Helper\Table class instead.', E_USER_DEPRECATED); + } + + $this->table = new Table(new NullOutput()); + } + + /** + * Sets table layout type. + * + * @param int $layout self::LAYOUT_* + * + * @return TableHelper + * + * @throws InvalidArgumentException when the table layout is not known + */ + public function setLayout($layout) + { + switch ($layout) { + case self::LAYOUT_BORDERLESS: + $this->table->setStyle('borderless'); + break; + + case self::LAYOUT_COMPACT: + $this->table->setStyle('compact'); + break; + + case self::LAYOUT_DEFAULT: + $this->table->setStyle('default'); + break; + + default: + throw new InvalidArgumentException(sprintf('Invalid table layout "%s".', $layout)); + } + + return $this; + } + + public function setHeaders(array $headers) + { + $this->table->setHeaders($headers); + + return $this; + } + + public function setRows(array $rows) + { + $this->table->setRows($rows); + + return $this; + } + + public function addRows(array $rows) + { + $this->table->addRows($rows); + + return $this; + } + + public function addRow(array $row) + { + $this->table->addRow($row); + + return $this; + } + + public function setRow($column, array $row) + { + $this->table->setRow($column, $row); + + return $this; + } + + /** + * Sets padding character, used for cell padding. + * + * @param string $paddingChar + * + * @return TableHelper + */ + public function setPaddingChar($paddingChar) + { + $this->table->getStyle()->setPaddingChar($paddingChar); + + return $this; + } + + /** + * Sets horizontal border character. + * + * @param string $horizontalBorderChar + * + * @return TableHelper + */ + public function setHorizontalBorderChar($horizontalBorderChar) + { + $this->table->getStyle()->setHorizontalBorderChar($horizontalBorderChar); + + return $this; + } + + /** + * Sets vertical border character. + * + * @param string $verticalBorderChar + * + * @return TableHelper + */ + public function setVerticalBorderChar($verticalBorderChar) + { + $this->table->getStyle()->setVerticalBorderChar($verticalBorderChar); + + return $this; + } + + /** + * Sets crossing character. + * + * @param string $crossingChar + * + * @return TableHelper + */ + public function setCrossingChar($crossingChar) + { + $this->table->getStyle()->setCrossingChar($crossingChar); + + return $this; + } + + /** + * Sets header cell format. + * + * @param string $cellHeaderFormat + * + * @return TableHelper + */ + public function setCellHeaderFormat($cellHeaderFormat) + { + $this->table->getStyle()->setCellHeaderFormat($cellHeaderFormat); + + return $this; + } + + /** + * Sets row cell format. + * + * @param string $cellRowFormat + * + * @return TableHelper + */ + public function setCellRowFormat($cellRowFormat) + { + $this->table->getStyle()->setCellHeaderFormat($cellRowFormat); + + return $this; + } + + /** + * Sets row cell content format. + * + * @param string $cellRowContentFormat + * + * @return TableHelper + */ + public function setCellRowContentFormat($cellRowContentFormat) + { + $this->table->getStyle()->setCellRowContentFormat($cellRowContentFormat); + + return $this; + } + + /** + * Sets table border format. + * + * @param string $borderFormat + * + * @return TableHelper + */ + public function setBorderFormat($borderFormat) + { + $this->table->getStyle()->setBorderFormat($borderFormat); + + return $this; + } + + /** + * Sets cell padding type. + * + * @param int $padType STR_PAD_* + * + * @return TableHelper + */ + public function setPadType($padType) + { + $this->table->getStyle()->setPadType($padType); + + return $this; + } + + /** + * Renders table to output. + * + * Example: + * +---------------+-----------------------+------------------+ + * | ISBN | Title | Author | + * +---------------+-----------------------+------------------+ + * | 99921-58-10-7 | Divine Comedy | Dante Alighieri | + * | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | + * | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien | + * +---------------+-----------------------+------------------+ + * + * @param OutputInterface $output + */ + public function render(OutputInterface $output) + { + $p = new \ReflectionProperty($this->table, 'output'); + $p->setAccessible(true); + $p->setValue($this->table, $output); + + $this->table->render(); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'table'; + } +} diff --git a/vendor/symfony/console/Helper/TableSeparator.php b/vendor/symfony/console/Helper/TableSeparator.php new file mode 100644 index 0000000000..8cbbc6613b --- /dev/null +++ b/vendor/symfony/console/Helper/TableSeparator.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +/** + * Marks a row as being a separator. + * + * @author Fabien Potencier + */ +class TableSeparator extends TableCell +{ + /** + * @param string $value + * @param array $options + */ + public function __construct(array $options = array()) + { + parent::__construct('', $options); + } +} diff --git a/vendor/symfony/console/Helper/TableStyle.php b/vendor/symfony/console/Helper/TableStyle.php new file mode 100644 index 0000000000..d7e28ff2b4 --- /dev/null +++ b/vendor/symfony/console/Helper/TableStyle.php @@ -0,0 +1,258 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; + +/** + * Defines the styles for a Table. + * + * @author Fabien Potencier + * @author Саша Стаменковић + */ +class TableStyle +{ + private $paddingChar = ' '; + private $horizontalBorderChar = '-'; + private $verticalBorderChar = '|'; + private $crossingChar = '+'; + private $cellHeaderFormat = '%s'; + private $cellRowFormat = '%s'; + private $cellRowContentFormat = ' %s '; + private $borderFormat = '%s'; + private $padType = STR_PAD_RIGHT; + + /** + * Sets padding character, used for cell padding. + * + * @param string $paddingChar + * + * @return TableStyle + */ + public function setPaddingChar($paddingChar) + { + if (!$paddingChar) { + throw new LogicException('The padding char must not be empty'); + } + + $this->paddingChar = $paddingChar; + + return $this; + } + + /** + * Gets padding character, used for cell padding. + * + * @return string + */ + public function getPaddingChar() + { + return $this->paddingChar; + } + + /** + * Sets horizontal border character. + * + * @param string $horizontalBorderChar + * + * @return TableStyle + */ + public function setHorizontalBorderChar($horizontalBorderChar) + { + $this->horizontalBorderChar = $horizontalBorderChar; + + return $this; + } + + /** + * Gets horizontal border character. + * + * @return string + */ + public function getHorizontalBorderChar() + { + return $this->horizontalBorderChar; + } + + /** + * Sets vertical border character. + * + * @param string $verticalBorderChar + * + * @return TableStyle + */ + public function setVerticalBorderChar($verticalBorderChar) + { + $this->verticalBorderChar = $verticalBorderChar; + + return $this; + } + + /** + * Gets vertical border character. + * + * @return string + */ + public function getVerticalBorderChar() + { + return $this->verticalBorderChar; + } + + /** + * Sets crossing character. + * + * @param string $crossingChar + * + * @return TableStyle + */ + public function setCrossingChar($crossingChar) + { + $this->crossingChar = $crossingChar; + + return $this; + } + + /** + * Gets crossing character. + * + * @return string $crossingChar + */ + public function getCrossingChar() + { + return $this->crossingChar; + } + + /** + * Sets header cell format. + * + * @param string $cellHeaderFormat + * + * @return TableStyle + */ + public function setCellHeaderFormat($cellHeaderFormat) + { + $this->cellHeaderFormat = $cellHeaderFormat; + + return $this; + } + + /** + * Gets header cell format. + * + * @return string + */ + public function getCellHeaderFormat() + { + return $this->cellHeaderFormat; + } + + /** + * Sets row cell format. + * + * @param string $cellRowFormat + * + * @return TableStyle + */ + public function setCellRowFormat($cellRowFormat) + { + $this->cellRowFormat = $cellRowFormat; + + return $this; + } + + /** + * Gets row cell format. + * + * @return string + */ + public function getCellRowFormat() + { + return $this->cellRowFormat; + } + + /** + * Sets row cell content format. + * + * @param string $cellRowContentFormat + * + * @return TableStyle + */ + public function setCellRowContentFormat($cellRowContentFormat) + { + $this->cellRowContentFormat = $cellRowContentFormat; + + return $this; + } + + /** + * Gets row cell content format. + * + * @return string + */ + public function getCellRowContentFormat() + { + return $this->cellRowContentFormat; + } + + /** + * Sets table border format. + * + * @param string $borderFormat + * + * @return TableStyle + */ + public function setBorderFormat($borderFormat) + { + $this->borderFormat = $borderFormat; + + return $this; + } + + /** + * Gets table border format. + * + * @return string + */ + public function getBorderFormat() + { + return $this->borderFormat; + } + + /** + * Sets cell padding type. + * + * @param int $padType STR_PAD_* + * + * @return TableStyle + */ + public function setPadType($padType) + { + if (!in_array($padType, array(STR_PAD_LEFT, STR_PAD_RIGHT, STR_PAD_BOTH), true)) { + throw new InvalidArgumentException('Invalid padding type. Expected one of (STR_PAD_LEFT, STR_PAD_RIGHT, STR_PAD_BOTH).'); + } + + $this->padType = $padType; + + return $this; + } + + /** + * Gets cell padding type. + * + * @return int + */ + public function getPadType() + { + return $this->padType; + } +} diff --git a/vendor/symfony/console/Input/ArgvInput.php b/vendor/symfony/console/Input/ArgvInput.php new file mode 100644 index 0000000000..02d4cdb388 --- /dev/null +++ b/vendor/symfony/console/Input/ArgvInput.php @@ -0,0 +1,351 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\RuntimeException; + +/** + * ArgvInput represents an input coming from the CLI arguments. + * + * Usage: + * + * $input = new ArgvInput(); + * + * By default, the `$_SERVER['argv']` array is used for the input values. + * + * This can be overridden by explicitly passing the input values in the constructor: + * + * $input = new ArgvInput($_SERVER['argv']); + * + * If you pass it yourself, don't forget that the first element of the array + * is the name of the running application. + * + * When passing an argument to the constructor, be sure that it respects + * the same rules as the argv one. It's almost always better to use the + * `StringInput` when you want to provide your own input. + * + * @author Fabien Potencier + * + * @see http://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html + * @see http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html#tag_12_02 + */ +class ArgvInput extends Input +{ + private $tokens; + private $parsed; + + /** + * Constructor. + * + * @param array $argv An array of parameters from the CLI (in the argv format) + * @param InputDefinition $definition A InputDefinition instance + */ + public function __construct(array $argv = null, InputDefinition $definition = null) + { + if (null === $argv) { + $argv = $_SERVER['argv']; + } + + // strip the application name + array_shift($argv); + + $this->tokens = $argv; + + parent::__construct($definition); + } + + protected function setTokens(array $tokens) + { + $this->tokens = $tokens; + } + + /** + * Processes command line arguments. + */ + protected function parse() + { + $parseOptions = true; + $this->parsed = $this->tokens; + while (null !== $token = array_shift($this->parsed)) { + if ($parseOptions && '' == $token) { + $this->parseArgument($token); + } elseif ($parseOptions && '--' == $token) { + $parseOptions = false; + } elseif ($parseOptions && 0 === strpos($token, '--')) { + $this->parseLongOption($token); + } elseif ($parseOptions && '-' === $token[0] && '-' !== $token) { + $this->parseShortOption($token); + } else { + $this->parseArgument($token); + } + } + } + + /** + * Parses a short option. + * + * @param string $token The current token. + */ + private function parseShortOption($token) + { + $name = substr($token, 1); + + if (strlen($name) > 1) { + if ($this->definition->hasShortcut($name[0]) && $this->definition->getOptionForShortcut($name[0])->acceptValue()) { + // an option with a value (with no space) + $this->addShortOption($name[0], substr($name, 1)); + } else { + $this->parseShortOptionSet($name); + } + } else { + $this->addShortOption($name, null); + } + } + + /** + * Parses a short option set. + * + * @param string $name The current token + * + * @throws RuntimeException When option given doesn't exist + */ + private function parseShortOptionSet($name) + { + $len = strlen($name); + for ($i = 0; $i < $len; ++$i) { + if (!$this->definition->hasShortcut($name[$i])) { + throw new RuntimeException(sprintf('The "-%s" option does not exist.', $name[$i])); + } + + $option = $this->definition->getOptionForShortcut($name[$i]); + if ($option->acceptValue()) { + $this->addLongOption($option->getName(), $i === $len - 1 ? null : substr($name, $i + 1)); + + break; + } else { + $this->addLongOption($option->getName(), null); + } + } + } + + /** + * Parses a long option. + * + * @param string $token The current token + */ + private function parseLongOption($token) + { + $name = substr($token, 2); + + if (false !== $pos = strpos($name, '=')) { + $this->addLongOption(substr($name, 0, $pos), substr($name, $pos + 1)); + } else { + $this->addLongOption($name, null); + } + } + + /** + * Parses an argument. + * + * @param string $token The current token + * + * @throws RuntimeException When too many arguments are given + */ + private function parseArgument($token) + { + $c = count($this->arguments); + + // if input is expecting another argument, add it + if ($this->definition->hasArgument($c)) { + $arg = $this->definition->getArgument($c); + $this->arguments[$arg->getName()] = $arg->isArray() ? array($token) : $token; + + // if last argument isArray(), append token to last argument + } elseif ($this->definition->hasArgument($c - 1) && $this->definition->getArgument($c - 1)->isArray()) { + $arg = $this->definition->getArgument($c - 1); + $this->arguments[$arg->getName()][] = $token; + + // unexpected argument + } else { + throw new RuntimeException('Too many arguments.'); + } + } + + /** + * Adds a short option value. + * + * @param string $shortcut The short option key + * @param mixed $value The value for the option + * + * @throws RuntimeException When option given doesn't exist + */ + private function addShortOption($shortcut, $value) + { + if (!$this->definition->hasShortcut($shortcut)) { + throw new RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut)); + } + + $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value); + } + + /** + * Adds a long option value. + * + * @param string $name The long option key + * @param mixed $value The value for the option + * + * @throws RuntimeException When option given doesn't exist + */ + private function addLongOption($name, $value) + { + if (!$this->definition->hasOption($name)) { + throw new RuntimeException(sprintf('The "--%s" option does not exist.', $name)); + } + + $option = $this->definition->getOption($name); + + // Convert empty values to null + if (!isset($value[0])) { + $value = null; + } + + if (null !== $value && !$option->acceptValue()) { + throw new RuntimeException(sprintf('The "--%s" option does not accept a value.', $name)); + } + + if (null === $value && $option->acceptValue() && count($this->parsed)) { + // if option accepts an optional or mandatory argument + // let's see if there is one provided + $next = array_shift($this->parsed); + if (isset($next[0]) && '-' !== $next[0]) { + $value = $next; + } elseif (empty($next)) { + $value = ''; + } else { + array_unshift($this->parsed, $next); + } + } + + if (null === $value) { + if ($option->isValueRequired()) { + throw new RuntimeException(sprintf('The "--%s" option requires a value.', $name)); + } + + if (!$option->isArray()) { + $value = $option->isValueOptional() ? $option->getDefault() : true; + } + } + + if ($option->isArray()) { + $this->options[$name][] = $value; + } else { + $this->options[$name] = $value; + } + } + + /** + * Returns the first argument from the raw parameters (not parsed). + * + * @return string The value of the first argument or null otherwise + */ + public function getFirstArgument() + { + foreach ($this->tokens as $token) { + if ($token && '-' === $token[0]) { + continue; + } + + return $token; + } + } + + /** + * Returns true if the raw parameters (not parsed) contain a value. + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The value(s) to look for in the raw parameters (can be an array) + * + * @return bool true if the value is contained in the raw parameters + */ + public function hasParameterOption($values) + { + $values = (array) $values; + + foreach ($this->tokens as $token) { + foreach ($values as $value) { + if ($token === $value || 0 === strpos($token, $value.'=')) { + return true; + } + } + } + + return false; + } + + /** + * Returns the value of a raw option (not parsed). + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The value(s) to look for in the raw parameters (can be an array) + * @param mixed $default The default value to return if no result is found + * + * @return mixed The option value + */ + public function getParameterOption($values, $default = false) + { + $values = (array) $values; + $tokens = $this->tokens; + + while (0 < count($tokens)) { + $token = array_shift($tokens); + + foreach ($values as $value) { + if ($token === $value || 0 === strpos($token, $value.'=')) { + if (false !== $pos = strpos($token, '=')) { + return substr($token, $pos + 1); + } + + return array_shift($tokens); + } + } + } + + return $default; + } + + /** + * Returns a stringified representation of the args passed to the command. + * + * @return string + */ + public function __toString() + { + $self = $this; + $tokens = array_map(function ($token) use ($self) { + if (preg_match('{^(-[^=]+=)(.+)}', $token, $match)) { + return $match[1].$self->escapeToken($match[2]); + } + + if ($token && $token[0] !== '-') { + return $self->escapeToken($token); + } + + return $token; + }, $this->tokens); + + return implode(' ', $tokens); + } +} diff --git a/vendor/symfony/console/Input/ArrayInput.php b/vendor/symfony/console/Input/ArrayInput.php new file mode 100644 index 0000000000..8cedbb37ec --- /dev/null +++ b/vendor/symfony/console/Input/ArrayInput.php @@ -0,0 +1,210 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\InvalidOptionException; + +/** + * ArrayInput represents an input provided as an array. + * + * Usage: + * + * $input = new ArrayInput(array('name' => 'foo', '--bar' => 'foobar')); + * + * @author Fabien Potencier + */ +class ArrayInput extends Input +{ + private $parameters; + + /** + * Constructor. + * + * @param array $parameters An array of parameters + * @param InputDefinition $definition A InputDefinition instance + */ + public function __construct(array $parameters, InputDefinition $definition = null) + { + $this->parameters = $parameters; + + parent::__construct($definition); + } + + /** + * Returns the first argument from the raw parameters (not parsed). + * + * @return string The value of the first argument or null otherwise + */ + public function getFirstArgument() + { + foreach ($this->parameters as $key => $value) { + if ($key && '-' === $key[0]) { + continue; + } + + return $value; + } + } + + /** + * Returns true if the raw parameters (not parsed) contain a value. + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The values to look for in the raw parameters (can be an array) + * + * @return bool true if the value is contained in the raw parameters + */ + public function hasParameterOption($values) + { + $values = (array) $values; + + foreach ($this->parameters as $k => $v) { + if (!is_int($k)) { + $v = $k; + } + + if (in_array($v, $values)) { + return true; + } + } + + return false; + } + + /** + * Returns the value of a raw option (not parsed). + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The value(s) to look for in the raw parameters (can be an array) + * @param mixed $default The default value to return if no result is found + * + * @return mixed The option value + */ + public function getParameterOption($values, $default = false) + { + $values = (array) $values; + + foreach ($this->parameters as $k => $v) { + if (is_int($k)) { + if (in_array($v, $values)) { + return true; + } + } elseif (in_array($k, $values)) { + return $v; + } + } + + return $default; + } + + /** + * Returns a stringified representation of the args passed to the command. + * + * @return string + */ + public function __toString() + { + $params = array(); + foreach ($this->parameters as $param => $val) { + if ($param && '-' === $param[0]) { + $params[] = $param.('' != $val ? '='.$this->escapeToken($val) : ''); + } else { + $params[] = $this->escapeToken($val); + } + } + + return implode(' ', $params); + } + + /** + * Processes command line arguments. + */ + protected function parse() + { + foreach ($this->parameters as $key => $value) { + if (0 === strpos($key, '--')) { + $this->addLongOption(substr($key, 2), $value); + } elseif ('-' === $key[0]) { + $this->addShortOption(substr($key, 1), $value); + } else { + $this->addArgument($key, $value); + } + } + } + + /** + * Adds a short option value. + * + * @param string $shortcut The short option key + * @param mixed $value The value for the option + * + * @throws InvalidOptionException When option given doesn't exist + */ + private function addShortOption($shortcut, $value) + { + if (!$this->definition->hasShortcut($shortcut)) { + throw new InvalidOptionException(sprintf('The "-%s" option does not exist.', $shortcut)); + } + + $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value); + } + + /** + * Adds a long option value. + * + * @param string $name The long option key + * @param mixed $value The value for the option + * + * @throws InvalidOptionException When option given doesn't exist + * @throws InvalidOptionException When a required value is missing + */ + private function addLongOption($name, $value) + { + if (!$this->definition->hasOption($name)) { + throw new InvalidOptionException(sprintf('The "--%s" option does not exist.', $name)); + } + + $option = $this->definition->getOption($name); + + if (null === $value) { + if ($option->isValueRequired()) { + throw new InvalidOptionException(sprintf('The "--%s" option requires a value.', $name)); + } + + $value = $option->isValueOptional() ? $option->getDefault() : true; + } + + $this->options[$name] = $value; + } + + /** + * Adds an argument value. + * + * @param string $name The argument name + * @param mixed $value The value for the argument + * + * @throws InvalidArgumentException When argument given doesn't exist + */ + private function addArgument($name, $value) + { + if (!$this->definition->hasArgument($name)) { + throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + $this->arguments[$name] = $value; + } +} diff --git a/vendor/symfony/console/Input/Input.php b/vendor/symfony/console/Input/Input.php new file mode 100644 index 0000000000..85499fc489 --- /dev/null +++ b/vendor/symfony/console/Input/Input.php @@ -0,0 +1,236 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\RuntimeException; + +/** + * Input is the base class for all concrete Input classes. + * + * Three concrete classes are provided by default: + * + * * `ArgvInput`: The input comes from the CLI arguments (argv) + * * `StringInput`: The input is provided as a string + * * `ArrayInput`: The input is provided as an array + * + * @author Fabien Potencier + */ +abstract class Input implements InputInterface +{ + /** + * @var InputDefinition + */ + protected $definition; + protected $options = array(); + protected $arguments = array(); + protected $interactive = true; + + /** + * Constructor. + * + * @param InputDefinition $definition A InputDefinition instance + */ + public function __construct(InputDefinition $definition = null) + { + if (null === $definition) { + $this->definition = new InputDefinition(); + } else { + $this->bind($definition); + $this->validate(); + } + } + + /** + * Binds the current Input instance with the given arguments and options. + * + * @param InputDefinition $definition A InputDefinition instance + */ + public function bind(InputDefinition $definition) + { + $this->arguments = array(); + $this->options = array(); + $this->definition = $definition; + + $this->parse(); + } + + /** + * Processes command line arguments. + */ + abstract protected function parse(); + + /** + * Validates the input. + * + * @throws RuntimeException When not enough arguments are given + */ + public function validate() + { + $definition = $this->definition; + $givenArguments = $this->arguments; + + $missingArguments = array_filter(array_keys($definition->getArguments()), function ($argument) use ($definition, $givenArguments) { + return !array_key_exists($argument, $givenArguments) && $definition->getArgument($argument)->isRequired(); + }); + + if (count($missingArguments) > 0) { + throw new RuntimeException(sprintf('Not enough arguments (missing: "%s").', implode(', ', $missingArguments))); + } + } + + /** + * Checks if the input is interactive. + * + * @return bool Returns true if the input is interactive + */ + public function isInteractive() + { + return $this->interactive; + } + + /** + * Sets the input interactivity. + * + * @param bool $interactive If the input should be interactive + */ + public function setInteractive($interactive) + { + $this->interactive = (bool) $interactive; + } + + /** + * Returns the argument values. + * + * @return array An array of argument values + */ + public function getArguments() + { + return array_merge($this->definition->getArgumentDefaults(), $this->arguments); + } + + /** + * Returns the argument value for a given argument name. + * + * @param string $name The argument name + * + * @return mixed The argument value + * + * @throws InvalidArgumentException When argument given doesn't exist + */ + public function getArgument($name) + { + if (!$this->definition->hasArgument($name)) { + throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + return isset($this->arguments[$name]) ? $this->arguments[$name] : $this->definition->getArgument($name)->getDefault(); + } + + /** + * Sets an argument value by name. + * + * @param string $name The argument name + * @param string $value The argument value + * + * @throws InvalidArgumentException When argument given doesn't exist + */ + public function setArgument($name, $value) + { + if (!$this->definition->hasArgument($name)) { + throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + $this->arguments[$name] = $value; + } + + /** + * Returns true if an InputArgument object exists by name or position. + * + * @param string|int $name The InputArgument name or position + * + * @return bool true if the InputArgument object exists, false otherwise + */ + public function hasArgument($name) + { + return $this->definition->hasArgument($name); + } + + /** + * Returns the options values. + * + * @return array An array of option values + */ + public function getOptions() + { + return array_merge($this->definition->getOptionDefaults(), $this->options); + } + + /** + * Returns the option value for a given option name. + * + * @param string $name The option name + * + * @return mixed The option value + * + * @throws InvalidArgumentException When option given doesn't exist + */ + public function getOption($name) + { + if (!$this->definition->hasOption($name)) { + throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name)); + } + + return isset($this->options[$name]) ? $this->options[$name] : $this->definition->getOption($name)->getDefault(); + } + + /** + * Sets an option value by name. + * + * @param string $name The option name + * @param string|bool $value The option value + * + * @throws InvalidArgumentException When option given doesn't exist + */ + public function setOption($name, $value) + { + if (!$this->definition->hasOption($name)) { + throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name)); + } + + $this->options[$name] = $value; + } + + /** + * Returns true if an InputOption object exists by name. + * + * @param string $name The InputOption name + * + * @return bool true if the InputOption object exists, false otherwise + */ + public function hasOption($name) + { + return $this->definition->hasOption($name); + } + + /** + * Escapes a token through escapeshellarg if it contains unsafe chars. + * + * @param string $token + * + * @return string + */ + public function escapeToken($token) + { + return preg_match('{^[\w-]+$}', $token) ? $token : escapeshellarg($token); + } +} diff --git a/vendor/symfony/console/Input/InputArgument.php b/vendor/symfony/console/Input/InputArgument.php new file mode 100644 index 0000000000..048ee4ff6b --- /dev/null +++ b/vendor/symfony/console/Input/InputArgument.php @@ -0,0 +1,131 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; + +/** + * Represents a command line argument. + * + * @author Fabien Potencier + */ +class InputArgument +{ + const REQUIRED = 1; + const OPTIONAL = 2; + const IS_ARRAY = 4; + + private $name; + private $mode; + private $default; + private $description; + + /** + * Constructor. + * + * @param string $name The argument name + * @param int $mode The argument mode: self::REQUIRED or self::OPTIONAL + * @param string $description A description text + * @param mixed $default The default value (for self::OPTIONAL mode only) + * + * @throws InvalidArgumentException When argument mode is not valid + */ + public function __construct($name, $mode = null, $description = '', $default = null) + { + if (null === $mode) { + $mode = self::OPTIONAL; + } elseif (!is_int($mode) || $mode > 7 || $mode < 1) { + throw new InvalidArgumentException(sprintf('Argument mode "%s" is not valid.', $mode)); + } + + $this->name = $name; + $this->mode = $mode; + $this->description = $description; + + $this->setDefault($default); + } + + /** + * Returns the argument name. + * + * @return string The argument name + */ + public function getName() + { + return $this->name; + } + + /** + * Returns true if the argument is required. + * + * @return bool true if parameter mode is self::REQUIRED, false otherwise + */ + public function isRequired() + { + return self::REQUIRED === (self::REQUIRED & $this->mode); + } + + /** + * Returns true if the argument can take multiple values. + * + * @return bool true if mode is self::IS_ARRAY, false otherwise + */ + public function isArray() + { + return self::IS_ARRAY === (self::IS_ARRAY & $this->mode); + } + + /** + * Sets the default value. + * + * @param mixed $default The default value + * + * @throws LogicException When incorrect default value is given + */ + public function setDefault($default = null) + { + if (self::REQUIRED === $this->mode && null !== $default) { + throw new LogicException('Cannot set a default value except for InputArgument::OPTIONAL mode.'); + } + + if ($this->isArray()) { + if (null === $default) { + $default = array(); + } elseif (!is_array($default)) { + throw new LogicException('A default value for an array argument must be an array.'); + } + } + + $this->default = $default; + } + + /** + * Returns the default value. + * + * @return mixed The default value + */ + public function getDefault() + { + return $this->default; + } + + /** + * Returns the description text. + * + * @return string The description text + */ + public function getDescription() + { + return $this->description; + } +} diff --git a/vendor/symfony/console/Input/InputAwareInterface.php b/vendor/symfony/console/Input/InputAwareInterface.php new file mode 100644 index 0000000000..d0f11e986a --- /dev/null +++ b/vendor/symfony/console/Input/InputAwareInterface.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +/** + * InputAwareInterface should be implemented by classes that depends on the + * Console Input. + * + * @author Wouter J + */ +interface InputAwareInterface +{ + /** + * Sets the Console Input. + * + * @param InputInterface + */ + public function setInput(InputInterface $input); +} diff --git a/vendor/symfony/console/Input/InputDefinition.php b/vendor/symfony/console/Input/InputDefinition.php new file mode 100644 index 0000000000..bd64163b9f --- /dev/null +++ b/vendor/symfony/console/Input/InputDefinition.php @@ -0,0 +1,457 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Descriptor\TextDescriptor; +use Symfony\Component\Console\Descriptor\XmlDescriptor; +use Symfony\Component\Console\Output\BufferedOutput; +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; + +/** + * A InputDefinition represents a set of valid command line arguments and options. + * + * Usage: + * + * $definition = new InputDefinition(array( + * new InputArgument('name', InputArgument::REQUIRED), + * new InputOption('foo', 'f', InputOption::VALUE_REQUIRED), + * )); + * + * @author Fabien Potencier + */ +class InputDefinition +{ + private $arguments; + private $requiredCount; + private $hasAnArrayArgument = false; + private $hasOptional; + private $options; + private $shortcuts; + + /** + * Constructor. + * + * @param array $definition An array of InputArgument and InputOption instance + */ + public function __construct(array $definition = array()) + { + $this->setDefinition($definition); + } + + /** + * Sets the definition of the input. + * + * @param array $definition The definition array + */ + public function setDefinition(array $definition) + { + $arguments = array(); + $options = array(); + foreach ($definition as $item) { + if ($item instanceof InputOption) { + $options[] = $item; + } else { + $arguments[] = $item; + } + } + + $this->setArguments($arguments); + $this->setOptions($options); + } + + /** + * Sets the InputArgument objects. + * + * @param InputArgument[] $arguments An array of InputArgument objects + */ + public function setArguments($arguments = array()) + { + $this->arguments = array(); + $this->requiredCount = 0; + $this->hasOptional = false; + $this->hasAnArrayArgument = false; + $this->addArguments($arguments); + } + + /** + * Adds an array of InputArgument objects. + * + * @param InputArgument[] $arguments An array of InputArgument objects + */ + public function addArguments($arguments = array()) + { + if (null !== $arguments) { + foreach ($arguments as $argument) { + $this->addArgument($argument); + } + } + } + + /** + * Adds an InputArgument object. + * + * @param InputArgument $argument An InputArgument object + * + * @throws LogicException When incorrect argument is given + */ + public function addArgument(InputArgument $argument) + { + if (isset($this->arguments[$argument->getName()])) { + throw new LogicException(sprintf('An argument with name "%s" already exists.', $argument->getName())); + } + + if ($this->hasAnArrayArgument) { + throw new LogicException('Cannot add an argument after an array argument.'); + } + + if ($argument->isRequired() && $this->hasOptional) { + throw new LogicException('Cannot add a required argument after an optional one.'); + } + + if ($argument->isArray()) { + $this->hasAnArrayArgument = true; + } + + if ($argument->isRequired()) { + ++$this->requiredCount; + } else { + $this->hasOptional = true; + } + + $this->arguments[$argument->getName()] = $argument; + } + + /** + * Returns an InputArgument by name or by position. + * + * @param string|int $name The InputArgument name or position + * + * @return InputArgument An InputArgument object + * + * @throws InvalidArgumentException When argument given doesn't exist + */ + public function getArgument($name) + { + if (!$this->hasArgument($name)) { + throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + $arguments = is_int($name) ? array_values($this->arguments) : $this->arguments; + + return $arguments[$name]; + } + + /** + * Returns true if an InputArgument object exists by name or position. + * + * @param string|int $name The InputArgument name or position + * + * @return bool true if the InputArgument object exists, false otherwise + */ + public function hasArgument($name) + { + $arguments = is_int($name) ? array_values($this->arguments) : $this->arguments; + + return isset($arguments[$name]); + } + + /** + * Gets the array of InputArgument objects. + * + * @return InputArgument[] An array of InputArgument objects + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * Returns the number of InputArguments. + * + * @return int The number of InputArguments + */ + public function getArgumentCount() + { + return $this->hasAnArrayArgument ? PHP_INT_MAX : count($this->arguments); + } + + /** + * Returns the number of required InputArguments. + * + * @return int The number of required InputArguments + */ + public function getArgumentRequiredCount() + { + return $this->requiredCount; + } + + /** + * Gets the default values. + * + * @return array An array of default values + */ + public function getArgumentDefaults() + { + $values = array(); + foreach ($this->arguments as $argument) { + $values[$argument->getName()] = $argument->getDefault(); + } + + return $values; + } + + /** + * Sets the InputOption objects. + * + * @param InputOption[] $options An array of InputOption objects + */ + public function setOptions($options = array()) + { + $this->options = array(); + $this->shortcuts = array(); + $this->addOptions($options); + } + + /** + * Adds an array of InputOption objects. + * + * @param InputOption[] $options An array of InputOption objects + */ + public function addOptions($options = array()) + { + foreach ($options as $option) { + $this->addOption($option); + } + } + + /** + * Adds an InputOption object. + * + * @param InputOption $option An InputOption object + * + * @throws LogicException When option given already exist + */ + public function addOption(InputOption $option) + { + if (isset($this->options[$option->getName()]) && !$option->equals($this->options[$option->getName()])) { + throw new LogicException(sprintf('An option named "%s" already exists.', $option->getName())); + } + + if ($option->getShortcut()) { + foreach (explode('|', $option->getShortcut()) as $shortcut) { + if (isset($this->shortcuts[$shortcut]) && !$option->equals($this->options[$this->shortcuts[$shortcut]])) { + throw new LogicException(sprintf('An option with shortcut "%s" already exists.', $shortcut)); + } + } + } + + $this->options[$option->getName()] = $option; + if ($option->getShortcut()) { + foreach (explode('|', $option->getShortcut()) as $shortcut) { + $this->shortcuts[$shortcut] = $option->getName(); + } + } + } + + /** + * Returns an InputOption by name. + * + * @param string $name The InputOption name + * + * @return InputOption A InputOption object + * + * @throws InvalidArgumentException When option given doesn't exist + */ + public function getOption($name) + { + if (!$this->hasOption($name)) { + throw new InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name)); + } + + return $this->options[$name]; + } + + /** + * Returns true if an InputOption object exists by name. + * + * @param string $name The InputOption name + * + * @return bool true if the InputOption object exists, false otherwise + */ + public function hasOption($name) + { + return isset($this->options[$name]); + } + + /** + * Gets the array of InputOption objects. + * + * @return InputOption[] An array of InputOption objects + */ + public function getOptions() + { + return $this->options; + } + + /** + * Returns true if an InputOption object exists by shortcut. + * + * @param string $name The InputOption shortcut + * + * @return bool true if the InputOption object exists, false otherwise + */ + public function hasShortcut($name) + { + return isset($this->shortcuts[$name]); + } + + /** + * Gets an InputOption by shortcut. + * + * @param string $shortcut the Shortcut name + * + * @return InputOption An InputOption object + */ + public function getOptionForShortcut($shortcut) + { + return $this->getOption($this->shortcutToName($shortcut)); + } + + /** + * Gets an array of default values. + * + * @return array An array of all default values + */ + public function getOptionDefaults() + { + $values = array(); + foreach ($this->options as $option) { + $values[$option->getName()] = $option->getDefault(); + } + + return $values; + } + + /** + * Returns the InputOption name given a shortcut. + * + * @param string $shortcut The shortcut + * + * @return string The InputOption name + * + * @throws InvalidArgumentException When option given does not exist + */ + private function shortcutToName($shortcut) + { + if (!isset($this->shortcuts[$shortcut])) { + throw new InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut)); + } + + return $this->shortcuts[$shortcut]; + } + + /** + * Gets the synopsis. + * + * @param bool $short Whether to return the short version (with options folded) or not + * + * @return string The synopsis + */ + public function getSynopsis($short = false) + { + $elements = array(); + + if ($short && $this->getOptions()) { + $elements[] = '[options]'; + } elseif (!$short) { + foreach ($this->getOptions() as $option) { + $value = ''; + if ($option->acceptValue()) { + $value = sprintf( + ' %s%s%s', + $option->isValueOptional() ? '[' : '', + strtoupper($option->getName()), + $option->isValueOptional() ? ']' : '' + ); + } + + $shortcut = $option->getShortcut() ? sprintf('-%s|', $option->getShortcut()) : ''; + $elements[] = sprintf('[%s--%s%s]', $shortcut, $option->getName(), $value); + } + } + + if (count($elements) && $this->getArguments()) { + $elements[] = '[--]'; + } + + foreach ($this->getArguments() as $argument) { + $element = '<'.$argument->getName().'>'; + if (!$argument->isRequired()) { + $element = '['.$element.']'; + } elseif ($argument->isArray()) { + $element = $element.' ('.$element.')'; + } + + if ($argument->isArray()) { + $element .= '...'; + } + + $elements[] = $element; + } + + return implode(' ', $elements); + } + + /** + * Returns a textual representation of the InputDefinition. + * + * @return string A string representing the InputDefinition + * + * @deprecated since version 2.3, to be removed in 3.0. + */ + public function asText() + { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.3 and will be removed in 3.0.', E_USER_DEPRECATED); + + $descriptor = new TextDescriptor(); + $output = new BufferedOutput(BufferedOutput::VERBOSITY_NORMAL, true); + $descriptor->describe($output, $this, array('raw_output' => true)); + + return $output->fetch(); + } + + /** + * Returns an XML representation of the InputDefinition. + * + * @param bool $asDom Whether to return a DOM or an XML string + * + * @return string|\DOMDocument An XML string representing the InputDefinition + * + * @deprecated since version 2.3, to be removed in 3.0. + */ + public function asXml($asDom = false) + { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.3 and will be removed in 3.0.', E_USER_DEPRECATED); + + $descriptor = new XmlDescriptor(); + + if ($asDom) { + return $descriptor->getInputDefinitionDocument($this); + } + + $output = new BufferedOutput(); + $descriptor->describe($output, $this); + + return $output->fetch(); + } +} diff --git a/vendor/symfony/console/Input/InputInterface.php b/vendor/symfony/console/Input/InputInterface.php new file mode 100644 index 0000000000..f83b88560d --- /dev/null +++ b/vendor/symfony/console/Input/InputInterface.php @@ -0,0 +1,152 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +/** + * InputInterface is the interface implemented by all input classes. + * + * @author Fabien Potencier + */ +interface InputInterface +{ + /** + * Returns the first argument from the raw parameters (not parsed). + * + * @return string The value of the first argument or null otherwise + */ + public function getFirstArgument(); + + /** + * Returns true if the raw parameters (not parsed) contain a value. + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The values to look for in the raw parameters (can be an array) + * + * @return bool true if the value is contained in the raw parameters + */ + public function hasParameterOption($values); + + /** + * Returns the value of a raw option (not parsed). + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The value(s) to look for in the raw parameters (can be an array) + * @param mixed $default The default value to return if no result is found + * + * @return mixed The option value + */ + public function getParameterOption($values, $default = false); + + /** + * Binds the current Input instance with the given arguments and options. + * + * @param InputDefinition $definition A InputDefinition instance + */ + public function bind(InputDefinition $definition); + + /** + * Validates if arguments given are correct. + * + * Throws an exception when not enough arguments are given. + * + * @throws \RuntimeException + */ + public function validate(); + + /** + * Returns all the given arguments merged with the default values. + * + * @return array + */ + public function getArguments(); + + /** + * Gets argument by name. + * + * @param string $name The name of the argument + * + * @return mixed + */ + public function getArgument($name); + + /** + * Sets an argument value by name. + * + * @param string $name The argument name + * @param string $value The argument value + * + * @throws InvalidArgumentException When argument given doesn't exist + */ + public function setArgument($name, $value); + + /** + * Returns true if an InputArgument object exists by name or position. + * + * @param string|int $name The InputArgument name or position + * + * @return bool true if the InputArgument object exists, false otherwise + */ + public function hasArgument($name); + + /** + * Returns all the given options merged with the default values. + * + * @return array + */ + public function getOptions(); + + /** + * Gets an option by name. + * + * @param string $name The name of the option + * + * @return mixed + */ + public function getOption($name); + + /** + * Sets an option value by name. + * + * @param string $name The option name + * @param string|bool $value The option value + * + * @throws InvalidArgumentException When option given doesn't exist + */ + public function setOption($name, $value); + + /** + * Returns true if an InputOption object exists by name. + * + * @param string $name The InputOption name + * + * @return bool true if the InputOption object exists, false otherwise + */ + public function hasOption($name); + + /** + * Is this input means interactive? + * + * @return bool + */ + public function isInteractive(); + + /** + * Sets the input interactivity. + * + * @param bool $interactive If the input should be interactive + */ + public function setInteractive($interactive); +} diff --git a/vendor/symfony/console/Input/InputOption.php b/vendor/symfony/console/Input/InputOption.php new file mode 100644 index 0000000000..f08c5f26c1 --- /dev/null +++ b/vendor/symfony/console/Input/InputOption.php @@ -0,0 +1,212 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; + +/** + * Represents a command line option. + * + * @author Fabien Potencier + */ +class InputOption +{ + const VALUE_NONE = 1; + const VALUE_REQUIRED = 2; + const VALUE_OPTIONAL = 4; + const VALUE_IS_ARRAY = 8; + + private $name; + private $shortcut; + private $mode; + private $default; + private $description; + + /** + * Constructor. + * + * @param string $name The option name + * @param string|array $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts + * @param int $mode The option mode: One of the VALUE_* constants + * @param string $description A description text + * @param mixed $default The default value (must be null for self::VALUE_NONE) + * + * @throws InvalidArgumentException If option mode is invalid or incompatible + */ + public function __construct($name, $shortcut = null, $mode = null, $description = '', $default = null) + { + if (0 === strpos($name, '--')) { + $name = substr($name, 2); + } + + if (empty($name)) { + throw new InvalidArgumentException('An option name cannot be empty.'); + } + + if (empty($shortcut)) { + $shortcut = null; + } + + if (null !== $shortcut) { + if (is_array($shortcut)) { + $shortcut = implode('|', $shortcut); + } + $shortcuts = preg_split('{(\|)-?}', ltrim($shortcut, '-')); + $shortcuts = array_filter($shortcuts); + $shortcut = implode('|', $shortcuts); + + if (empty($shortcut)) { + throw new InvalidArgumentException('An option shortcut cannot be empty.'); + } + } + + if (null === $mode) { + $mode = self::VALUE_NONE; + } elseif (!is_int($mode) || $mode > 15 || $mode < 1) { + throw new InvalidArgumentException(sprintf('Option mode "%s" is not valid.', $mode)); + } + + $this->name = $name; + $this->shortcut = $shortcut; + $this->mode = $mode; + $this->description = $description; + + if ($this->isArray() && !$this->acceptValue()) { + throw new InvalidArgumentException('Impossible to have an option mode VALUE_IS_ARRAY if the option does not accept a value.'); + } + + $this->setDefault($default); + } + + /** + * Returns the option shortcut. + * + * @return string The shortcut + */ + public function getShortcut() + { + return $this->shortcut; + } + + /** + * Returns the option name. + * + * @return string The name + */ + public function getName() + { + return $this->name; + } + + /** + * Returns true if the option accepts a value. + * + * @return bool true if value mode is not self::VALUE_NONE, false otherwise + */ + public function acceptValue() + { + return $this->isValueRequired() || $this->isValueOptional(); + } + + /** + * Returns true if the option requires a value. + * + * @return bool true if value mode is self::VALUE_REQUIRED, false otherwise + */ + public function isValueRequired() + { + return self::VALUE_REQUIRED === (self::VALUE_REQUIRED & $this->mode); + } + + /** + * Returns true if the option takes an optional value. + * + * @return bool true if value mode is self::VALUE_OPTIONAL, false otherwise + */ + public function isValueOptional() + { + return self::VALUE_OPTIONAL === (self::VALUE_OPTIONAL & $this->mode); + } + + /** + * Returns true if the option can take multiple values. + * + * @return bool true if mode is self::VALUE_IS_ARRAY, false otherwise + */ + public function isArray() + { + return self::VALUE_IS_ARRAY === (self::VALUE_IS_ARRAY & $this->mode); + } + + /** + * Sets the default value. + * + * @param mixed $default The default value + * + * @throws LogicException When incorrect default value is given + */ + public function setDefault($default = null) + { + if (self::VALUE_NONE === (self::VALUE_NONE & $this->mode) && null !== $default) { + throw new LogicException('Cannot set a default value when using InputOption::VALUE_NONE mode.'); + } + + if ($this->isArray()) { + if (null === $default) { + $default = array(); + } elseif (!is_array($default)) { + throw new LogicException('A default value for an array option must be an array.'); + } + } + + $this->default = $this->acceptValue() ? $default : false; + } + + /** + * Returns the default value. + * + * @return mixed The default value + */ + public function getDefault() + { + return $this->default; + } + + /** + * Returns the description text. + * + * @return string The description text + */ + public function getDescription() + { + return $this->description; + } + + /** + * Checks whether the given option equals this one. + * + * @param InputOption $option option to compare + * + * @return bool + */ + public function equals(InputOption $option) + { + return $option->getName() === $this->getName() + && $option->getShortcut() === $this->getShortcut() + && $option->getDefault() === $this->getDefault() + && $option->isArray() === $this->isArray() + && $option->isValueRequired() === $this->isValueRequired() + && $option->isValueOptional() === $this->isValueOptional() + ; + } +} diff --git a/vendor/symfony/console/Input/StringInput.php b/vendor/symfony/console/Input/StringInput.php new file mode 100644 index 0000000000..a40ddba31d --- /dev/null +++ b/vendor/symfony/console/Input/StringInput.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * StringInput represents an input provided as a string. + * + * Usage: + * + * $input = new StringInput('foo --bar="foobar"'); + * + * @author Fabien Potencier + */ +class StringInput extends ArgvInput +{ + const REGEX_STRING = '([^\s]+?)(?:\s|(?setTokens($this->tokenize($input)); + + if (null !== $definition) { + $this->bind($definition); + } + } + + /** + * Tokenizes a string. + * + * @param string $input The input to tokenize + * + * @return array An array of tokens + * + * @throws InvalidArgumentException When unable to parse input (should never happen) + */ + private function tokenize($input) + { + $tokens = array(); + $length = strlen($input); + $cursor = 0; + while ($cursor < $length) { + if (preg_match('/\s+/A', $input, $match, null, $cursor)) { + } elseif (preg_match('/([^="\'\s]+?)(=?)('.self::REGEX_QUOTED_STRING.'+)/A', $input, $match, null, $cursor)) { + $tokens[] = $match[1].$match[2].stripcslashes(str_replace(array('"\'', '\'"', '\'\'', '""'), '', substr($match[3], 1, strlen($match[3]) - 2))); + } elseif (preg_match('/'.self::REGEX_QUOTED_STRING.'/A', $input, $match, null, $cursor)) { + $tokens[] = stripcslashes(substr($match[0], 1, strlen($match[0]) - 2)); + } elseif (preg_match('/'.self::REGEX_STRING.'/A', $input, $match, null, $cursor)) { + $tokens[] = stripcslashes($match[1]); + } else { + // should never happen + throw new InvalidArgumentException(sprintf('Unable to parse input near "... %s ..."', substr($input, $cursor, 10))); + } + + $cursor += strlen($match[0]); + } + + return $tokens; + } +} diff --git a/vendor/symfony/console/LICENSE b/vendor/symfony/console/LICENSE new file mode 100644 index 0000000000..12a74531e4 --- /dev/null +++ b/vendor/symfony/console/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2016 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/console/Logger/ConsoleLogger.php b/vendor/symfony/console/Logger/ConsoleLogger.php new file mode 100644 index 0000000000..1f7417ea5a --- /dev/null +++ b/vendor/symfony/console/Logger/ConsoleLogger.php @@ -0,0 +1,119 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Logger; + +use Psr\Log\AbstractLogger; +use Psr\Log\InvalidArgumentException; +use Psr\Log\LogLevel; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\ConsoleOutputInterface; + +/** + * PSR-3 compliant console logger. + * + * @author Kévin Dunglas + * + * @link http://www.php-fig.org/psr/psr-3/ + */ +class ConsoleLogger extends AbstractLogger +{ + const INFO = 'info'; + const ERROR = 'error'; + + /** + * @var OutputInterface + */ + private $output; + /** + * @var array + */ + private $verbosityLevelMap = array( + LogLevel::EMERGENCY => OutputInterface::VERBOSITY_NORMAL, + LogLevel::ALERT => OutputInterface::VERBOSITY_NORMAL, + LogLevel::CRITICAL => OutputInterface::VERBOSITY_NORMAL, + LogLevel::ERROR => OutputInterface::VERBOSITY_NORMAL, + LogLevel::WARNING => OutputInterface::VERBOSITY_NORMAL, + LogLevel::NOTICE => OutputInterface::VERBOSITY_VERBOSE, + LogLevel::INFO => OutputInterface::VERBOSITY_VERY_VERBOSE, + LogLevel::DEBUG => OutputInterface::VERBOSITY_DEBUG, + ); + /** + * @var array + */ + private $formatLevelMap = array( + LogLevel::EMERGENCY => self::ERROR, + LogLevel::ALERT => self::ERROR, + LogLevel::CRITICAL => self::ERROR, + LogLevel::ERROR => self::ERROR, + LogLevel::WARNING => self::INFO, + LogLevel::NOTICE => self::INFO, + LogLevel::INFO => self::INFO, + LogLevel::DEBUG => self::INFO, + ); + + /** + * @param OutputInterface $output + * @param array $verbosityLevelMap + * @param array $formatLevelMap + */ + public function __construct(OutputInterface $output, array $verbosityLevelMap = array(), array $formatLevelMap = array()) + { + $this->output = $output; + $this->verbosityLevelMap = $verbosityLevelMap + $this->verbosityLevelMap; + $this->formatLevelMap = $formatLevelMap + $this->formatLevelMap; + } + + /** + * {@inheritdoc} + */ + public function log($level, $message, array $context = array()) + { + if (!isset($this->verbosityLevelMap[$level])) { + throw new InvalidArgumentException(sprintf('The log level "%s" does not exist.', $level)); + } + + // Write to the error output if necessary and available + if ($this->formatLevelMap[$level] === self::ERROR && $this->output instanceof ConsoleOutputInterface) { + $output = $this->output->getErrorOutput(); + } else { + $output = $this->output; + } + + if ($output->getVerbosity() >= $this->verbosityLevelMap[$level]) { + $output->writeln(sprintf('<%1$s>[%2$s] %3$s', $this->formatLevelMap[$level], $level, $this->interpolate($message, $context))); + } + } + + /** + * Interpolates context values into the message placeholders. + * + * @author PHP Framework Interoperability Group + * + * @param string $message + * @param array $context + * + * @return string + */ + private function interpolate($message, array $context) + { + // build a replacement array with braces around the context keys + $replace = array(); + foreach ($context as $key => $val) { + if (!is_array($val) && (!is_object($val) || method_exists($val, '__toString'))) { + $replace[sprintf('{%s}', $key)] = $val; + } + } + + // interpolate replacement values into the message and return + return strtr($message, $replace); + } +} diff --git a/vendor/symfony/console/Output/BufferedOutput.php b/vendor/symfony/console/Output/BufferedOutput.php new file mode 100644 index 0000000000..5682fc2404 --- /dev/null +++ b/vendor/symfony/console/Output/BufferedOutput.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +/** + * @author Jean-François Simon + */ +class BufferedOutput extends Output +{ + /** + * @var string + */ + private $buffer = ''; + + /** + * Empties buffer and returns its content. + * + * @return string + */ + public function fetch() + { + $content = $this->buffer; + $this->buffer = ''; + + return $content; + } + + /** + * {@inheritdoc} + */ + protected function doWrite($message, $newline) + { + $this->buffer .= $message; + + if ($newline) { + $this->buffer .= "\n"; + } + } +} diff --git a/vendor/symfony/console/Output/ConsoleOutput.php b/vendor/symfony/console/Output/ConsoleOutput.php new file mode 100644 index 0000000000..f666c793e2 --- /dev/null +++ b/vendor/symfony/console/Output/ConsoleOutput.php @@ -0,0 +1,156 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * ConsoleOutput is the default class for all CLI output. It uses STDOUT. + * + * This class is a convenient wrapper around `StreamOutput`. + * + * $output = new ConsoleOutput(); + * + * This is equivalent to: + * + * $output = new StreamOutput(fopen('php://stdout', 'w')); + * + * @author Fabien Potencier + */ +class ConsoleOutput extends StreamOutput implements ConsoleOutputInterface +{ + /** + * @var StreamOutput + */ + private $stderr; + + /** + * Constructor. + * + * @param int $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface) + * @param bool|null $decorated Whether to decorate messages (null for auto-guessing) + * @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter) + */ + public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = null, OutputFormatterInterface $formatter = null) + { + parent::__construct($this->openOutputStream(), $verbosity, $decorated, $formatter); + + $actualDecorated = $this->isDecorated(); + $this->stderr = new StreamOutput($this->openErrorStream(), $verbosity, $decorated, $this->getFormatter()); + + if (null === $decorated) { + $this->setDecorated($actualDecorated && $this->stderr->isDecorated()); + } + } + + /** + * {@inheritdoc} + */ + public function setDecorated($decorated) + { + parent::setDecorated($decorated); + $this->stderr->setDecorated($decorated); + } + + /** + * {@inheritdoc} + */ + public function setFormatter(OutputFormatterInterface $formatter) + { + parent::setFormatter($formatter); + $this->stderr->setFormatter($formatter); + } + + /** + * {@inheritdoc} + */ + public function setVerbosity($level) + { + parent::setVerbosity($level); + $this->stderr->setVerbosity($level); + } + + /** + * {@inheritdoc} + */ + public function getErrorOutput() + { + return $this->stderr; + } + + /** + * {@inheritdoc} + */ + public function setErrorOutput(OutputInterface $error) + { + $this->stderr = $error; + } + + /** + * Returns true if current environment supports writing console output to + * STDOUT. + * + * @return bool + */ + protected function hasStdoutSupport() + { + return false === $this->isRunningOS400(); + } + + /** + * Returns true if current environment supports writing console output to + * STDERR. + * + * @return bool + */ + protected function hasStderrSupport() + { + return false === $this->isRunningOS400(); + } + + /** + * Checks if current executing environment is IBM iSeries (OS400), which + * doesn't properly convert character-encodings between ASCII to EBCDIC. + * + * @return bool + */ + private function isRunningOS400() + { + $checks = array( + function_exists('php_uname') ? php_uname('s') : '', + getenv('OSTYPE'), + PHP_OS, + ); + + return false !== stripos(implode(';', $checks), 'OS400'); + } + + /** + * @return resource + */ + private function openOutputStream() + { + $outputStream = $this->hasStdoutSupport() ? 'php://stdout' : 'php://output'; + + return @fopen($outputStream, 'w') ?: fopen('php://output', 'w'); + } + + /** + * @return resource + */ + private function openErrorStream() + { + $errorStream = $this->hasStderrSupport() ? 'php://stderr' : 'php://output'; + + return fopen($errorStream, 'w'); + } +} diff --git a/vendor/symfony/console/Output/ConsoleOutputInterface.php b/vendor/symfony/console/Output/ConsoleOutputInterface.php new file mode 100644 index 0000000000..5eb4fc7acd --- /dev/null +++ b/vendor/symfony/console/Output/ConsoleOutputInterface.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +/** + * ConsoleOutputInterface is the interface implemented by ConsoleOutput class. + * This adds information about stderr output stream. + * + * @author Dariusz Górecki + */ +interface ConsoleOutputInterface extends OutputInterface +{ + /** + * Gets the OutputInterface for errors. + * + * @return OutputInterface + */ + public function getErrorOutput(); + + /** + * Sets the OutputInterface used for errors. + * + * @param OutputInterface $error + */ + public function setErrorOutput(OutputInterface $error); +} diff --git a/vendor/symfony/console/Output/NullOutput.php b/vendor/symfony/console/Output/NullOutput.php new file mode 100644 index 0000000000..682f9a4d43 --- /dev/null +++ b/vendor/symfony/console/Output/NullOutput.php @@ -0,0 +1,111 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * NullOutput suppresses all output. + * + * $output = new NullOutput(); + * + * @author Fabien Potencier + * @author Tobias Schultze + */ +class NullOutput implements OutputInterface +{ + /** + * {@inheritdoc} + */ + public function setFormatter(OutputFormatterInterface $formatter) + { + // do nothing + } + + /** + * {@inheritdoc} + */ + public function getFormatter() + { + // to comply with the interface we must return a OutputFormatterInterface + return new OutputFormatter(); + } + + /** + * {@inheritdoc} + */ + public function setDecorated($decorated) + { + // do nothing + } + + /** + * {@inheritdoc} + */ + public function isDecorated() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function setVerbosity($level) + { + // do nothing + } + + /** + * {@inheritdoc} + */ + public function getVerbosity() + { + return self::VERBOSITY_QUIET; + } + + public function isQuiet() + { + return true; + } + + public function isVerbose() + { + return false; + } + + public function isVeryVerbose() + { + return false; + } + + public function isDebug() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function writeln($messages, $options = self::OUTPUT_NORMAL) + { + // do nothing + } + + /** + * {@inheritdoc} + */ + public function write($messages, $newline = false, $options = self::OUTPUT_NORMAL) + { + // do nothing + } +} diff --git a/vendor/symfony/console/Output/Output.php b/vendor/symfony/console/Output/Output.php new file mode 100644 index 0000000000..4476ffb590 --- /dev/null +++ b/vendor/symfony/console/Output/Output.php @@ -0,0 +1,165 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; +use Symfony\Component\Console\Formatter\OutputFormatter; + +/** + * Base class for output classes. + * + * There are five levels of verbosity: + * + * * normal: no option passed (normal output) + * * verbose: -v (more output) + * * very verbose: -vv (highly extended output) + * * debug: -vvv (all debug output) + * * quiet: -q (no output) + * + * @author Fabien Potencier + */ +abstract class Output implements OutputInterface +{ + private $verbosity; + private $formatter; + + /** + * Constructor. + * + * @param int $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface) + * @param bool $decorated Whether to decorate messages + * @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter) + */ + public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = false, OutputFormatterInterface $formatter = null) + { + $this->verbosity = null === $verbosity ? self::VERBOSITY_NORMAL : $verbosity; + $this->formatter = $formatter ?: new OutputFormatter(); + $this->formatter->setDecorated($decorated); + } + + /** + * {@inheritdoc} + */ + public function setFormatter(OutputFormatterInterface $formatter) + { + $this->formatter = $formatter; + } + + /** + * {@inheritdoc} + */ + public function getFormatter() + { + return $this->formatter; + } + + /** + * {@inheritdoc} + */ + public function setDecorated($decorated) + { + $this->formatter->setDecorated($decorated); + } + + /** + * {@inheritdoc} + */ + public function isDecorated() + { + return $this->formatter->isDecorated(); + } + + /** + * {@inheritdoc} + */ + public function setVerbosity($level) + { + $this->verbosity = (int) $level; + } + + /** + * {@inheritdoc} + */ + public function getVerbosity() + { + return $this->verbosity; + } + + public function isQuiet() + { + return self::VERBOSITY_QUIET === $this->verbosity; + } + + public function isVerbose() + { + return self::VERBOSITY_VERBOSE <= $this->verbosity; + } + + public function isVeryVerbose() + { + return self::VERBOSITY_VERY_VERBOSE <= $this->verbosity; + } + + public function isDebug() + { + return self::VERBOSITY_DEBUG <= $this->verbosity; + } + + /** + * {@inheritdoc} + */ + public function writeln($messages, $options = self::OUTPUT_NORMAL) + { + $this->write($messages, true, $options); + } + + /** + * {@inheritdoc} + */ + public function write($messages, $newline = false, $options = self::OUTPUT_NORMAL) + { + $messages = (array) $messages; + + $types = self::OUTPUT_NORMAL | self::OUTPUT_RAW | self::OUTPUT_PLAIN; + $type = $types & $options ?: self::OUTPUT_NORMAL; + + $verbosities = self::VERBOSITY_QUIET | self::VERBOSITY_NORMAL | self::VERBOSITY_VERBOSE | self::VERBOSITY_VERY_VERBOSE | self::VERBOSITY_DEBUG; + $verbosity = $verbosities & $options ?: self::VERBOSITY_NORMAL; + + if ($verbosity > $this->getVerbosity()) { + return; + } + + foreach ($messages as $message) { + switch ($type) { + case OutputInterface::OUTPUT_NORMAL: + $message = $this->formatter->format($message); + break; + case OutputInterface::OUTPUT_RAW: + break; + case OutputInterface::OUTPUT_PLAIN: + $message = strip_tags($this->formatter->format($message)); + break; + } + + $this->doWrite($message, $newline); + } + } + + /** + * Writes a message to the output. + * + * @param string $message A message to write to the output + * @param bool $newline Whether to add a newline or not + */ + abstract protected function doWrite($message, $newline); +} diff --git a/vendor/symfony/console/Output/OutputInterface.php b/vendor/symfony/console/Output/OutputInterface.php new file mode 100644 index 0000000000..9a8290bddd --- /dev/null +++ b/vendor/symfony/console/Output/OutputInterface.php @@ -0,0 +1,91 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * OutputInterface is the interface implemented by all Output classes. + * + * @author Fabien Potencier + */ +interface OutputInterface +{ + const VERBOSITY_QUIET = 16; + const VERBOSITY_NORMAL = 32; + const VERBOSITY_VERBOSE = 64; + const VERBOSITY_VERY_VERBOSE = 128; + const VERBOSITY_DEBUG = 256; + + const OUTPUT_NORMAL = 1; + const OUTPUT_RAW = 2; + const OUTPUT_PLAIN = 4; + + /** + * Writes a message to the output. + * + * @param string|array $messages The message as an array of lines or a single string + * @param bool $newline Whether to add a newline + * @param int $options A bitmask of options (one of the OUTPUT or VERBOSITY constants), 0 is considered the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL + */ + public function write($messages, $newline = false, $options = 0); + + /** + * Writes a message to the output and adds a newline at the end. + * + * @param string|array $messages The message as an array of lines of a single string + * @param int $options A bitmask of options (one of the OUTPUT or VERBOSITY constants), 0 is considered the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL + */ + public function writeln($messages, $options = 0); + + /** + * Sets the verbosity of the output. + * + * @param int $level The level of verbosity (one of the VERBOSITY constants) + */ + public function setVerbosity($level); + + /** + * Gets the current verbosity of the output. + * + * @return int The current level of verbosity (one of the VERBOSITY constants) + */ + public function getVerbosity(); + + /** + * Sets the decorated flag. + * + * @param bool $decorated Whether to decorate the messages + */ + public function setDecorated($decorated); + + /** + * Gets the decorated flag. + * + * @return bool true if the output will decorate messages, false otherwise + */ + public function isDecorated(); + + /** + * Sets output formatter. + * + * @param OutputFormatterInterface $formatter + */ + public function setFormatter(OutputFormatterInterface $formatter); + + /** + * Returns current output formatter instance. + * + * @return OutputFormatterInterface + */ + public function getFormatter(); +} diff --git a/vendor/symfony/console/Output/StreamOutput.php b/vendor/symfony/console/Output/StreamOutput.php new file mode 100644 index 0000000000..9e6b74810d --- /dev/null +++ b/vendor/symfony/console/Output/StreamOutput.php @@ -0,0 +1,105 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * StreamOutput writes the output to a given stream. + * + * Usage: + * + * $output = new StreamOutput(fopen('php://stdout', 'w')); + * + * As `StreamOutput` can use any stream, you can also use a file: + * + * $output = new StreamOutput(fopen('/path/to/output.log', 'a', false)); + * + * @author Fabien Potencier + */ +class StreamOutput extends Output +{ + private $stream; + + /** + * Constructor. + * + * @param resource $stream A stream resource + * @param int $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface) + * @param bool|null $decorated Whether to decorate messages (null for auto-guessing) + * @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter) + * + * @throws InvalidArgumentException When first argument is not a real stream + */ + public function __construct($stream, $verbosity = self::VERBOSITY_NORMAL, $decorated = null, OutputFormatterInterface $formatter = null) + { + if (!is_resource($stream) || 'stream' !== get_resource_type($stream)) { + throw new InvalidArgumentException('The StreamOutput class needs a stream as its first argument.'); + } + + $this->stream = $stream; + + if (null === $decorated) { + $decorated = $this->hasColorSupport(); + } + + parent::__construct($verbosity, $decorated, $formatter); + } + + /** + * Gets the stream attached to this StreamOutput instance. + * + * @return resource A stream resource + */ + public function getStream() + { + return $this->stream; + } + + /** + * {@inheritdoc} + */ + protected function doWrite($message, $newline) + { + if (false === @fwrite($this->stream, $message) || ($newline && (false === @fwrite($this->stream, PHP_EOL)))) { + // should never happen + throw new RuntimeException('Unable to write output.'); + } + + fflush($this->stream); + } + + /** + * Returns true if the stream supports colorization. + * + * Colorization is disabled if not supported by the stream: + * + * - Windows before 10.0.10586 without Ansicon, ConEmu or Mintty + * - non tty consoles + * + * @return bool true if the stream supports colorization, false otherwise + */ + protected function hasColorSupport() + { + if (DIRECTORY_SEPARATOR === '\\') { + return + 0 >= version_compare('10.0.10586', PHP_WINDOWS_VERSION_MAJOR.'.'.PHP_WINDOWS_VERSION_MINOR.'.'.PHP_WINDOWS_VERSION_BUILD) + || false !== getenv('ANSICON') + || 'ON' === getenv('ConEmuANSI') + || 'xterm' === getenv('TERM'); + } + + return function_exists('posix_isatty') && @posix_isatty($this->stream); + } +} diff --git a/vendor/symfony/console/Question/ChoiceQuestion.php b/vendor/symfony/console/Question/ChoiceQuestion.php new file mode 100644 index 0000000000..2c40638dc6 --- /dev/null +++ b/vendor/symfony/console/Question/ChoiceQuestion.php @@ -0,0 +1,177 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Question; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * Represents a choice question. + * + * @author Fabien Potencier + */ +class ChoiceQuestion extends Question +{ + private $choices; + private $multiselect = false; + private $prompt = ' > '; + private $errorMessage = 'Value "%s" is invalid'; + + /** + * Constructor. + * + * @param string $question The question to ask to the user + * @param array $choices The list of available choices + * @param mixed $default The default answer to return + */ + public function __construct($question, array $choices, $default = null) + { + parent::__construct($question, $default); + + $this->choices = $choices; + $this->setValidator($this->getDefaultValidator()); + $this->setAutocompleterValues($choices); + } + + /** + * Returns available choices. + * + * @return array + */ + public function getChoices() + { + return $this->choices; + } + + /** + * Sets multiselect option. + * + * When multiselect is set to true, multiple choices can be answered. + * + * @param bool $multiselect + * + * @return ChoiceQuestion The current instance + */ + public function setMultiselect($multiselect) + { + $this->multiselect = $multiselect; + $this->setValidator($this->getDefaultValidator()); + + return $this; + } + + /** + * Gets the prompt for choices. + * + * @return string + */ + public function getPrompt() + { + return $this->prompt; + } + + /** + * Sets the prompt for choices. + * + * @param string $prompt + * + * @return ChoiceQuestion The current instance + */ + public function setPrompt($prompt) + { + $this->prompt = $prompt; + + return $this; + } + + /** + * Sets the error message for invalid values. + * + * The error message has a string placeholder (%s) for the invalid value. + * + * @param string $errorMessage + * + * @return ChoiceQuestion The current instance + */ + public function setErrorMessage($errorMessage) + { + $this->errorMessage = $errorMessage; + $this->setValidator($this->getDefaultValidator()); + + return $this; + } + + /** + * Returns the default answer validator. + * + * @return callable + */ + private function getDefaultValidator() + { + $choices = $this->choices; + $errorMessage = $this->errorMessage; + $multiselect = $this->multiselect; + $isAssoc = $this->isAssoc($choices); + + return function ($selected) use ($choices, $errorMessage, $multiselect, $isAssoc) { + // Collapse all spaces. + $selectedChoices = str_replace(' ', '', $selected); + + if ($multiselect) { + // Check for a separated comma values + if (!preg_match('/^[a-zA-Z0-9_-]+(?:,[a-zA-Z0-9_-]+)*$/', $selectedChoices, $matches)) { + throw new InvalidArgumentException(sprintf($errorMessage, $selected)); + } + $selectedChoices = explode(',', $selectedChoices); + } else { + $selectedChoices = array($selected); + } + + $multiselectChoices = array(); + foreach ($selectedChoices as $value) { + $results = array(); + foreach ($choices as $key => $choice) { + if ($choice === $value) { + $results[] = $key; + } + } + + if (count($results) > 1) { + throw new InvalidArgumentException(sprintf('The provided answer is ambiguous. Value should be one of %s.', implode(' or ', $results))); + } + + $result = array_search($value, $choices); + + if (!$isAssoc) { + if (false !== $result) { + $result = $choices[$result]; + } elseif (isset($choices[$value])) { + $result = $choices[$value]; + } + } elseif (false === $result && isset($choices[$value])) { + $result = $value; + } + + if (false === $result) { + throw new InvalidArgumentException(sprintf($errorMessage, $value)); + } + + $multiselectChoices[] = (string) $result; + } + + if ($multiselect) { + return $multiselectChoices; + } + + return current($multiselectChoices); + }; + } +} diff --git a/vendor/symfony/console/Question/ConfirmationQuestion.php b/vendor/symfony/console/Question/ConfirmationQuestion.php new file mode 100644 index 0000000000..29d98879f0 --- /dev/null +++ b/vendor/symfony/console/Question/ConfirmationQuestion.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Question; + +/** + * Represents a yes/no question. + * + * @author Fabien Potencier + */ +class ConfirmationQuestion extends Question +{ + private $trueAnswerRegex; + + /** + * Constructor. + * + * @param string $question The question to ask to the user + * @param bool $default The default answer to return, true or false + * @param string $trueAnswerRegex A regex to match the "yes" answer + */ + public function __construct($question, $default = true, $trueAnswerRegex = '/^y/i') + { + parent::__construct($question, (bool) $default); + + $this->trueAnswerRegex = $trueAnswerRegex; + $this->setNormalizer($this->getDefaultNormalizer()); + } + + /** + * Returns the default answer normalizer. + * + * @return callable + */ + private function getDefaultNormalizer() + { + $default = $this->getDefault(); + $regex = $this->trueAnswerRegex; + + return function ($answer) use ($default, $regex) { + if (is_bool($answer)) { + return $answer; + } + + $answerIsTrue = (bool) preg_match($regex, $answer); + if (false === $default) { + return $answer && $answerIsTrue; + } + + return !$answer || $answerIsTrue; + }; + } +} diff --git a/vendor/symfony/console/Question/Question.php b/vendor/symfony/console/Question/Question.php new file mode 100644 index 0000000000..ab415c26d8 --- /dev/null +++ b/vendor/symfony/console/Question/Question.php @@ -0,0 +1,250 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Question; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; + +/** + * Represents a Question. + * + * @author Fabien Potencier + */ +class Question +{ + private $question; + private $attempts; + private $hidden = false; + private $hiddenFallback = true; + private $autocompleterValues; + private $validator; + private $default; + private $normalizer; + + /** + * Constructor. + * + * @param string $question The question to ask to the user + * @param mixed $default The default answer to return if the user enters nothing + */ + public function __construct($question, $default = null) + { + $this->question = $question; + $this->default = $default; + } + + /** + * Returns the question. + * + * @return string + */ + public function getQuestion() + { + return $this->question; + } + + /** + * Returns the default answer. + * + * @return mixed + */ + public function getDefault() + { + return $this->default; + } + + /** + * Returns whether the user response must be hidden. + * + * @return bool + */ + public function isHidden() + { + return $this->hidden; + } + + /** + * Sets whether the user response must be hidden or not. + * + * @param bool $hidden + * + * @return Question The current instance + * + * @throws LogicException In case the autocompleter is also used + */ + public function setHidden($hidden) + { + if ($this->autocompleterValues) { + throw new LogicException('A hidden question cannot use the autocompleter.'); + } + + $this->hidden = (bool) $hidden; + + return $this; + } + + /** + * In case the response can not be hidden, whether to fallback on non-hidden question or not. + * + * @return bool + */ + public function isHiddenFallback() + { + return $this->hiddenFallback; + } + + /** + * Sets whether to fallback on non-hidden question if the response can not be hidden. + * + * @param bool $fallback + * + * @return Question The current instance + */ + public function setHiddenFallback($fallback) + { + $this->hiddenFallback = (bool) $fallback; + + return $this; + } + + /** + * Gets values for the autocompleter. + * + * @return null|array|\Traversable + */ + public function getAutocompleterValues() + { + return $this->autocompleterValues; + } + + /** + * Sets values for the autocompleter. + * + * @param null|array|\Traversable $values + * + * @return Question The current instance + * + * @throws InvalidArgumentException + * @throws LogicException + */ + public function setAutocompleterValues($values) + { + if (is_array($values)) { + $values = $this->isAssoc($values) ? array_merge(array_keys($values), array_values($values)) : array_values($values); + } + + if (null !== $values && !is_array($values)) { + if (!$values instanceof \Traversable || !$values instanceof \Countable) { + throw new InvalidArgumentException('Autocompleter values can be either an array, `null` or an object implementing both `Countable` and `Traversable` interfaces.'); + } + } + + if ($this->hidden) { + throw new LogicException('A hidden question cannot use the autocompleter.'); + } + + $this->autocompleterValues = $values; + + return $this; + } + + /** + * Sets a validator for the question. + * + * @param null|callable $validator + * + * @return Question The current instance + */ + public function setValidator($validator) + { + $this->validator = $validator; + + return $this; + } + + /** + * Gets the validator for the question. + * + * @return null|callable + */ + public function getValidator() + { + return $this->validator; + } + + /** + * Sets the maximum number of attempts. + * + * Null means an unlimited number of attempts. + * + * @param null|int $attempts + * + * @return Question The current instance + * + * @throws InvalidArgumentException In case the number of attempts is invalid. + */ + public function setMaxAttempts($attempts) + { + if (null !== $attempts && $attempts < 1) { + throw new InvalidArgumentException('Maximum number of attempts must be a positive value.'); + } + + $this->attempts = $attempts; + + return $this; + } + + /** + * Gets the maximum number of attempts. + * + * Null means an unlimited number of attempts. + * + * @return null|int + */ + public function getMaxAttempts() + { + return $this->attempts; + } + + /** + * Sets a normalizer for the response. + * + * The normalizer can be a callable (a string), a closure or a class implementing __invoke. + * + * @param callable $normalizer + * + * @return Question The current instance + */ + public function setNormalizer($normalizer) + { + $this->normalizer = $normalizer; + + return $this; + } + + /** + * Gets the normalizer for the response. + * + * The normalizer can ba a callable (a string), a closure or a class implementing __invoke. + * + * @return callable + */ + public function getNormalizer() + { + return $this->normalizer; + } + + protected function isAssoc($array) + { + return (bool) count(array_filter(array_keys($array), 'is_string')); + } +} diff --git a/vendor/symfony/console/README.md b/vendor/symfony/console/README.md new file mode 100644 index 0000000000..664a37c0ee --- /dev/null +++ b/vendor/symfony/console/README.md @@ -0,0 +1,20 @@ +Console Component +================= + +The Console component eases the creation of beautiful and testable command line +interfaces. + +Resources +--------- + + * [Documentation](https://symfony.com/doc/current/components/console/index.html) + * [Contributing](https://symfony.com/doc/current/contributing/index.html) + * [Report issues](https://github.com/symfony/symfony/issues) and + [send Pull Requests](https://github.com/symfony/symfony/pulls) + in the [main Symfony repository](https://github.com/symfony/symfony) + +Credits +------- + +`Resources/bin/hiddeninput.exe` is a third party binary provided within this +component. Find sources and license at https://github.com/Seldaek/hidden-input. diff --git a/vendor/symfony/console/Resources/bin/hiddeninput.exe b/vendor/symfony/console/Resources/bin/hiddeninput.exe new file mode 100644 index 0000000000000000000000000000000000000000..c8cf65e8d819e6e525121cf6b21f1c2429746038 GIT binary patch literal 9216 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 J0sQ~1{4brj`U(I5 literal 0 HcmV?d00001 diff --git a/vendor/symfony/console/Shell.php b/vendor/symfony/console/Shell.php new file mode 100644 index 0000000000..dacdf223a1 --- /dev/null +++ b/vendor/symfony/console/Shell.php @@ -0,0 +1,233 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console; + +use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Console\Input\StringInput; +use Symfony\Component\Console\Output\ConsoleOutput; +use Symfony\Component\Process\ProcessBuilder; +use Symfony\Component\Process\PhpExecutableFinder; + +/** + * A Shell wraps an Application to add shell capabilities to it. + * + * Support for history and completion only works with a PHP compiled + * with readline support (either --with-readline or --with-libedit) + * + * @deprecated since version 2.8, to be removed in 3.0. + * + * @author Fabien Potencier + * @author Martin Hasoň + */ +class Shell +{ + private $application; + private $history; + private $output; + private $hasReadline; + private $processIsolation = false; + + /** + * Constructor. + * + * If there is no readline support for the current PHP executable + * a \RuntimeException exception is thrown. + * + * @param Application $application An application instance + */ + public function __construct(Application $application) + { + @trigger_error('The '.__CLASS__.' class is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED); + + $this->hasReadline = function_exists('readline'); + $this->application = $application; + $this->history = getenv('HOME').'/.history_'.$application->getName(); + $this->output = new ConsoleOutput(); + } + + /** + * Runs the shell. + */ + public function run() + { + $this->application->setAutoExit(false); + $this->application->setCatchExceptions(true); + + if ($this->hasReadline) { + readline_read_history($this->history); + readline_completion_function(array($this, 'autocompleter')); + } + + $this->output->writeln($this->getHeader()); + $php = null; + if ($this->processIsolation) { + $finder = new PhpExecutableFinder(); + $php = $finder->find(); + $this->output->writeln(<<<'EOF' +Running with process isolation, you should consider this: + * each command is executed as separate process, + * commands don't support interactivity, all params must be passed explicitly, + * commands output is not colorized. + +EOF + ); + } + + while (true) { + $command = $this->readline(); + + if (false === $command) { + $this->output->writeln("\n"); + + break; + } + + if ($this->hasReadline) { + readline_add_history($command); + readline_write_history($this->history); + } + + if ($this->processIsolation) { + $pb = new ProcessBuilder(); + + $process = $pb + ->add($php) + ->add($_SERVER['argv'][0]) + ->add($command) + ->inheritEnvironmentVariables(true) + ->getProcess() + ; + + $output = $this->output; + $process->run(function ($type, $data) use ($output) { + $output->writeln($data); + }); + + $ret = $process->getExitCode(); + } else { + $ret = $this->application->run(new StringInput($command), $this->output); + } + + if (0 !== $ret) { + $this->output->writeln(sprintf('The command terminated with an error status (%s)', $ret)); + } + } + } + + /** + * Returns the shell header. + * + * @return string The header string + */ + protected function getHeader() + { + return <<{$this->application->getName()} shell ({$this->application->getVersion()}). + +At the prompt, type help for some help, +or list to get a list of available commands. + +To exit the shell, type ^D. + +EOF; + } + + /** + * Renders a prompt. + * + * @return string The prompt + */ + protected function getPrompt() + { + // using the formatter here is required when using readline + return $this->output->getFormatter()->format($this->application->getName().' > '); + } + + protected function getOutput() + { + return $this->output; + } + + protected function getApplication() + { + return $this->application; + } + + /** + * Tries to return autocompletion for the current entered text. + * + * @param string $text The last segment of the entered text + * + * @return bool|array A list of guessed strings or true + */ + private function autocompleter($text) + { + $info = readline_info(); + $text = substr($info['line_buffer'], 0, $info['end']); + + if ($info['point'] !== $info['end']) { + return true; + } + + // task name? + if (false === strpos($text, ' ') || !$text) { + return array_keys($this->application->all()); + } + + // options and arguments? + try { + $command = $this->application->find(substr($text, 0, strpos($text, ' '))); + } catch (\Exception $e) { + return true; + } + + $list = array('--help'); + foreach ($command->getDefinition()->getOptions() as $option) { + $list[] = '--'.$option->getName(); + } + + return $list; + } + + /** + * Reads a single line from standard input. + * + * @return string The single line from standard input + */ + private function readline() + { + if ($this->hasReadline) { + $line = readline($this->getPrompt()); + } else { + $this->output->write($this->getPrompt()); + $line = fgets(STDIN, 1024); + $line = (false === $line || '' === $line) ? false : rtrim($line); + } + + return $line; + } + + public function getProcessIsolation() + { + return $this->processIsolation; + } + + public function setProcessIsolation($processIsolation) + { + $this->processIsolation = (bool) $processIsolation; + + if ($this->processIsolation && !class_exists('Symfony\\Component\\Process\\Process')) { + throw new RuntimeException('Unable to isolate processes as the Symfony Process Component is not installed.'); + } + } +} diff --git a/vendor/symfony/console/Style/OutputStyle.php b/vendor/symfony/console/Style/OutputStyle.php new file mode 100644 index 0000000000..8371bb5335 --- /dev/null +++ b/vendor/symfony/console/Style/OutputStyle.php @@ -0,0 +1,116 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Style; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; +use Symfony\Component\Console\Helper\ProgressBar; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Decorates output to add console style guide helpers. + * + * @author Kevin Bond + */ +abstract class OutputStyle implements OutputInterface, StyleInterface +{ + private $output; + + /** + * @param OutputInterface $output + */ + public function __construct(OutputInterface $output) + { + $this->output = $output; + } + + /** + * {@inheritdoc} + */ + public function newLine($count = 1) + { + $this->output->write(str_repeat(PHP_EOL, $count)); + } + + /** + * @param int $max + * + * @return ProgressBar + */ + public function createProgressBar($max = 0) + { + return new ProgressBar($this->output, $max); + } + + /** + * {@inheritdoc} + */ + public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL) + { + $this->output->write($messages, $newline, $type); + } + + /** + * {@inheritdoc} + */ + public function writeln($messages, $type = self::OUTPUT_NORMAL) + { + $this->output->writeln($messages, $type); + } + + /** + * {@inheritdoc} + */ + public function setVerbosity($level) + { + $this->output->setVerbosity($level); + } + + /** + * {@inheritdoc} + */ + public function getVerbosity() + { + return $this->output->getVerbosity(); + } + + /** + * {@inheritdoc} + */ + public function setDecorated($decorated) + { + $this->output->setDecorated($decorated); + } + + /** + * {@inheritdoc} + */ + public function isDecorated() + { + return $this->output->isDecorated(); + } + + /** + * {@inheritdoc} + */ + public function setFormatter(OutputFormatterInterface $formatter) + { + $this->output->setFormatter($formatter); + } + + /** + * {@inheritdoc} + */ + public function getFormatter() + { + return $this->output->getFormatter(); + } +} diff --git a/vendor/symfony/console/Style/StyleInterface.php b/vendor/symfony/console/Style/StyleInterface.php new file mode 100644 index 0000000000..2448547f85 --- /dev/null +++ b/vendor/symfony/console/Style/StyleInterface.php @@ -0,0 +1,159 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Style; + +/** + * Output style helpers. + * + * @author Kevin Bond + */ +interface StyleInterface +{ + /** + * Formats a command title. + * + * @param string $message + */ + public function title($message); + + /** + * Formats a section title. + * + * @param string $message + */ + public function section($message); + + /** + * Formats a list. + * + * @param array $elements + */ + public function listing(array $elements); + + /** + * Formats informational text. + * + * @param string|array $message + */ + public function text($message); + + /** + * Formats a success result bar. + * + * @param string|array $message + */ + public function success($message); + + /** + * Formats an error result bar. + * + * @param string|array $message + */ + public function error($message); + + /** + * Formats an warning result bar. + * + * @param string|array $message + */ + public function warning($message); + + /** + * Formats a note admonition. + * + * @param string|array $message + */ + public function note($message); + + /** + * Formats a caution admonition. + * + * @param string|array $message + */ + public function caution($message); + + /** + * Formats a table. + * + * @param array $headers + * @param array $rows + */ + public function table(array $headers, array $rows); + + /** + * Asks a question. + * + * @param string $question + * @param string|null $default + * @param callable|null $validator + * + * @return string + */ + public function ask($question, $default = null, $validator = null); + + /** + * Asks a question with the user input hidden. + * + * @param string $question + * @param callable|null $validator + * + * @return string + */ + public function askHidden($question, $validator = null); + + /** + * Asks for confirmation. + * + * @param string $question + * @param bool $default + * + * @return bool + */ + public function confirm($question, $default = true); + + /** + * Asks a choice question. + * + * @param string $question + * @param array $choices + * @param string|int|null $default + * + * @return string + */ + public function choice($question, array $choices, $default = null); + + /** + * Add newline(s). + * + * @param int $count The number of newlines + */ + public function newLine($count = 1); + + /** + * Starts the progress output. + * + * @param int $max Maximum steps (0 if unknown) + */ + public function progressStart($max = 0); + + /** + * Advances the progress output X steps. + * + * @param int $step Number of steps to advance + */ + public function progressAdvance($step = 1); + + /** + * Finishes the progress output. + */ + public function progressFinish(); +} diff --git a/vendor/symfony/console/Style/SymfonyStyle.php b/vendor/symfony/console/Style/SymfonyStyle.php new file mode 100644 index 0000000000..53a7951e01 --- /dev/null +++ b/vendor/symfony/console/Style/SymfonyStyle.php @@ -0,0 +1,440 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Style; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Helper\Helper; +use Symfony\Component\Console\Helper\ProgressBar; +use Symfony\Component\Console\Helper\SymfonyQuestionHelper; +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Helper\TableCell; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\BufferedOutput; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\ChoiceQuestion; +use Symfony\Component\Console\Question\ConfirmationQuestion; +use Symfony\Component\Console\Question\Question; + +/** + * Output decorator helpers for the Symfony Style Guide. + * + * @author Kevin Bond + */ +class SymfonyStyle extends OutputStyle +{ + const MAX_LINE_LENGTH = 120; + + private $input; + private $questionHelper; + private $progressBar; + private $lineLength; + private $bufferedOutput; + + /** + * @param InputInterface $input + * @param OutputInterface $output + */ + public function __construct(InputInterface $input, OutputInterface $output) + { + $this->input = $input; + $this->bufferedOutput = new BufferedOutput($output->getVerbosity(), false, clone $output->getFormatter()); + // Windows cmd wraps lines as soon as the terminal width is reached, whether there are following chars or not. + $this->lineLength = min($this->getTerminalWidth() - (int) (DIRECTORY_SEPARATOR === '\\'), self::MAX_LINE_LENGTH); + + parent::__construct($output); + } + + /** + * Formats a message as a block of text. + * + * @param string|array $messages The message to write in the block + * @param string|null $type The block type (added in [] on first line) + * @param string|null $style The style to apply to the whole block + * @param string $prefix The prefix for the block + * @param bool $padding Whether to add vertical padding + */ + public function block($messages, $type = null, $style = null, $prefix = ' ', $padding = false) + { + $this->autoPrependBlock(); + $messages = is_array($messages) ? array_values($messages) : array($messages); + $indentLength = 0; + $lines = array(); + + if (null !== $type) { + $typePrefix = sprintf('[%s] ', $type); + $indentLength = strlen($typePrefix); + $lineIndentation = str_repeat(' ', $indentLength); + } + + // wrap and add newlines for each element + foreach ($messages as $key => $message) { + $message = OutputFormatter::escape($message); + $lines = array_merge($lines, explode(PHP_EOL, wordwrap($message, $this->lineLength - Helper::strlen($prefix) - $indentLength, PHP_EOL, true))); + + // prefix each line with a number of spaces equivalent to the type length + if (null !== $type) { + foreach ($lines as &$line) { + $line = $lineIndentation === substr($line, 0, $indentLength) ? $line : $lineIndentation.$line; + } + } + + if (count($messages) > 1 && $key < count($messages) - 1) { + $lines[] = ''; + } + } + + if (null !== $type) { + $lines[0] = substr_replace($lines[0], $typePrefix, 0, $indentLength); + } + + if ($padding && $this->isDecorated()) { + array_unshift($lines, ''); + $lines[] = ''; + } + + foreach ($lines as &$line) { + $line = sprintf('%s%s', $prefix, $line); + $line .= str_repeat(' ', $this->lineLength - Helper::strlenWithoutDecoration($this->getFormatter(), $line)); + + if ($style) { + $line = sprintf('<%s>%s', $style, $line); + } + } + + $this->writeln($lines); + $this->newLine(); + } + + /** + * {@inheritdoc} + */ + public function title($message) + { + $this->autoPrependBlock(); + $this->writeln(array( + sprintf('%s', $message), + sprintf('%s', str_repeat('=', Helper::strlenWithoutDecoration($this->getFormatter(), $message))), + )); + $this->newLine(); + } + + /** + * {@inheritdoc} + */ + public function section($message) + { + $this->autoPrependBlock(); + $this->writeln(array( + sprintf('%s', $message), + sprintf('%s', str_repeat('-', Helper::strlenWithoutDecoration($this->getFormatter(), $message))), + )); + $this->newLine(); + } + + /** + * {@inheritdoc} + */ + public function listing(array $elements) + { + $this->autoPrependText(); + $elements = array_map(function ($element) { + return sprintf(' * %s', $element); + }, $elements); + + $this->writeln($elements); + $this->newLine(); + } + + /** + * {@inheritdoc} + */ + public function text($message) + { + $this->autoPrependText(); + + $messages = is_array($message) ? array_values($message) : array($message); + foreach ($messages as $message) { + $this->writeln(sprintf(' %s', $message)); + } + } + + /** + * Formats a command comment. + * + * @param string|array $message + */ + public function comment($message) + { + $messages = is_array($message) ? array_values($message) : array($message); + foreach ($messages as &$message) { + $message = $this->getFormatter()->format($message); + } + + $this->block($messages, null, null, ' // '); + } + + /** + * {@inheritdoc} + */ + public function success($message) + { + $this->block($message, 'OK', 'fg=black;bg=green', ' ', true); + } + + /** + * {@inheritdoc} + */ + public function error($message) + { + $this->block($message, 'ERROR', 'fg=white;bg=red', ' ', true); + } + + /** + * {@inheritdoc} + */ + public function warning($message) + { + $this->block($message, 'WARNING', 'fg=white;bg=red', ' ', true); + } + + /** + * {@inheritdoc} + */ + public function note($message) + { + $this->block($message, 'NOTE', 'fg=yellow', ' ! '); + } + + /** + * {@inheritdoc} + */ + public function caution($message) + { + $this->block($message, 'CAUTION', 'fg=white;bg=red', ' ! ', true); + } + + /** + * {@inheritdoc} + */ + public function table(array $headers, array $rows) + { + array_walk_recursive($headers, function (&$value) { + if ($value instanceof TableCell) { + $value = new TableCell(sprintf('%s', $value), array( + 'colspan' => $value->getColspan(), + 'rowspan' => $value->getRowspan(), + )); + } else { + $value = sprintf('%s', $value); + } + }); + + $table = new Table($this); + $table->setHeaders($headers); + $table->setRows($rows); + $table->setStyle('symfony-style-guide'); + + $table->render(); + $this->newLine(); + } + + /** + * {@inheritdoc} + */ + public function ask($question, $default = null, $validator = null) + { + $question = new Question($question, $default); + $question->setValidator($validator); + + return $this->askQuestion($question); + } + + /** + * {@inheritdoc} + */ + public function askHidden($question, $validator = null) + { + $question = new Question($question); + + $question->setHidden(true); + $question->setValidator($validator); + + return $this->askQuestion($question); + } + + /** + * {@inheritdoc} + */ + public function confirm($question, $default = true) + { + return $this->askQuestion(new ConfirmationQuestion($question, $default)); + } + + /** + * {@inheritdoc} + */ + public function choice($question, array $choices, $default = null) + { + if (null !== $default) { + $values = array_flip($choices); + $default = $values[$default]; + } + + return $this->askQuestion(new ChoiceQuestion($question, $choices, $default)); + } + + /** + * {@inheritdoc} + */ + public function progressStart($max = 0) + { + $this->progressBar = $this->createProgressBar($max); + $this->progressBar->start(); + } + + /** + * {@inheritdoc} + */ + public function progressAdvance($step = 1) + { + $this->getProgressBar()->advance($step); + } + + /** + * {@inheritdoc} + */ + public function progressFinish() + { + $this->getProgressBar()->finish(); + $this->newLine(2); + $this->progressBar = null; + } + + /** + * {@inheritdoc} + */ + public function createProgressBar($max = 0) + { + $progressBar = parent::createProgressBar($max); + + if ('\\' !== DIRECTORY_SEPARATOR) { + $progressBar->setEmptyBarCharacter('░'); // light shade character \u2591 + $progressBar->setProgressCharacter(''); + $progressBar->setBarCharacter('▓'); // dark shade character \u2593 + } + + return $progressBar; + } + + /** + * @param Question $question + * + * @return string + */ + public function askQuestion(Question $question) + { + if ($this->input->isInteractive()) { + $this->autoPrependBlock(); + } + + if (!$this->questionHelper) { + $this->questionHelper = new SymfonyQuestionHelper(); + } + + $answer = $this->questionHelper->ask($this->input, $this, $question); + + if ($this->input->isInteractive()) { + $this->newLine(); + $this->bufferedOutput->write("\n"); + } + + return $answer; + } + + /** + * {@inheritdoc} + */ + public function writeln($messages, $type = self::OUTPUT_NORMAL) + { + parent::writeln($messages, $type); + $this->bufferedOutput->writeln($this->reduceBuffer($messages), $type); + } + + /** + * {@inheritdoc} + */ + public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL) + { + parent::write($messages, $newline, $type); + $this->bufferedOutput->write($this->reduceBuffer($messages), $newline, $type); + } + + /** + * {@inheritdoc} + */ + public function newLine($count = 1) + { + parent::newLine($count); + $this->bufferedOutput->write(str_repeat("\n", $count)); + } + + /** + * @return ProgressBar + */ + private function getProgressBar() + { + if (!$this->progressBar) { + throw new RuntimeException('The ProgressBar is not started.'); + } + + return $this->progressBar; + } + + private function getTerminalWidth() + { + $application = new Application(); + $dimensions = $application->getTerminalDimensions(); + + return $dimensions[0] ?: self::MAX_LINE_LENGTH; + } + + private function autoPrependBlock() + { + $chars = substr(str_replace(PHP_EOL, "\n", $this->bufferedOutput->fetch()), -2); + + if (!isset($chars[0])) { + return $this->newLine(); //empty history, so we should start with a new line. + } + //Prepend new line for each non LF chars (This means no blank line was output before) + $this->newLine(2 - substr_count($chars, "\n")); + } + + private function autoPrependText() + { + $fetched = $this->bufferedOutput->fetch(); + //Prepend new line if last char isn't EOL: + if ("\n" !== substr($fetched, -1)) { + $this->newLine(); + } + } + + private function reduceBuffer($messages) + { + // We need to know if the two last chars are PHP_EOL + // Preserve the last 4 chars inserted (PHP_EOL on windows is two chars) in the history buffer + return array_map(function ($value) { + return substr($value, -4); + }, array_merge(array($this->bufferedOutput->fetch()), (array) $messages)); + } +} diff --git a/vendor/symfony/console/Tester/ApplicationTester.php b/vendor/symfony/console/Tester/ApplicationTester.php new file mode 100644 index 0000000000..da8a19ce50 --- /dev/null +++ b/vendor/symfony/console/Tester/ApplicationTester.php @@ -0,0 +1,128 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tester; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\StreamOutput; + +/** + * Eases the testing of console applications. + * + * When testing an application, don't forget to disable the auto exit flag: + * + * $application = new Application(); + * $application->setAutoExit(false); + * + * @author Fabien Potencier + */ +class ApplicationTester +{ + private $application; + private $input; + private $output; + private $statusCode; + + /** + * Constructor. + * + * @param Application $application An Application instance to test. + */ + public function __construct(Application $application) + { + $this->application = $application; + } + + /** + * Executes the application. + * + * Available options: + * + * * interactive: Sets the input interactive flag + * * decorated: Sets the output decorated flag + * * verbosity: Sets the output verbosity flag + * + * @param array $input An array of arguments and options + * @param array $options An array of options + * + * @return int The command exit code + */ + public function run(array $input, $options = array()) + { + $this->input = new ArrayInput($input); + if (isset($options['interactive'])) { + $this->input->setInteractive($options['interactive']); + } + + $this->output = new StreamOutput(fopen('php://memory', 'w', false)); + if (isset($options['decorated'])) { + $this->output->setDecorated($options['decorated']); + } + if (isset($options['verbosity'])) { + $this->output->setVerbosity($options['verbosity']); + } + + return $this->statusCode = $this->application->run($this->input, $this->output); + } + + /** + * Gets the display returned by the last execution of the application. + * + * @param bool $normalize Whether to normalize end of lines to \n or not + * + * @return string The display + */ + public function getDisplay($normalize = false) + { + rewind($this->output->getStream()); + + $display = stream_get_contents($this->output->getStream()); + + if ($normalize) { + $display = str_replace(PHP_EOL, "\n", $display); + } + + return $display; + } + + /** + * Gets the input instance used by the last execution of the application. + * + * @return InputInterface The current input instance + */ + public function getInput() + { + return $this->input; + } + + /** + * Gets the output instance used by the last execution of the application. + * + * @return OutputInterface The current output instance + */ + public function getOutput() + { + return $this->output; + } + + /** + * Gets the status code returned by the last execution of the application. + * + * @return int The status code + */ + public function getStatusCode() + { + return $this->statusCode; + } +} diff --git a/vendor/symfony/console/Tester/CommandTester.php b/vendor/symfony/console/Tester/CommandTester.php new file mode 100644 index 0000000000..8d6486e11c --- /dev/null +++ b/vendor/symfony/console/Tester/CommandTester.php @@ -0,0 +1,132 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tester; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Output\StreamOutput; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Eases the testing of console commands. + * + * @author Fabien Potencier + */ +class CommandTester +{ + private $command; + private $input; + private $output; + private $statusCode; + + /** + * Constructor. + * + * @param Command $command A Command instance to test. + */ + public function __construct(Command $command) + { + $this->command = $command; + } + + /** + * Executes the command. + * + * Available execution options: + * + * * interactive: Sets the input interactive flag + * * decorated: Sets the output decorated flag + * * verbosity: Sets the output verbosity flag + * + * @param array $input An array of command arguments and options + * @param array $options An array of execution options + * + * @return int The command exit code + */ + public function execute(array $input, array $options = array()) + { + // set the command name automatically if the application requires + // this argument and no command name was passed + if (!isset($input['command']) + && (null !== $application = $this->command->getApplication()) + && $application->getDefinition()->hasArgument('command') + ) { + $input = array_merge(array('command' => $this->command->getName()), $input); + } + + $this->input = new ArrayInput($input); + if (isset($options['interactive'])) { + $this->input->setInteractive($options['interactive']); + } + + $this->output = new StreamOutput(fopen('php://memory', 'w', false)); + if (isset($options['decorated'])) { + $this->output->setDecorated($options['decorated']); + } + if (isset($options['verbosity'])) { + $this->output->setVerbosity($options['verbosity']); + } + + return $this->statusCode = $this->command->run($this->input, $this->output); + } + + /** + * Gets the display returned by the last execution of the command. + * + * @param bool $normalize Whether to normalize end of lines to \n or not + * + * @return string The display + */ + public function getDisplay($normalize = false) + { + rewind($this->output->getStream()); + + $display = stream_get_contents($this->output->getStream()); + + if ($normalize) { + $display = str_replace(PHP_EOL, "\n", $display); + } + + return $display; + } + + /** + * Gets the input instance used by the last execution of the command. + * + * @return InputInterface The current input instance + */ + public function getInput() + { + return $this->input; + } + + /** + * Gets the output instance used by the last execution of the command. + * + * @return OutputInterface The current output instance + */ + public function getOutput() + { + return $this->output; + } + + /** + * Gets the status code returned by the last execution of the application. + * + * @return int The status code + */ + public function getStatusCode() + { + return $this->statusCode; + } +} diff --git a/vendor/symfony/console/Tests/ApplicationTest.php b/vendor/symfony/console/Tests/ApplicationTest.php new file mode 100644 index 0000000000..927fa0bd82 --- /dev/null +++ b/vendor/symfony/console/Tests/ApplicationTest.php @@ -0,0 +1,1146 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Helper\FormatterHelper; +use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\NullOutput; +use Symfony\Component\Console\Output\Output; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\StreamOutput; +use Symfony\Component\Console\Tester\ApplicationTester; +use Symfony\Component\Console\Event\ConsoleCommandEvent; +use Symfony\Component\Console\Event\ConsoleExceptionEvent; +use Symfony\Component\Console\Event\ConsoleTerminateEvent; +use Symfony\Component\EventDispatcher\EventDispatcher; + +class ApplicationTest extends \PHPUnit_Framework_TestCase +{ + protected static $fixturesPath; + + public static function setUpBeforeClass() + { + self::$fixturesPath = realpath(__DIR__.'/Fixtures/'); + require_once self::$fixturesPath.'/FooCommand.php'; + require_once self::$fixturesPath.'/Foo1Command.php'; + require_once self::$fixturesPath.'/Foo2Command.php'; + require_once self::$fixturesPath.'/Foo3Command.php'; + require_once self::$fixturesPath.'/Foo4Command.php'; + require_once self::$fixturesPath.'/Foo5Command.php'; + require_once self::$fixturesPath.'/FoobarCommand.php'; + require_once self::$fixturesPath.'/BarBucCommand.php'; + require_once self::$fixturesPath.'/FooSubnamespaced1Command.php'; + require_once self::$fixturesPath.'/FooSubnamespaced2Command.php'; + } + + protected function normalizeLineBreaks($text) + { + return str_replace(PHP_EOL, "\n", $text); + } + + /** + * Replaces the dynamic placeholders of the command help text with a static version. + * The placeholder %command.full_name% includes the script path that is not predictable + * and can not be tested against. + */ + protected function ensureStaticCommandHelp(Application $application) + { + foreach ($application->all() as $command) { + $command->setHelp(str_replace('%command.full_name%', 'app/console %command.name%', $command->getHelp())); + } + } + + public function testConstructor() + { + $application = new Application('foo', 'bar'); + $this->assertEquals('foo', $application->getName(), '__construct() takes the application name as its first argument'); + $this->assertEquals('bar', $application->getVersion(), '__construct() takes the application version as its second argument'); + $this->assertEquals(array('help', 'list'), array_keys($application->all()), '__construct() registered the help and list commands by default'); + } + + public function testSetGetName() + { + $application = new Application(); + $application->setName('foo'); + $this->assertEquals('foo', $application->getName(), '->setName() sets the name of the application'); + } + + public function testSetGetVersion() + { + $application = new Application(); + $application->setVersion('bar'); + $this->assertEquals('bar', $application->getVersion(), '->setVersion() sets the version of the application'); + } + + public function testGetLongVersion() + { + $application = new Application('foo', 'bar'); + $this->assertEquals('foo version bar', $application->getLongVersion(), '->getLongVersion() returns the long version of the application'); + } + + public function testHelp() + { + $application = new Application(); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_gethelp.txt', $this->normalizeLineBreaks($application->getHelp()), '->getHelp() returns a help message'); + } + + public function testAll() + { + $application = new Application(); + $commands = $application->all(); + $this->assertInstanceOf('Symfony\\Component\\Console\\Command\\HelpCommand', $commands['help'], '->all() returns the registered commands'); + + $application->add(new \FooCommand()); + $commands = $application->all('foo'); + $this->assertCount(1, $commands, '->all() takes a namespace as its first argument'); + } + + public function testRegister() + { + $application = new Application(); + $command = $application->register('foo'); + $this->assertEquals('foo', $command->getName(), '->register() registers a new command'); + } + + public function testAdd() + { + $application = new Application(); + $application->add($foo = new \FooCommand()); + $commands = $application->all(); + $this->assertEquals($foo, $commands['foo:bar'], '->add() registers a command'); + + $application = new Application(); + $application->addCommands(array($foo = new \FooCommand(), $foo1 = new \Foo1Command())); + $commands = $application->all(); + $this->assertEquals(array($foo, $foo1), array($commands['foo:bar'], $commands['foo:bar1']), '->addCommands() registers an array of commands'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Command class "Foo5Command" is not correctly initialized. You probably forgot to call the parent constructor. + */ + public function testAddCommandWithEmptyConstructor() + { + $application = new Application(); + $application->add(new \Foo5Command()); + } + + public function testHasGet() + { + $application = new Application(); + $this->assertTrue($application->has('list'), '->has() returns true if a named command is registered'); + $this->assertFalse($application->has('afoobar'), '->has() returns false if a named command is not registered'); + + $application->add($foo = new \FooCommand()); + $this->assertTrue($application->has('afoobar'), '->has() returns true if an alias is registered'); + $this->assertEquals($foo, $application->get('foo:bar'), '->get() returns a command by name'); + $this->assertEquals($foo, $application->get('afoobar'), '->get() returns a command by alias'); + + $application = new Application(); + $application->add($foo = new \FooCommand()); + // simulate --help + $r = new \ReflectionObject($application); + $p = $r->getProperty('wantHelps'); + $p->setAccessible(true); + $p->setValue($application, true); + $command = $application->get('foo:bar'); + $this->assertInstanceOf('Symfony\Component\Console\Command\HelpCommand', $command, '->get() returns the help command if --help is provided as the input'); + } + + public function testSilentHelp() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $tester = new ApplicationTester($application); + $tester->run(array('-h' => true, '-q' => true), array('decorated' => false)); + + $this->assertEmpty($tester->getDisplay(true)); + } + + /** + * @expectedException Symfony\Component\Console\Exception\CommandNotFoundException + * @expectedExceptionMessage The command "foofoo" does not exist. + */ + public function testGetInvalidCommand() + { + $application = new Application(); + $application->get('foofoo'); + } + + public function testGetNamespaces() + { + $application = new Application(); + $application->add(new \FooCommand()); + $application->add(new \Foo1Command()); + $this->assertEquals(array('foo'), $application->getNamespaces(), '->getNamespaces() returns an array of unique used namespaces'); + } + + public function testFindNamespace() + { + $application = new Application(); + $application->add(new \FooCommand()); + $this->assertEquals('foo', $application->findNamespace('foo'), '->findNamespace() returns the given namespace if it exists'); + $this->assertEquals('foo', $application->findNamespace('f'), '->findNamespace() finds a namespace given an abbreviation'); + $application->add(new \Foo2Command()); + $this->assertEquals('foo', $application->findNamespace('foo'), '->findNamespace() returns the given namespace if it exists'); + } + + public function testFindNamespaceWithSubnamespaces() + { + $application = new Application(); + $application->add(new \FooSubnamespaced1Command()); + $application->add(new \FooSubnamespaced2Command()); + $this->assertEquals('foo', $application->findNamespace('foo'), '->findNamespace() returns commands even if the commands are only contained in subnamespaces'); + } + + /** + * @expectedException Symfony\Component\Console\Exception\CommandNotFoundException + * @expectedExceptionMessage The namespace "f" is ambiguous (foo, foo1). + */ + public function testFindAmbiguousNamespace() + { + $application = new Application(); + $application->add(new \BarBucCommand()); + $application->add(new \FooCommand()); + $application->add(new \Foo2Command()); + $application->findNamespace('f'); + } + + /** + * @expectedException Symfony\Component\Console\Exception\CommandNotFoundException + * @expectedExceptionMessage There are no commands defined in the "bar" namespace. + */ + public function testFindInvalidNamespace() + { + $application = new Application(); + $application->findNamespace('bar'); + } + + /** + * @expectedException Symfony\Component\Console\Exception\CommandNotFoundException + * @expectedExceptionMessage Command "foo1" is not defined + */ + public function testFindUniqueNameButNamespaceName() + { + $application = new Application(); + $application->add(new \FooCommand()); + $application->add(new \Foo1Command()); + $application->add(new \Foo2Command()); + + $application->find($commandName = 'foo1'); + } + + public function testFind() + { + $application = new Application(); + $application->add(new \FooCommand()); + + $this->assertInstanceOf('FooCommand', $application->find('foo:bar'), '->find() returns a command if its name exists'); + $this->assertInstanceOf('Symfony\Component\Console\Command\HelpCommand', $application->find('h'), '->find() returns a command if its name exists'); + $this->assertInstanceOf('FooCommand', $application->find('f:bar'), '->find() returns a command if the abbreviation for the namespace exists'); + $this->assertInstanceOf('FooCommand', $application->find('f:b'), '->find() returns a command if the abbreviation for the namespace and the command name exist'); + $this->assertInstanceOf('FooCommand', $application->find('a'), '->find() returns a command if the abbreviation exists for an alias'); + } + + /** + * @dataProvider provideAmbiguousAbbreviations + */ + public function testFindWithAmbiguousAbbreviations($abbreviation, $expectedExceptionMessage) + { + $this->setExpectedException('Symfony\Component\Console\Exception\CommandNotFoundException', $expectedExceptionMessage); + + $application = new Application(); + $application->add(new \FooCommand()); + $application->add(new \Foo1Command()); + $application->add(new \Foo2Command()); + + $application->find($abbreviation); + } + + public function provideAmbiguousAbbreviations() + { + return array( + array('f', 'Command "f" is not defined.'), + array('a', 'Command "a" is ambiguous (afoobar, afoobar1 and 1 more).'), + array('foo:b', 'Command "foo:b" is ambiguous (foo:bar, foo:bar1 and 1 more).'), + ); + } + + public function testFindCommandEqualNamespace() + { + $application = new Application(); + $application->add(new \Foo3Command()); + $application->add(new \Foo4Command()); + + $this->assertInstanceOf('Foo3Command', $application->find('foo3:bar'), '->find() returns the good command even if a namespace has same name'); + $this->assertInstanceOf('Foo4Command', $application->find('foo3:bar:toh'), '->find() returns a command even if its namespace equals another command name'); + } + + public function testFindCommandWithAmbiguousNamespacesButUniqueName() + { + $application = new Application(); + $application->add(new \FooCommand()); + $application->add(new \FoobarCommand()); + + $this->assertInstanceOf('FoobarCommand', $application->find('f:f')); + } + + public function testFindCommandWithMissingNamespace() + { + $application = new Application(); + $application->add(new \Foo4Command()); + + $this->assertInstanceOf('Foo4Command', $application->find('f::t')); + } + + /** + * @dataProvider provideInvalidCommandNamesSingle + * @expectedException Symfony\Component\Console\Exception\CommandNotFoundException + * @expectedExceptionMessage Did you mean this + */ + public function testFindAlternativeExceptionMessageSingle($name) + { + $application = new Application(); + $application->add(new \Foo3Command()); + $application->find($name); + } + + public function provideInvalidCommandNamesSingle() + { + return array( + array('foo3:baR'), + array('foO3:bar'), + ); + } + + public function testFindAlternativeExceptionMessageMultiple() + { + $application = new Application(); + $application->add(new \FooCommand()); + $application->add(new \Foo1Command()); + $application->add(new \Foo2Command()); + + // Command + plural + try { + $application->find('foo:baR'); + $this->fail('->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + $this->assertRegExp('/Did you mean one of these/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + $this->assertRegExp('/foo1:bar/', $e->getMessage()); + $this->assertRegExp('/foo:bar/', $e->getMessage()); + } + + // Namespace + plural + try { + $application->find('foo2:bar'); + $this->fail('->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + $this->assertRegExp('/Did you mean one of these/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + $this->assertRegExp('/foo1/', $e->getMessage()); + } + + $application->add(new \Foo3Command()); + $application->add(new \Foo4Command()); + + // Subnamespace + plural + try { + $a = $application->find('foo3:'); + $this->fail('->find() should throw an Symfony\Component\Console\Exception\CommandNotFoundException if a command is ambiguous because of a subnamespace, with alternatives'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e); + $this->assertRegExp('/foo3:bar/', $e->getMessage()); + $this->assertRegExp('/foo3:bar:toh/', $e->getMessage()); + } + } + + public function testFindAlternativeCommands() + { + $application = new Application(); + + $application->add(new \FooCommand()); + $application->add(new \Foo1Command()); + $application->add(new \Foo2Command()); + + try { + $application->find($commandName = 'Unknown command'); + $this->fail('->find() throws a CommandNotFoundException if command does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if command does not exist'); + $this->assertSame(array(), $e->getAlternatives()); + $this->assertEquals(sprintf('Command "%s" is not defined.', $commandName), $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, without alternatives'); + } + + // Test if "bar1" command throw a "CommandNotFoundException" and does not contain + // "foo:bar" as alternative because "bar1" is too far from "foo:bar" + try { + $application->find($commandName = 'bar1'); + $this->fail('->find() throws a CommandNotFoundException if command does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if command does not exist'); + $this->assertSame(array('afoobar1', 'foo:bar1'), $e->getAlternatives()); + $this->assertRegExp(sprintf('/Command "%s" is not defined./', $commandName), $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + $this->assertRegExp('/afoobar1/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternative : "afoobar1"'); + $this->assertRegExp('/foo:bar1/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternative : "foo:bar1"'); + $this->assertNotRegExp('/foo:bar(?>!1)/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, without "foo:bar" alternative'); + } + } + + public function testFindAlternativeCommandsWithAnAlias() + { + $fooCommand = new \FooCommand(); + $fooCommand->setAliases(array('foo2')); + + $application = new Application(); + $application->add($fooCommand); + + $result = $application->find('foo'); + + $this->assertSame($fooCommand, $result); + } + + public function testFindAlternativeNamespace() + { + $application = new Application(); + + $application->add(new \FooCommand()); + $application->add(new \Foo1Command()); + $application->add(new \Foo2Command()); + $application->add(new \foo3Command()); + + try { + $application->find('Unknown-namespace:Unknown-command'); + $this->fail('->find() throws a CommandNotFoundException if namespace does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if namespace does not exist'); + $this->assertSame(array(), $e->getAlternatives()); + $this->assertEquals('There are no commands defined in the "Unknown-namespace" namespace.', $e->getMessage(), '->find() throws a CommandNotFoundException if namespace does not exist, without alternatives'); + } + + try { + $application->find('foo2:command'); + $this->fail('->find() throws a CommandNotFoundException if namespace does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if namespace does not exist'); + $this->assertCount(3, $e->getAlternatives()); + $this->assertContains('foo', $e->getAlternatives()); + $this->assertContains('foo1', $e->getAlternatives()); + $this->assertContains('foo3', $e->getAlternatives()); + $this->assertRegExp('/There are no commands defined in the "foo2" namespace./', $e->getMessage(), '->find() throws a CommandNotFoundException if namespace does not exist, with alternative'); + $this->assertRegExp('/foo/', $e->getMessage(), '->find() throws a CommandNotFoundException if namespace does not exist, with alternative : "foo"'); + $this->assertRegExp('/foo1/', $e->getMessage(), '->find() throws a CommandNotFoundException if namespace does not exist, with alternative : "foo1"'); + $this->assertRegExp('/foo3/', $e->getMessage(), '->find() throws a CommandNotFoundException if namespace does not exist, with alternative : "foo3"'); + } + } + + public function testFindNamespaceDoesNotFailOnDeepSimilarNamespaces() + { + $application = $this->getMock('Symfony\Component\Console\Application', array('getNamespaces')); + $application->expects($this->once()) + ->method('getNamespaces') + ->will($this->returnValue(array('foo:sublong', 'bar:sub'))); + + $this->assertEquals('foo:sublong', $application->findNamespace('f:sub')); + } + + /** + * @expectedException Symfony\Component\Console\Exception\CommandNotFoundException + * @expectedExceptionMessage Command "foo::bar" is not defined. + */ + public function testFindWithDoubleColonInNameThrowsException() + { + $application = new Application(); + $application->add(new \FooCommand()); + $application->add(new \Foo4Command()); + $application->find('foo::bar'); + } + + public function testSetCatchExceptions() + { + $application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth')); + $application->setAutoExit(false); + $application->expects($this->any()) + ->method('getTerminalWidth') + ->will($this->returnValue(120)); + $tester = new ApplicationTester($application); + + $application->setCatchExceptions(true); + $tester->run(array('command' => 'foo'), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception1.txt', $tester->getDisplay(true), '->setCatchExceptions() sets the catch exception flag'); + + $application->setCatchExceptions(false); + try { + $tester->run(array('command' => 'foo'), array('decorated' => false)); + $this->fail('->setCatchExceptions() sets the catch exception flag'); + } catch (\Exception $e) { + $this->assertInstanceOf('\Exception', $e, '->setCatchExceptions() sets the catch exception flag'); + $this->assertEquals('Command "foo" is not defined.', $e->getMessage(), '->setCatchExceptions() sets the catch exception flag'); + } + } + + /** + * @group legacy + */ + public function testLegacyAsText() + { + $application = new Application(); + $application->add(new \FooCommand()); + $this->ensureStaticCommandHelp($application); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_astext1.txt', $this->normalizeLineBreaks($application->asText()), '->asText() returns a text representation of the application'); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_astext2.txt', $this->normalizeLineBreaks($application->asText('foo')), '->asText() returns a text representation of the application'); + } + + /** + * @group legacy + */ + public function testLegacyAsXml() + { + $application = new Application(); + $application->add(new \FooCommand()); + $this->ensureStaticCommandHelp($application); + $this->assertXmlStringEqualsXmlFile(self::$fixturesPath.'/application_asxml1.txt', $application->asXml(), '->asXml() returns an XML representation of the application'); + $this->assertXmlStringEqualsXmlFile(self::$fixturesPath.'/application_asxml2.txt', $application->asXml('foo'), '->asXml() returns an XML representation of the application'); + } + + public function testRenderException() + { + $application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth')); + $application->setAutoExit(false); + $application->expects($this->any()) + ->method('getTerminalWidth') + ->will($this->returnValue(120)); + $tester = new ApplicationTester($application); + + $tester->run(array('command' => 'foo'), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception1.txt', $tester->getDisplay(true), '->renderException() renders a pretty exception'); + + $tester->run(array('command' => 'foo'), array('decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE)); + $this->assertContains('Exception trace', $tester->getDisplay(), '->renderException() renders a pretty exception with a stack trace when verbosity is verbose'); + + $tester->run(array('command' => 'list', '--foo' => true), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception2.txt', $tester->getDisplay(true), '->renderException() renders the command synopsis when an exception occurs in the context of a command'); + + $application->add(new \Foo3Command()); + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'foo3:bar'), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception3.txt', $tester->getDisplay(true), '->renderException() renders a pretty exceptions with previous exceptions'); + + $tester->run(array('command' => 'foo3:bar'), array('decorated' => true)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception3decorated.txt', $tester->getDisplay(true), '->renderException() renders a pretty exceptions with previous exceptions'); + + $application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth')); + $application->setAutoExit(false); + $application->expects($this->any()) + ->method('getTerminalWidth') + ->will($this->returnValue(32)); + $tester = new ApplicationTester($application); + + $tester->run(array('command' => 'foo'), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception4.txt', $tester->getDisplay(true), '->renderException() wraps messages when they are bigger than the terminal'); + } + + public function testRenderExceptionWithDoubleWidthCharacters() + { + $application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth')); + $application->setAutoExit(false); + $application->expects($this->any()) + ->method('getTerminalWidth') + ->will($this->returnValue(120)); + $application->register('foo')->setCode(function () { + throw new \Exception('エラーメッセージ'); + }); + $tester = new ApplicationTester($application); + + $tester->run(array('command' => 'foo'), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth1.txt', $tester->getDisplay(true), '->renderException() renders a pretty exceptions with previous exceptions'); + + $tester->run(array('command' => 'foo'), array('decorated' => true)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth1decorated.txt', $tester->getDisplay(true), '->renderException() renders a pretty exceptions with previous exceptions'); + + $application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth')); + $application->setAutoExit(false); + $application->expects($this->any()) + ->method('getTerminalWidth') + ->will($this->returnValue(32)); + $application->register('foo')->setCode(function () { + throw new \Exception('コマンドの実行中にエラーが発生しました。'); + }); + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'foo'), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth2.txt', $tester->getDisplay(true), '->renderException() wraps messages when they are bigger than the terminal'); + } + + public function testRun() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + $application->add($command = new \Foo1Command()); + $_SERVER['argv'] = array('cli.php', 'foo:bar1'); + + ob_start(); + $application->run(); + ob_end_clean(); + + $this->assertInstanceOf('Symfony\Component\Console\Input\ArgvInput', $command->input, '->run() creates an ArgvInput by default if none is given'); + $this->assertInstanceOf('Symfony\Component\Console\Output\ConsoleOutput', $command->output, '->run() creates a ConsoleOutput by default if none is given'); + + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $this->ensureStaticCommandHelp($application); + $tester = new ApplicationTester($application); + + $tester->run(array(), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run1.txt', $tester->getDisplay(true), '->run() runs the list command if no argument is passed'); + + $tester->run(array('--help' => true), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run2.txt', $tester->getDisplay(true), '->run() runs the help command if --help is passed'); + + $tester->run(array('-h' => true), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run2.txt', $tester->getDisplay(true), '->run() runs the help command if -h is passed'); + + $tester->run(array('command' => 'list', '--help' => true), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run3.txt', $tester->getDisplay(true), '->run() displays the help if --help is passed'); + + $tester->run(array('command' => 'list', '-h' => true), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run3.txt', $tester->getDisplay(true), '->run() displays the help if -h is passed'); + + $tester->run(array('--ansi' => true)); + $this->assertTrue($tester->getOutput()->isDecorated(), '->run() forces color output if --ansi is passed'); + + $tester->run(array('--no-ansi' => true)); + $this->assertFalse($tester->getOutput()->isDecorated(), '->run() forces color output to be disabled if --no-ansi is passed'); + + $tester->run(array('--version' => true), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run4.txt', $tester->getDisplay(true), '->run() displays the program version if --version is passed'); + + $tester->run(array('-V' => true), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run4.txt', $tester->getDisplay(true), '->run() displays the program version if -v is passed'); + + $tester->run(array('command' => 'list', '--quiet' => true)); + $this->assertSame('', $tester->getDisplay(), '->run() removes all output if --quiet is passed'); + + $tester->run(array('command' => 'list', '-q' => true)); + $this->assertSame('', $tester->getDisplay(), '->run() removes all output if -q is passed'); + + $tester->run(array('command' => 'list', '--verbose' => true)); + $this->assertSame(Output::VERBOSITY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if --verbose is passed'); + + $tester->run(array('command' => 'list', '--verbose' => 1)); + $this->assertSame(Output::VERBOSITY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if --verbose=1 is passed'); + + $tester->run(array('command' => 'list', '--verbose' => 2)); + $this->assertSame(Output::VERBOSITY_VERY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to very verbose if --verbose=2 is passed'); + + $tester->run(array('command' => 'list', '--verbose' => 3)); + $this->assertSame(Output::VERBOSITY_DEBUG, $tester->getOutput()->getVerbosity(), '->run() sets the output to debug if --verbose=3 is passed'); + + $tester->run(array('command' => 'list', '--verbose' => 4)); + $this->assertSame(Output::VERBOSITY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if unknown --verbose level is passed'); + + $tester->run(array('command' => 'list', '-v' => true)); + $this->assertSame(Output::VERBOSITY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if -v is passed'); + + $tester->run(array('command' => 'list', '-vv' => true)); + $this->assertSame(Output::VERBOSITY_VERY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if -v is passed'); + + $tester->run(array('command' => 'list', '-vvv' => true)); + $this->assertSame(Output::VERBOSITY_DEBUG, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if -v is passed'); + + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + $application->add(new \FooCommand()); + $tester = new ApplicationTester($application); + + $tester->run(array('command' => 'foo:bar', '--no-interaction' => true), array('decorated' => false)); + $this->assertSame('called'.PHP_EOL, $tester->getDisplay(), '->run() does not call interact() if --no-interaction is passed'); + + $tester->run(array('command' => 'foo:bar', '-n' => true), array('decorated' => false)); + $this->assertSame('called'.PHP_EOL, $tester->getDisplay(), '->run() does not call interact() if -n is passed'); + } + + /** + * Issue #9285. + * + * If the "verbose" option is just before an argument in ArgvInput, + * an argument value should not be treated as verbosity value. + * This test will fail with "Not enough arguments." if broken + */ + public function testVerboseValueNotBreakArguments() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + $application->add(new \FooCommand()); + + $output = new StreamOutput(fopen('php://memory', 'w', false)); + + $input = new ArgvInput(array('cli.php', '-v', 'foo:bar')); + $application->run($input, $output); + + $input = new ArgvInput(array('cli.php', '--verbose', 'foo:bar')); + $application->run($input, $output); + } + + public function testRunReturnsIntegerExitCode() + { + $exception = new \Exception('', 4); + + $application = $this->getMock('Symfony\Component\Console\Application', array('doRun')); + $application->setAutoExit(false); + $application->expects($this->once()) + ->method('doRun') + ->will($this->throwException($exception)); + + $exitCode = $application->run(new ArrayInput(array()), new NullOutput()); + + $this->assertSame(4, $exitCode, '->run() returns integer exit code extracted from raised exception'); + } + + public function testRunReturnsExitCodeOneForExceptionCodeZero() + { + $exception = new \Exception('', 0); + + $application = $this->getMock('Symfony\Component\Console\Application', array('doRun')); + $application->setAutoExit(false); + $application->expects($this->once()) + ->method('doRun') + ->will($this->throwException($exception)); + + $exitCode = $application->run(new ArrayInput(array()), new NullOutput()); + + $this->assertSame(1, $exitCode, '->run() returns exit code 1 when exception code is 0'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage An option with shortcut "e" already exists. + */ + public function testAddingOptionWithDuplicateShortcut() + { + $dispatcher = new EventDispatcher(); + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + $application->setDispatcher($dispatcher); + + $application->getDefinition()->addOption(new InputOption('--env', '-e', InputOption::VALUE_REQUIRED, 'Environment')); + + $application + ->register('foo') + ->setAliases(array('f')) + ->setDefinition(array(new InputOption('survey', 'e', InputOption::VALUE_REQUIRED, 'My option with a shortcut.'))) + ->setCode(function (InputInterface $input, OutputInterface $output) {}) + ; + + $input = new ArrayInput(array('command' => 'foo')); + $output = new NullOutput(); + + $application->run($input, $output); + } + + /** + * @expectedException \LogicException + * @dataProvider getAddingAlreadySetDefinitionElementData + */ + public function testAddingAlreadySetDefinitionElementData($def) + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + $application + ->register('foo') + ->setDefinition(array($def)) + ->setCode(function (InputInterface $input, OutputInterface $output) {}) + ; + + $input = new ArrayInput(array('command' => 'foo')); + $output = new NullOutput(); + $application->run($input, $output); + } + + public function getAddingAlreadySetDefinitionElementData() + { + return array( + array(new InputArgument('command', InputArgument::REQUIRED)), + array(new InputOption('quiet', '', InputOption::VALUE_NONE)), + array(new InputOption('query', 'q', InputOption::VALUE_NONE)), + ); + } + + public function testGetDefaultHelperSetReturnsDefaultValues() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $helperSet = $application->getHelperSet(); + + $this->assertTrue($helperSet->has('formatter')); + $this->assertTrue($helperSet->has('dialog')); + $this->assertTrue($helperSet->has('progress')); + } + + public function testAddingSingleHelperSetOverwritesDefaultValues() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $application->setHelperSet(new HelperSet(array(new FormatterHelper()))); + + $helperSet = $application->getHelperSet(); + + $this->assertTrue($helperSet->has('formatter')); + + // no other default helper set should be returned + $this->assertFalse($helperSet->has('dialog')); + $this->assertFalse($helperSet->has('progress')); + } + + public function testOverwritingDefaultHelperSetOverwritesDefaultValues() + { + $application = new CustomApplication(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $application->setHelperSet(new HelperSet(array(new FormatterHelper()))); + + $helperSet = $application->getHelperSet(); + + $this->assertTrue($helperSet->has('formatter')); + + // no other default helper set should be returned + $this->assertFalse($helperSet->has('dialog')); + $this->assertFalse($helperSet->has('progress')); + } + + public function testGetDefaultInputDefinitionReturnsDefaultValues() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $inputDefinition = $application->getDefinition(); + + $this->assertTrue($inputDefinition->hasArgument('command')); + + $this->assertTrue($inputDefinition->hasOption('help')); + $this->assertTrue($inputDefinition->hasOption('quiet')); + $this->assertTrue($inputDefinition->hasOption('verbose')); + $this->assertTrue($inputDefinition->hasOption('version')); + $this->assertTrue($inputDefinition->hasOption('ansi')); + $this->assertTrue($inputDefinition->hasOption('no-ansi')); + $this->assertTrue($inputDefinition->hasOption('no-interaction')); + } + + public function testOverwritingDefaultInputDefinitionOverwritesDefaultValues() + { + $application = new CustomApplication(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $inputDefinition = $application->getDefinition(); + + // check whether the default arguments and options are not returned any more + $this->assertFalse($inputDefinition->hasArgument('command')); + + $this->assertFalse($inputDefinition->hasOption('help')); + $this->assertFalse($inputDefinition->hasOption('quiet')); + $this->assertFalse($inputDefinition->hasOption('verbose')); + $this->assertFalse($inputDefinition->hasOption('version')); + $this->assertFalse($inputDefinition->hasOption('ansi')); + $this->assertFalse($inputDefinition->hasOption('no-ansi')); + $this->assertFalse($inputDefinition->hasOption('no-interaction')); + + $this->assertTrue($inputDefinition->hasOption('custom')); + } + + public function testSettingCustomInputDefinitionOverwritesDefaultValues() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $application->setDefinition(new InputDefinition(array(new InputOption('--custom', '-c', InputOption::VALUE_NONE, 'Set the custom input definition.')))); + + $inputDefinition = $application->getDefinition(); + + // check whether the default arguments and options are not returned any more + $this->assertFalse($inputDefinition->hasArgument('command')); + + $this->assertFalse($inputDefinition->hasOption('help')); + $this->assertFalse($inputDefinition->hasOption('quiet')); + $this->assertFalse($inputDefinition->hasOption('verbose')); + $this->assertFalse($inputDefinition->hasOption('version')); + $this->assertFalse($inputDefinition->hasOption('ansi')); + $this->assertFalse($inputDefinition->hasOption('no-ansi')); + $this->assertFalse($inputDefinition->hasOption('no-interaction')); + + $this->assertTrue($inputDefinition->hasOption('custom')); + } + + public function testRunWithDispatcher() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setDispatcher($this->getDispatcher()); + + $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { + $output->write('foo.'); + }); + + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'foo')); + $this->assertEquals('before.foo.after.'.PHP_EOL, $tester->getDisplay()); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage caught + */ + public function testRunWithExceptionAndDispatcher() + { + $application = new Application(); + $application->setDispatcher($this->getDispatcher()); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { + throw new \RuntimeException('foo'); + }); + + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'foo')); + } + + public function testRunDispatchesAllEventsWithException() + { + $application = new Application(); + $application->setDispatcher($this->getDispatcher()); + $application->setAutoExit(false); + + $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { + $output->write('foo.'); + + throw new \RuntimeException('foo'); + }); + + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'foo')); + $this->assertContains('before.foo.caught.after.', $tester->getDisplay()); + } + + public function testRunWithDispatcherSkippingCommand() + { + $application = new Application(); + $application->setDispatcher($this->getDispatcher(true)); + $application->setAutoExit(false); + + $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { + $output->write('foo.'); + }); + + $tester = new ApplicationTester($application); + $exitCode = $tester->run(array('command' => 'foo')); + $this->assertContains('before.after.', $tester->getDisplay()); + $this->assertEquals(ConsoleCommandEvent::RETURN_CODE_DISABLED, $exitCode); + } + + public function testRunWithDispatcherAccessingInputOptions() + { + $noInteractionValue = null; + $quietValue = null; + + $dispatcher = $this->getDispatcher(); + $dispatcher->addListener('console.command', function (ConsoleCommandEvent $event) use (&$noInteractionValue, &$quietValue) { + $input = $event->getInput(); + + $noInteractionValue = $input->getOption('no-interaction'); + $quietValue = $input->getOption('quiet'); + }); + + $application = new Application(); + $application->setDispatcher($dispatcher); + $application->setAutoExit(false); + + $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { + $output->write('foo.'); + }); + + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'foo', '--no-interaction' => true)); + + $this->assertTrue($noInteractionValue); + $this->assertFalse($quietValue); + } + + public function testRunWithDispatcherAddingInputOptions() + { + $extraValue = null; + + $dispatcher = $this->getDispatcher(); + $dispatcher->addListener('console.command', function (ConsoleCommandEvent $event) use (&$extraValue) { + $definition = $event->getCommand()->getDefinition(); + $input = $event->getInput(); + + $definition->addOption(new InputOption('extra', null, InputOption::VALUE_REQUIRED)); + $input->bind($definition); + + $extraValue = $input->getOption('extra'); + }); + + $application = new Application(); + $application->setDispatcher($dispatcher); + $application->setAutoExit(false); + + $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { + $output->write('foo.'); + }); + + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'foo', '--extra' => 'some test value')); + + $this->assertEquals('some test value', $extraValue); + } + + public function testTerminalDimensions() + { + $application = new Application(); + $originalDimensions = $application->getTerminalDimensions(); + $this->assertCount(2, $originalDimensions); + + $width = 80; + if ($originalDimensions[0] == $width) { + $width = 100; + } + + $application->setTerminalDimensions($width, 80); + $this->assertSame(array($width, 80), $application->getTerminalDimensions()); + } + + protected function getDispatcher($skipCommand = false) + { + $dispatcher = new EventDispatcher(); + $dispatcher->addListener('console.command', function (ConsoleCommandEvent $event) use ($skipCommand) { + $event->getOutput()->write('before.'); + + if ($skipCommand) { + $event->disableCommand(); + } + }); + $dispatcher->addListener('console.terminate', function (ConsoleTerminateEvent $event) use ($skipCommand) { + $event->getOutput()->writeln('after.'); + + if (!$skipCommand) { + $event->setExitCode(113); + } + }); + $dispatcher->addListener('console.exception', function (ConsoleExceptionEvent $event) { + $event->getOutput()->write('caught.'); + + $event->setException(new \LogicException('caught.', $event->getExitCode(), $event->getException())); + }); + + return $dispatcher; + } + + public function testSetRunCustomDefaultCommand() + { + $command = new \FooCommand(); + + $application = new Application(); + $application->setAutoExit(false); + $application->add($command); + $application->setDefaultCommand($command->getName()); + + $tester = new ApplicationTester($application); + $tester->run(array()); + $this->assertEquals('interact called'.PHP_EOL.'called'.PHP_EOL, $tester->getDisplay(), 'Application runs the default set command if different from \'list\' command'); + + $application = new CustomDefaultCommandApplication(); + $application->setAutoExit(false); + + $tester = new ApplicationTester($application); + $tester->run(array()); + + $this->assertEquals('interact called'.PHP_EOL.'called'.PHP_EOL, $tester->getDisplay(), 'Application runs the default set command if different from \'list\' command'); + } + + /** + * @requires function posix_isatty + */ + public function testCanCheckIfTerminalIsInteractive() + { + $application = new CustomDefaultCommandApplication(); + $application->setAutoExit(false); + + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'help')); + + $this->assertFalse($tester->getInput()->hasParameterOption(array('--no-interaction', '-n'))); + + $inputStream = $application->getHelperSet()->get('question')->getInputStream(); + $this->assertEquals($tester->getInput()->isInteractive(), @posix_isatty($inputStream)); + } +} + +class CustomApplication extends Application +{ + /** + * Overwrites the default input definition. + * + * @return InputDefinition An InputDefinition instance + */ + protected function getDefaultInputDefinition() + { + return new InputDefinition(array(new InputOption('--custom', '-c', InputOption::VALUE_NONE, 'Set the custom input definition.'))); + } + + /** + * Gets the default helper set with the helpers that should always be available. + * + * @return HelperSet A HelperSet instance + */ + protected function getDefaultHelperSet() + { + return new HelperSet(array(new FormatterHelper())); + } +} + +class CustomDefaultCommandApplication extends Application +{ + /** + * Overwrites the constructor in order to set a different default command. + */ + public function __construct() + { + parent::__construct(); + + $command = new \FooCommand(); + $this->add($command); + $this->setDefaultCommand($command->getName()); + } +} diff --git a/vendor/symfony/console/Tests/Command/CommandTest.php b/vendor/symfony/console/Tests/Command/CommandTest.php new file mode 100644 index 0000000000..e8836d8c61 --- /dev/null +++ b/vendor/symfony/console/Tests/Command/CommandTest.php @@ -0,0 +1,395 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Command; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\FormatterHelper; +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\StringInput; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\NullOutput; +use Symfony\Component\Console\Tester\CommandTester; + +class CommandTest extends \PHPUnit_Framework_TestCase +{ + protected static $fixturesPath; + + public static function setUpBeforeClass() + { + self::$fixturesPath = __DIR__.'/../Fixtures/'; + require_once self::$fixturesPath.'/TestCommand.php'; + } + + public function testConstructor() + { + $command = new Command('foo:bar'); + $this->assertEquals('foo:bar', $command->getName(), '__construct() takes the command name as its first argument'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage The command defined in "Symfony\Component\Console\Command\Command" cannot have an empty name. + */ + public function testCommandNameCannotBeEmpty() + { + new Command(); + } + + public function testSetApplication() + { + $application = new Application(); + $command = new \TestCommand(); + $command->setApplication($application); + $this->assertEquals($application, $command->getApplication(), '->setApplication() sets the current application'); + } + + public function testSetGetDefinition() + { + $command = new \TestCommand(); + $ret = $command->setDefinition($definition = new InputDefinition()); + $this->assertEquals($command, $ret, '->setDefinition() implements a fluent interface'); + $this->assertEquals($definition, $command->getDefinition(), '->setDefinition() sets the current InputDefinition instance'); + $command->setDefinition(array(new InputArgument('foo'), new InputOption('bar'))); + $this->assertTrue($command->getDefinition()->hasArgument('foo'), '->setDefinition() also takes an array of InputArguments and InputOptions as an argument'); + $this->assertTrue($command->getDefinition()->hasOption('bar'), '->setDefinition() also takes an array of InputArguments and InputOptions as an argument'); + $command->setDefinition(new InputDefinition()); + } + + public function testAddArgument() + { + $command = new \TestCommand(); + $ret = $command->addArgument('foo'); + $this->assertEquals($command, $ret, '->addArgument() implements a fluent interface'); + $this->assertTrue($command->getDefinition()->hasArgument('foo'), '->addArgument() adds an argument to the command'); + } + + public function testAddOption() + { + $command = new \TestCommand(); + $ret = $command->addOption('foo'); + $this->assertEquals($command, $ret, '->addOption() implements a fluent interface'); + $this->assertTrue($command->getDefinition()->hasOption('foo'), '->addOption() adds an option to the command'); + } + + public function testGetNamespaceGetNameSetName() + { + $command = new \TestCommand(); + $this->assertEquals('namespace:name', $command->getName(), '->getName() returns the command name'); + $command->setName('foo'); + $this->assertEquals('foo', $command->getName(), '->setName() sets the command name'); + + $ret = $command->setName('foobar:bar'); + $this->assertEquals($command, $ret, '->setName() implements a fluent interface'); + $this->assertEquals('foobar:bar', $command->getName(), '->setName() sets the command name'); + } + + /** + * @dataProvider provideInvalidCommandNames + */ + public function testInvalidCommandNames($name) + { + $this->setExpectedException('InvalidArgumentException', sprintf('Command name "%s" is invalid.', $name)); + + $command = new \TestCommand(); + $command->setName($name); + } + + public function provideInvalidCommandNames() + { + return array( + array(''), + array('foo:'), + ); + } + + public function testGetSetDescription() + { + $command = new \TestCommand(); + $this->assertEquals('description', $command->getDescription(), '->getDescription() returns the description'); + $ret = $command->setDescription('description1'); + $this->assertEquals($command, $ret, '->setDescription() implements a fluent interface'); + $this->assertEquals('description1', $command->getDescription(), '->setDescription() sets the description'); + } + + public function testGetSetHelp() + { + $command = new \TestCommand(); + $this->assertEquals('help', $command->getHelp(), '->getHelp() returns the help'); + $ret = $command->setHelp('help1'); + $this->assertEquals($command, $ret, '->setHelp() implements a fluent interface'); + $this->assertEquals('help1', $command->getHelp(), '->setHelp() sets the help'); + $command->setHelp(''); + $this->assertEquals('', $command->getHelp(), '->getHelp() does not fall back to the description'); + } + + public function testGetProcessedHelp() + { + $command = new \TestCommand(); + $command->setHelp('The %command.name% command does... Example: php %command.full_name%.'); + $this->assertContains('The namespace:name command does...', $command->getProcessedHelp(), '->getProcessedHelp() replaces %command.name% correctly'); + $this->assertNotContains('%command.full_name%', $command->getProcessedHelp(), '->getProcessedHelp() replaces %command.full_name%'); + + $command = new \TestCommand(); + $command->setHelp(''); + $this->assertContains('description', $command->getProcessedHelp(), '->getProcessedHelp() falls back to the description'); + } + + public function testGetSetAliases() + { + $command = new \TestCommand(); + $this->assertEquals(array('name'), $command->getAliases(), '->getAliases() returns the aliases'); + $ret = $command->setAliases(array('name1')); + $this->assertEquals($command, $ret, '->setAliases() implements a fluent interface'); + $this->assertEquals(array('name1'), $command->getAliases(), '->setAliases() sets the aliases'); + } + + public function testGetSynopsis() + { + $command = new \TestCommand(); + $command->addOption('foo'); + $command->addArgument('bar'); + $this->assertEquals('namespace:name [--foo] [--] []', $command->getSynopsis(), '->getSynopsis() returns the synopsis'); + } + + public function testGetHelper() + { + $application = new Application(); + $command = new \TestCommand(); + $command->setApplication($application); + $formatterHelper = new FormatterHelper(); + $this->assertEquals($formatterHelper->getName(), $command->getHelper('formatter')->getName(), '->getHelper() returns the correct helper'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Cannot retrieve helper "formatter" because there is no HelperSet defined. + */ + public function testGetHelperWithoutHelperSet() + { + $command = new \TestCommand(); + $command->getHelper('formatter'); + } + + public function testMergeApplicationDefinition() + { + $application1 = new Application(); + $application1->getDefinition()->addArguments(array(new InputArgument('foo'))); + $application1->getDefinition()->addOptions(array(new InputOption('bar'))); + $command = new \TestCommand(); + $command->setApplication($application1); + $command->setDefinition($definition = new InputDefinition(array(new InputArgument('bar'), new InputOption('foo')))); + + $r = new \ReflectionObject($command); + $m = $r->getMethod('mergeApplicationDefinition'); + $m->setAccessible(true); + $m->invoke($command); + $this->assertTrue($command->getDefinition()->hasArgument('foo'), '->mergeApplicationDefinition() merges the application arguments and the command arguments'); + $this->assertTrue($command->getDefinition()->hasArgument('bar'), '->mergeApplicationDefinition() merges the application arguments and the command arguments'); + $this->assertTrue($command->getDefinition()->hasOption('foo'), '->mergeApplicationDefinition() merges the application options and the command options'); + $this->assertTrue($command->getDefinition()->hasOption('bar'), '->mergeApplicationDefinition() merges the application options and the command options'); + + $m->invoke($command); + $this->assertEquals(3, $command->getDefinition()->getArgumentCount(), '->mergeApplicationDefinition() does not try to merge twice the application arguments and options'); + } + + public function testMergeApplicationDefinitionWithoutArgsThenWithArgsAddsArgs() + { + $application1 = new Application(); + $application1->getDefinition()->addArguments(array(new InputArgument('foo'))); + $application1->getDefinition()->addOptions(array(new InputOption('bar'))); + $command = new \TestCommand(); + $command->setApplication($application1); + $command->setDefinition($definition = new InputDefinition(array())); + + $r = new \ReflectionObject($command); + $m = $r->getMethod('mergeApplicationDefinition'); + $m->setAccessible(true); + $m->invoke($command, false); + $this->assertTrue($command->getDefinition()->hasOption('bar'), '->mergeApplicationDefinition(false) merges the application and the command options'); + $this->assertFalse($command->getDefinition()->hasArgument('foo'), '->mergeApplicationDefinition(false) does not merge the application arguments'); + + $m->invoke($command, true); + $this->assertTrue($command->getDefinition()->hasArgument('foo'), '->mergeApplicationDefinition(true) merges the application arguments and the command arguments'); + + $m->invoke($command); + $this->assertEquals(2, $command->getDefinition()->getArgumentCount(), '->mergeApplicationDefinition() does not try to merge twice the application arguments'); + } + + public function testRunInteractive() + { + $tester = new CommandTester(new \TestCommand()); + + $tester->execute(array(), array('interactive' => true)); + + $this->assertEquals('interact called'.PHP_EOL.'execute called'.PHP_EOL, $tester->getDisplay(), '->run() calls the interact() method if the input is interactive'); + } + + public function testRunNonInteractive() + { + $tester = new CommandTester(new \TestCommand()); + + $tester->execute(array(), array('interactive' => false)); + + $this->assertEquals('execute called'.PHP_EOL, $tester->getDisplay(), '->run() does not call the interact() method if the input is not interactive'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage You must override the execute() method in the concrete command class. + */ + public function testExecuteMethodNeedsToBeOverridden() + { + $command = new Command('foo'); + $command->run(new StringInput(''), new NullOutput()); + } + + /** + * @expectedException Symfony\Component\Console\Exception\InvalidOptionException + * @expectedExceptionMessage The "--bar" option does not exist. + */ + public function testRunWithInvalidOption() + { + $command = new \TestCommand(); + $tester = new CommandTester($command); + $tester->execute(array('--bar' => true)); + } + + public function testRunReturnsIntegerExitCode() + { + $command = new \TestCommand(); + $exitCode = $command->run(new StringInput(''), new NullOutput()); + $this->assertSame(0, $exitCode, '->run() returns integer exit code (treats null as 0)'); + + $command = $this->getMock('TestCommand', array('execute')); + $command->expects($this->once()) + ->method('execute') + ->will($this->returnValue('2.3')); + $exitCode = $command->run(new StringInput(''), new NullOutput()); + $this->assertSame(2, $exitCode, '->run() returns integer exit code (casts numeric to int)'); + } + + public function testRunWithApplication() + { + $command = new \TestCommand(); + $command->setApplication(new Application()); + $exitCode = $command->run(new StringInput(''), new NullOutput()); + + $this->assertSame(0, $exitCode, '->run() returns an integer exit code'); + } + + public function testRunReturnsAlwaysInteger() + { + $command = new \TestCommand(); + + $this->assertSame(0, $command->run(new StringInput(''), new NullOutput())); + } + + public function testSetCode() + { + $command = new \TestCommand(); + $ret = $command->setCode(function (InputInterface $input, OutputInterface $output) { + $output->writeln('from the code...'); + }); + $this->assertEquals($command, $ret, '->setCode() implements a fluent interface'); + $tester = new CommandTester($command); + $tester->execute(array()); + $this->assertEquals('interact called'.PHP_EOL.'from the code...'.PHP_EOL, $tester->getDisplay()); + } + + public function getSetCodeBindToClosureTests() + { + return array( + array(true, 'not bound to the command'), + array(false, 'bound to the command'), + ); + } + + /** + * @dataProvider getSetCodeBindToClosureTests + * @requires PHP 5.4 + */ + public function testSetCodeBindToClosure($previouslyBound, $expected) + { + $code = createClosure(); + if ($previouslyBound) { + $code = $code->bindTo($this); + } + + $command = new \TestCommand(); + $command->setCode($code); + $tester = new CommandTester($command); + $tester->execute(array()); + $this->assertEquals('interact called'.PHP_EOL.$expected.PHP_EOL, $tester->getDisplay()); + } + + public function testSetCodeWithNonClosureCallable() + { + $command = new \TestCommand(); + $ret = $command->setCode(array($this, 'callableMethodCommand')); + $this->assertEquals($command, $ret, '->setCode() implements a fluent interface'); + $tester = new CommandTester($command); + $tester->execute(array()); + $this->assertEquals('interact called'.PHP_EOL.'from the code...'.PHP_EOL, $tester->getDisplay()); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Invalid callable provided to Command::setCode. + */ + public function testSetCodeWithNonCallable() + { + $command = new \TestCommand(); + $command->setCode(array($this, 'nonExistentMethod')); + } + + public function callableMethodCommand(InputInterface $input, OutputInterface $output) + { + $output->writeln('from the code...'); + } + + /** + * @group legacy + */ + public function testLegacyAsText() + { + $command = new \TestCommand(); + $command->setApplication(new Application()); + $tester = new CommandTester($command); + $tester->execute(array('command' => $command->getName())); + $this->assertStringEqualsFile(self::$fixturesPath.'/command_astext.txt', $command->asText(), '->asText() returns a text representation of the command'); + } + + /** + * @group legacy + */ + public function testLegacyAsXml() + { + $command = new \TestCommand(); + $command->setApplication(new Application()); + $tester = new CommandTester($command); + $tester->execute(array('command' => $command->getName())); + $this->assertXmlStringEqualsXmlFile(self::$fixturesPath.'/command_asxml.txt', $command->asXml(), '->asXml() returns an XML representation of the command'); + } +} + +// In order to get an unbound closure, we should create it outside a class +// scope. +function createClosure() +{ + return function (InputInterface $input, OutputInterface $output) { + $output->writeln($this instanceof Command ? 'bound to the command' : 'not bound to the command'); + }; +} diff --git a/vendor/symfony/console/Tests/Command/HelpCommandTest.php b/vendor/symfony/console/Tests/Command/HelpCommandTest.php new file mode 100644 index 0000000000..9e068587f8 --- /dev/null +++ b/vendor/symfony/console/Tests/Command/HelpCommandTest.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Command; + +use Symfony\Component\Console\Tester\CommandTester; +use Symfony\Component\Console\Command\HelpCommand; +use Symfony\Component\Console\Command\ListCommand; +use Symfony\Component\Console\Application; + +class HelpCommandTest extends \PHPUnit_Framework_TestCase +{ + public function testExecuteForCommandAlias() + { + $command = new HelpCommand(); + $command->setApplication(new Application()); + $commandTester = new CommandTester($command); + $commandTester->execute(array('command_name' => 'li'), array('decorated' => false)); + $this->assertContains('list [options] [--] []', $commandTester->getDisplay(), '->execute() returns a text help for the given command alias'); + $this->assertContains('format=FORMAT', $commandTester->getDisplay(), '->execute() returns a text help for the given command alias'); + $this->assertContains('raw', $commandTester->getDisplay(), '->execute() returns a text help for the given command alias'); + } + + public function testExecuteForCommand() + { + $command = new HelpCommand(); + $commandTester = new CommandTester($command); + $command->setCommand(new ListCommand()); + $commandTester->execute(array(), array('decorated' => false)); + $this->assertContains('list [options] [--] []', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + $this->assertContains('format=FORMAT', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + $this->assertContains('raw', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + } + + public function testExecuteForCommandWithXmlOption() + { + $command = new HelpCommand(); + $commandTester = new CommandTester($command); + $command->setCommand(new ListCommand()); + $commandTester->execute(array('--format' => 'xml')); + $this->assertContains('getDisplay(), '->execute() returns an XML help text if --xml is passed'); + } + + public function testExecuteForApplicationCommand() + { + $application = new Application(); + $commandTester = new CommandTester($application->get('help')); + $commandTester->execute(array('command_name' => 'list')); + $this->assertContains('list [options] [--] []', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + $this->assertContains('format=FORMAT', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + $this->assertContains('raw', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + } + + public function testExecuteForApplicationCommandWithXmlOption() + { + $application = new Application(); + $commandTester = new CommandTester($application->get('help')); + $commandTester->execute(array('command_name' => 'list', '--format' => 'xml')); + $this->assertContains('list [--xml] [--raw] [--format FORMAT] [--] [<namespace>]', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + $this->assertContains('getDisplay(), '->execute() returns an XML help text if --format=xml is passed'); + } +} diff --git a/vendor/symfony/console/Tests/Command/ListCommandTest.php b/vendor/symfony/console/Tests/Command/ListCommandTest.php new file mode 100644 index 0000000000..a166a04099 --- /dev/null +++ b/vendor/symfony/console/Tests/Command/ListCommandTest.php @@ -0,0 +1,112 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Command; + +use Symfony\Component\Console\Tester\CommandTester; +use Symfony\Component\Console\Application; + +class ListCommandTest extends \PHPUnit_Framework_TestCase +{ + public function testExecuteListsCommands() + { + $application = new Application(); + $commandTester = new CommandTester($command = $application->get('list')); + $commandTester->execute(array('command' => $command->getName()), array('decorated' => false)); + + $this->assertRegExp('/help\s{2,}Displays help for a command/', $commandTester->getDisplay(), '->execute() returns a list of available commands'); + } + + public function testExecuteListsCommandsWithXmlOption() + { + $application = new Application(); + $commandTester = new CommandTester($command = $application->get('list')); + $commandTester->execute(array('command' => $command->getName(), '--format' => 'xml')); + $this->assertRegExp('//', $commandTester->getDisplay(), '->execute() returns a list of available commands in XML if --xml is passed'); + } + + public function testExecuteListsCommandsWithRawOption() + { + $application = new Application(); + $commandTester = new CommandTester($command = $application->get('list')); + $commandTester->execute(array('command' => $command->getName(), '--raw' => true)); + $output = <<<'EOF' +help Displays help for a command +list Lists commands + +EOF; + + $this->assertEquals($output, $commandTester->getDisplay(true)); + } + + public function testExecuteListsCommandsWithNamespaceArgument() + { + require_once realpath(__DIR__.'/../Fixtures/FooCommand.php'); + $application = new Application(); + $application->add(new \FooCommand()); + $commandTester = new CommandTester($command = $application->get('list')); + $commandTester->execute(array('command' => $command->getName(), 'namespace' => 'foo', '--raw' => true)); + $output = <<<'EOF' +foo:bar The foo:bar command + +EOF; + + $this->assertEquals($output, $commandTester->getDisplay(true)); + } + + public function testExecuteListsCommandsOrder() + { + require_once realpath(__DIR__.'/../Fixtures/Foo6Command.php'); + $application = new Application(); + $application->add(new \Foo6Command()); + $commandTester = new CommandTester($command = $application->get('list')); + $commandTester->execute(array('command' => $command->getName()), array('decorated' => false)); + $output = <<<'EOF' +Console Tool + +Usage: + command [options] [arguments] + +Options: + -h, --help Display this help message + -q, --quiet Do not output any message + -V, --version Display this application version + --ansi Force ANSI output + --no-ansi Disable ANSI output + -n, --no-interaction Do not ask any interactive question + -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +Available commands: + help Displays help for a command + list Lists commands + 0foo + 0foo:bar 0foo:bar command +EOF; + + $this->assertEquals($output, trim($commandTester->getDisplay(true))); + } + + public function testExecuteListsCommandsOrderRaw() + { + require_once realpath(__DIR__.'/../Fixtures/Foo6Command.php'); + $application = new Application(); + $application->add(new \Foo6Command()); + $commandTester = new CommandTester($command = $application->get('list')); + $commandTester->execute(array('command' => $command->getName(), '--raw' => true)); + $output = <<<'EOF' +help Displays help for a command +list Lists commands +0foo:bar 0foo:bar command +EOF; + + $this->assertEquals($output, trim($commandTester->getDisplay(true))); + } +} diff --git a/vendor/symfony/console/Tests/Descriptor/AbstractDescriptorTest.php b/vendor/symfony/console/Tests/Descriptor/AbstractDescriptorTest.php new file mode 100644 index 0000000000..c36c4a8e5e --- /dev/null +++ b/vendor/symfony/console/Tests/Descriptor/AbstractDescriptorTest.php @@ -0,0 +1,106 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\BufferedOutput; + +abstract class AbstractDescriptorTest extends \PHPUnit_Framework_TestCase +{ + /** @dataProvider getDescribeInputArgumentTestData */ + public function testDescribeInputArgument(InputArgument $argument, $expectedDescription) + { + $this->assertDescription($expectedDescription, $argument); + } + + /** @dataProvider getDescribeInputOptionTestData */ + public function testDescribeInputOption(InputOption $option, $expectedDescription) + { + $this->assertDescription($expectedDescription, $option); + } + + /** @dataProvider getDescribeInputDefinitionTestData */ + public function testDescribeInputDefinition(InputDefinition $definition, $expectedDescription) + { + $this->assertDescription($expectedDescription, $definition); + } + + /** @dataProvider getDescribeCommandTestData */ + public function testDescribeCommand(Command $command, $expectedDescription) + { + $this->assertDescription($expectedDescription, $command); + } + + /** @dataProvider getDescribeApplicationTestData */ + public function testDescribeApplication(Application $application, $expectedDescription) + { + // Replaces the dynamic placeholders of the command help text with a static version. + // The placeholder %command.full_name% includes the script path that is not predictable + // and can not be tested against. + foreach ($application->all() as $command) { + $command->setHelp(str_replace('%command.full_name%', 'app/console %command.name%', $command->getHelp())); + } + + $this->assertDescription($expectedDescription, $application); + } + + public function getDescribeInputArgumentTestData() + { + return $this->getDescriptionTestData(ObjectsProvider::getInputArguments()); + } + + public function getDescribeInputOptionTestData() + { + return $this->getDescriptionTestData(ObjectsProvider::getInputOptions()); + } + + public function getDescribeInputDefinitionTestData() + { + return $this->getDescriptionTestData(ObjectsProvider::getInputDefinitions()); + } + + public function getDescribeCommandTestData() + { + return $this->getDescriptionTestData(ObjectsProvider::getCommands()); + } + + public function getDescribeApplicationTestData() + { + return $this->getDescriptionTestData(ObjectsProvider::getApplications()); + } + + abstract protected function getDescriptor(); + + abstract protected function getFormat(); + + private function getDescriptionTestData(array $objects) + { + $data = array(); + foreach ($objects as $name => $object) { + $description = file_get_contents(sprintf('%s/../Fixtures/%s.%s', __DIR__, $name, $this->getFormat())); + $data[] = array($object, $description); + } + + return $data; + } + + protected function assertDescription($expectedDescription, $describedObject) + { + $output = new BufferedOutput(BufferedOutput::VERBOSITY_NORMAL, true); + $this->getDescriptor()->describe($output, $describedObject, array('raw_output' => true)); + $this->assertEquals(trim($expectedDescription), trim(str_replace(PHP_EOL, "\n", $output->fetch()))); + } +} diff --git a/vendor/symfony/console/Tests/Descriptor/JsonDescriptorTest.php b/vendor/symfony/console/Tests/Descriptor/JsonDescriptorTest.php new file mode 100644 index 0000000000..f9a15612b3 --- /dev/null +++ b/vendor/symfony/console/Tests/Descriptor/JsonDescriptorTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Descriptor; + +use Symfony\Component\Console\Descriptor\JsonDescriptor; +use Symfony\Component\Console\Output\BufferedOutput; + +class JsonDescriptorTest extends AbstractDescriptorTest +{ + protected function getDescriptor() + { + return new JsonDescriptor(); + } + + protected function getFormat() + { + return 'json'; + } + + protected function assertDescription($expectedDescription, $describedObject) + { + $output = new BufferedOutput(BufferedOutput::VERBOSITY_NORMAL, true); + $this->getDescriptor()->describe($output, $describedObject, array('raw_output' => true)); + $this->assertEquals(json_decode(trim($expectedDescription), true), json_decode(trim(str_replace(PHP_EOL, "\n", $output->fetch())), true)); + } +} diff --git a/vendor/symfony/console/Tests/Descriptor/MarkdownDescriptorTest.php b/vendor/symfony/console/Tests/Descriptor/MarkdownDescriptorTest.php new file mode 100644 index 0000000000..c85e8a594b --- /dev/null +++ b/vendor/symfony/console/Tests/Descriptor/MarkdownDescriptorTest.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Descriptor; + +use Symfony\Component\Console\Descriptor\MarkdownDescriptor; + +class MarkdownDescriptorTest extends AbstractDescriptorTest +{ + protected function getDescriptor() + { + return new MarkdownDescriptor(); + } + + protected function getFormat() + { + return 'md'; + } +} diff --git a/vendor/symfony/console/Tests/Descriptor/ObjectsProvider.php b/vendor/symfony/console/Tests/Descriptor/ObjectsProvider.php new file mode 100644 index 0000000000..45b3b2fff9 --- /dev/null +++ b/vendor/symfony/console/Tests/Descriptor/ObjectsProvider.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Descriptor; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Tests\Fixtures\DescriptorApplication1; +use Symfony\Component\Console\Tests\Fixtures\DescriptorApplication2; +use Symfony\Component\Console\Tests\Fixtures\DescriptorCommand1; +use Symfony\Component\Console\Tests\Fixtures\DescriptorCommand2; + +/** + * @author Jean-François Simon + */ +class ObjectsProvider +{ + public static function getInputArguments() + { + return array( + 'input_argument_1' => new InputArgument('argument_name', InputArgument::REQUIRED), + 'input_argument_2' => new InputArgument('argument_name', InputArgument::IS_ARRAY, 'argument description'), + 'input_argument_3' => new InputArgument('argument_name', InputArgument::OPTIONAL, 'argument description', 'default_value'), + 'input_argument_4' => new InputArgument('argument_name', InputArgument::REQUIRED, "multiline\nargument description"), + ); + } + + public static function getInputOptions() + { + return array( + 'input_option_1' => new InputOption('option_name', 'o', InputOption::VALUE_NONE), + 'input_option_2' => new InputOption('option_name', 'o', InputOption::VALUE_OPTIONAL, 'option description', 'default_value'), + 'input_option_3' => new InputOption('option_name', 'o', InputOption::VALUE_REQUIRED, 'option description'), + 'input_option_4' => new InputOption('option_name', 'o', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, 'option description', array()), + 'input_option_5' => new InputOption('option_name', 'o', InputOption::VALUE_REQUIRED, "multiline\noption description"), + 'input_option_6' => new InputOption('option_name', array('o', 'O'), InputOption::VALUE_REQUIRED, 'option with multiple shortcuts'), + ); + } + + public static function getInputDefinitions() + { + return array( + 'input_definition_1' => new InputDefinition(), + 'input_definition_2' => new InputDefinition(array(new InputArgument('argument_name', InputArgument::REQUIRED))), + 'input_definition_3' => new InputDefinition(array(new InputOption('option_name', 'o', InputOption::VALUE_NONE))), + 'input_definition_4' => new InputDefinition(array( + new InputArgument('argument_name', InputArgument::REQUIRED), + new InputOption('option_name', 'o', InputOption::VALUE_NONE), + )), + ); + } + + public static function getCommands() + { + return array( + 'command_1' => new DescriptorCommand1(), + 'command_2' => new DescriptorCommand2(), + ); + } + + public static function getApplications() + { + return array( + 'application_1' => new DescriptorApplication1(), + 'application_2' => new DescriptorApplication2(), + ); + } +} diff --git a/vendor/symfony/console/Tests/Descriptor/TextDescriptorTest.php b/vendor/symfony/console/Tests/Descriptor/TextDescriptorTest.php new file mode 100644 index 0000000000..350b67950d --- /dev/null +++ b/vendor/symfony/console/Tests/Descriptor/TextDescriptorTest.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Descriptor; + +use Symfony\Component\Console\Descriptor\TextDescriptor; + +class TextDescriptorTest extends AbstractDescriptorTest +{ + protected function getDescriptor() + { + return new TextDescriptor(); + } + + protected function getFormat() + { + return 'txt'; + } +} diff --git a/vendor/symfony/console/Tests/Descriptor/XmlDescriptorTest.php b/vendor/symfony/console/Tests/Descriptor/XmlDescriptorTest.php new file mode 100644 index 0000000000..59a5d1ed8a --- /dev/null +++ b/vendor/symfony/console/Tests/Descriptor/XmlDescriptorTest.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Descriptor; + +use Symfony\Component\Console\Descriptor\XmlDescriptor; + +class XmlDescriptorTest extends AbstractDescriptorTest +{ + protected function getDescriptor() + { + return new XmlDescriptor(); + } + + protected function getFormat() + { + return 'xml'; + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/BarBucCommand.php b/vendor/symfony/console/Tests/Fixtures/BarBucCommand.php new file mode 100644 index 0000000000..52b619e821 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/BarBucCommand.php @@ -0,0 +1,11 @@ +setName('bar:buc'); + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/DescriptorApplication1.php b/vendor/symfony/console/Tests/Fixtures/DescriptorApplication1.php new file mode 100644 index 0000000000..132b6d57dd --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/DescriptorApplication1.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Fixtures; + +use Symfony\Component\Console\Application; + +class DescriptorApplication1 extends Application +{ +} diff --git a/vendor/symfony/console/Tests/Fixtures/DescriptorApplication2.php b/vendor/symfony/console/Tests/Fixtures/DescriptorApplication2.php new file mode 100644 index 0000000000..ff55135800 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/DescriptorApplication2.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Fixtures; + +use Symfony\Component\Console\Application; + +class DescriptorApplication2 extends Application +{ + public function __construct() + { + parent::__construct('My Symfony application', 'v1.0'); + $this->add(new DescriptorCommand1()); + $this->add(new DescriptorCommand2()); + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/DescriptorCommand1.php b/vendor/symfony/console/Tests/Fixtures/DescriptorCommand1.php new file mode 100644 index 0000000000..ede05d7a73 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/DescriptorCommand1.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Fixtures; + +use Symfony\Component\Console\Command\Command; + +class DescriptorCommand1 extends Command +{ + protected function configure() + { + $this + ->setName('descriptor:command1') + ->setAliases(array('alias1', 'alias2')) + ->setDescription('command 1 description') + ->setHelp('command 1 help') + ; + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/DescriptorCommand2.php b/vendor/symfony/console/Tests/Fixtures/DescriptorCommand2.php new file mode 100644 index 0000000000..51106b9611 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/DescriptorCommand2.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Fixtures; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; + +class DescriptorCommand2 extends Command +{ + protected function configure() + { + $this + ->setName('descriptor:command2') + ->setDescription('command 2 description') + ->setHelp('command 2 help') + ->addUsage('-o|--option_name ') + ->addUsage('') + ->addArgument('argument_name', InputArgument::REQUIRED) + ->addOption('option_name', 'o', InputOption::VALUE_NONE) + ; + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/DummyOutput.php b/vendor/symfony/console/Tests/Fixtures/DummyOutput.php new file mode 100644 index 0000000000..0070c0a486 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/DummyOutput.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Fixtures; + +use Symfony\Component\Console\Output\BufferedOutput; + +/** + * Dummy output. + * + * @author Kévin Dunglas + */ +class DummyOutput extends BufferedOutput +{ + /** + * @return array + */ + public function getLogs() + { + $logs = array(); + foreach (explode("\n", trim($this->fetch())) as $message) { + preg_match('/^\[(.*)\] (.*)/', $message, $matches); + $logs[] = sprintf('%s %s', $matches[1], $matches[2]); + } + + return $logs; + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/Foo1Command.php b/vendor/symfony/console/Tests/Fixtures/Foo1Command.php new file mode 100644 index 0000000000..254162f320 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Foo1Command.php @@ -0,0 +1,26 @@ +setName('foo:bar1') + ->setDescription('The foo:bar1 command') + ->setAliases(array('afoobar1')) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->input = $input; + $this->output = $output; + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/Foo2Command.php b/vendor/symfony/console/Tests/Fixtures/Foo2Command.php new file mode 100644 index 0000000000..8071dc8fb3 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Foo2Command.php @@ -0,0 +1,21 @@ +setName('foo1:bar') + ->setDescription('The foo1:bar command') + ->setAliases(array('afoobar2')) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/Foo3Command.php b/vendor/symfony/console/Tests/Fixtures/Foo3Command.php new file mode 100644 index 0000000000..6c890fafff --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Foo3Command.php @@ -0,0 +1,29 @@ +setName('foo3:bar') + ->setDescription('The foo3:bar command') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + try { + try { + throw new \Exception('First exception

this is html

'); + } catch (\Exception $e) { + throw new \Exception('Second exception comment', 0, $e); + } + } catch (\Exception $e) { + throw new \Exception('Third exception comment', 0, $e); + } + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/Foo4Command.php b/vendor/symfony/console/Tests/Fixtures/Foo4Command.php new file mode 100644 index 0000000000..1c5463995f --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Foo4Command.php @@ -0,0 +1,11 @@ +setName('foo3:bar:toh'); + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/Foo5Command.php b/vendor/symfony/console/Tests/Fixtures/Foo5Command.php new file mode 100644 index 0000000000..a1c60827a5 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Foo5Command.php @@ -0,0 +1,10 @@ +setName('0foo:bar')->setDescription('0foo:bar command'); + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/FooCommand.php b/vendor/symfony/console/Tests/Fixtures/FooCommand.php new file mode 100644 index 0000000000..355e0ad6d6 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/FooCommand.php @@ -0,0 +1,33 @@ +setName('foo:bar') + ->setDescription('The foo:bar command') + ->setAliases(array('afoobar')) + ; + } + + protected function interact(InputInterface $input, OutputInterface $output) + { + $output->writeln('interact called'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->input = $input; + $this->output = $output; + + $output->writeln('called'); + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/FooSubnamespaced1Command.php b/vendor/symfony/console/Tests/Fixtures/FooSubnamespaced1Command.php new file mode 100644 index 0000000000..fc50c72bfc --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/FooSubnamespaced1Command.php @@ -0,0 +1,26 @@ +setName('foo:bar:baz') + ->setDescription('The foo:bar:baz command') + ->setAliases(array('foobarbaz')) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->input = $input; + $this->output = $output; + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/FooSubnamespaced2Command.php b/vendor/symfony/console/Tests/Fixtures/FooSubnamespaced2Command.php new file mode 100644 index 0000000000..1cf31ff110 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/FooSubnamespaced2Command.php @@ -0,0 +1,26 @@ +setName('foo:go:bret') + ->setDescription('The foo:bar:go command') + ->setAliases(array('foobargo')) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->input = $input; + $this->output = $output; + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/FoobarCommand.php b/vendor/symfony/console/Tests/Fixtures/FoobarCommand.php new file mode 100644 index 0000000000..968162804c --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/FoobarCommand.php @@ -0,0 +1,25 @@ +setName('foobar:foo') + ->setDescription('The foobar:foo command') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->input = $input; + $this->output = $output; + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_0.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_0.php new file mode 100644 index 0000000000..996fafb989 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_0.php @@ -0,0 +1,11 @@ +caution('Lorem ipsum dolor sit amet'); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_1.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_1.php new file mode 100644 index 0000000000..6634cd5690 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_1.php @@ -0,0 +1,13 @@ +title('Title'); + $output->warning('Lorem ipsum dolor sit amet'); + $output->title('Title'); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_10.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_10.php new file mode 100644 index 0000000000..4120df9cb6 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_10.php @@ -0,0 +1,17 @@ +block( + 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum', + 'CUSTOM', + 'fg=white;bg=green', + 'X ', + true + ); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_11.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_11.php new file mode 100644 index 0000000000..a2781ddbea --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_11.php @@ -0,0 +1,13 @@ +comment( + 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum' + ); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_2.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_2.php new file mode 100644 index 0000000000..6004e3d6c2 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_2.php @@ -0,0 +1,16 @@ +warning('Warning'); + $output->caution('Caution'); + $output->error('Error'); + $output->success('Success'); + $output->note('Note'); + $output->block('Custom block', 'CUSTOM', 'fg=white;bg=green', 'X ', true); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_3.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_3.php new file mode 100644 index 0000000000..c7a08f138d --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_3.php @@ -0,0 +1,12 @@ +title('First title'); + $output->title('Second title'); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_4.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_4.php new file mode 100644 index 0000000000..afea70c7aa --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_4.php @@ -0,0 +1,34 @@ +write('Lorem ipsum dolor sit amet'); + $output->title('First title'); + + $output->writeln('Lorem ipsum dolor sit amet'); + $output->title('Second title'); + + $output->write('Lorem ipsum dolor sit amet'); + $output->write(''); + $output->title('Third title'); + + //Ensure edge case by appending empty strings to history: + $output->write('Lorem ipsum dolor sit amet'); + $output->write(array('', '', '')); + $output->title('Fourth title'); + + //Ensure have manual control over number of blank lines: + $output->writeln('Lorem ipsum dolor sit amet'); + $output->writeln(array('', '')); //Should append an extra blank line + $output->title('Fifth title'); + + $output->writeln('Lorem ipsum dolor sit amet'); + $output->newLine(2); //Should append an extra blank line + $output->title('Fifth title'); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_5.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_5.php new file mode 100644 index 0000000000..d2c68a9e73 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_5.php @@ -0,0 +1,37 @@ +writeln('Lorem ipsum dolor sit amet'); + $output->listing(array( + 'Lorem ipsum dolor sit amet', + 'consectetur adipiscing elit', + )); + + //Even using write: + $output->write('Lorem ipsum dolor sit amet'); + $output->listing(array( + 'Lorem ipsum dolor sit amet', + 'consectetur adipiscing elit', + )); + + $output->write('Lorem ipsum dolor sit amet'); + $output->text(array( + 'Lorem ipsum dolor sit amet', + 'consectetur adipiscing elit', + )); + + $output->newLine(); + + $output->write('Lorem ipsum dolor sit amet'); + $output->comment(array( + 'Lorem ipsum dolor sit amet', + 'consectetur adipiscing elit', + )); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_6.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_6.php new file mode 100644 index 0000000000..f1d7990544 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_6.php @@ -0,0 +1,16 @@ +listing(array( + 'Lorem ipsum dolor sit amet', + 'consectetur adipiscing elit', + )); + $output->success('Lorem ipsum dolor sit amet'); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_7.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_7.php new file mode 100644 index 0000000000..cbfea734b4 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_7.php @@ -0,0 +1,15 @@ +title('Title'); + $output->askHidden('Hidden question'); + $output->choice('Choice question with default', array('choice1', 'choice2'), 'choice1'); + $output->confirm('Confirmation with yes default', true); + $output->text('Duis aute irure dolor in reprehenderit in voluptate velit esse'); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_8.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_8.php new file mode 100644 index 0000000000..0244fd2725 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_8.php @@ -0,0 +1,26 @@ + 3))), + array('ISBN', 'Title', 'Author'), + ); + + $rows = array( + array( + '978-0521567817', + 'De Monarchia', + new TableCell("Dante Alighieri\nspans multiple rows", array('rowspan' => 2)), + ), + array('978-0804169127', 'Divine Comedy'), + ); + + $output = new SymfonyStyleWithForcedLineLength($input, $output); + $output->table($headers, $rows); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_9.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_9.php new file mode 100644 index 0000000000..6420730fd6 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_9.php @@ -0,0 +1,11 @@ +block(array('Custom block', 'Second custom block line'), 'CUSTOM', 'fg=white;bg=green', 'X ', true); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_0.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_0.txt new file mode 100644 index 0000000000..a42e0f792a --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_0.txt @@ -0,0 +1,3 @@ + + ! [CAUTION] Lorem ipsum dolor sit amet + diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_1.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_1.txt new file mode 100644 index 0000000000..334875f789 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_1.txt @@ -0,0 +1,9 @@ + +Title +===== + + [WARNING] Lorem ipsum dolor sit amet + +Title +===== + diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_10.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_10.txt new file mode 100644 index 0000000000..385c6a283c --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_10.txt @@ -0,0 +1,7 @@ + +X [CUSTOM] Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et +X dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea +X commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat +X nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit +X anim id est laborum + diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_11.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_11.txt new file mode 100644 index 0000000000..9983af832a --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_11.txt @@ -0,0 +1,6 @@ + + // Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna + // aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + // Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur + // sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum + diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_2.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_2.txt new file mode 100644 index 0000000000..ca609760cc --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_2.txt @@ -0,0 +1,13 @@ + + [WARNING] Warning + + ! [CAUTION] Caution + + [ERROR] Error + + [OK] Success + + ! [NOTE] Note + +X [CUSTOM] Custom block + diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_3.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_3.txt new file mode 100644 index 0000000000..f4b6d58276 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_3.txt @@ -0,0 +1,7 @@ + +First title +=========== + +Second title +============ + diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_4.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_4.txt new file mode 100644 index 0000000000..2646d858e7 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_4.txt @@ -0,0 +1,32 @@ +Lorem ipsum dolor sit amet + +First title +=========== + +Lorem ipsum dolor sit amet + +Second title +============ + +Lorem ipsum dolor sit amet + +Third title +=========== + +Lorem ipsum dolor sit amet + +Fourth title +============ + +Lorem ipsum dolor sit amet + + +Fifth title +=========== + +Lorem ipsum dolor sit amet + + +Fifth title +=========== + diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_5.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_5.txt new file mode 100644 index 0000000000..be4a2db605 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_5.txt @@ -0,0 +1,18 @@ +Lorem ipsum dolor sit amet + * Lorem ipsum dolor sit amet + * consectetur adipiscing elit + +Lorem ipsum dolor sit amet + * Lorem ipsum dolor sit amet + * consectetur adipiscing elit + +Lorem ipsum dolor sit amet + Lorem ipsum dolor sit amet + consectetur adipiscing elit + +Lorem ipsum dolor sit amet + + // Lorem ipsum dolor sit amet + // + // consectetur adipiscing elit + diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_6.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_6.txt new file mode 100644 index 0000000000..5f2d33c148 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_6.txt @@ -0,0 +1,6 @@ + + * Lorem ipsum dolor sit amet + * consectetur adipiscing elit + + [OK] Lorem ipsum dolor sit amet + diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_7.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_7.txt new file mode 100644 index 0000000000..ecea9778b1 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_7.txt @@ -0,0 +1,5 @@ + +Title +===== + + Duis aute irure dolor in reprehenderit in voluptate velit esse diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_8.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_8.txt new file mode 100644 index 0000000000..005b846eae --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_8.txt @@ -0,0 +1,9 @@ + ---------------- --------------- --------------------- + Main table title + ---------------- --------------- --------------------- + ISBN Title Author + ---------------- --------------- --------------------- + 978-0521567817 De Monarchia Dante Alighieri + 978-0804169127 Divine Comedy spans multiple rows + ---------------- --------------- --------------------- + diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_9.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_9.txt new file mode 100644 index 0000000000..069c0d5119 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_9.txt @@ -0,0 +1,5 @@ + +X [CUSTOM] Custom block +X +X Second custom block line + diff --git a/vendor/symfony/console/Tests/Fixtures/TestCommand.php b/vendor/symfony/console/Tests/Fixtures/TestCommand.php new file mode 100644 index 0000000000..dcd32739c4 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/TestCommand.php @@ -0,0 +1,28 @@ +setName('namespace:name') + ->setAliases(array('name')) + ->setDescription('description') + ->setHelp('help') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $output->writeln('execute called'); + } + + protected function interact(InputInterface $input, OutputInterface $output) + { + $output->writeln('interact called'); + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/application_1.json b/vendor/symfony/console/Tests/Fixtures/application_1.json new file mode 100644 index 0000000000..b17b38d8a3 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_1.json @@ -0,0 +1 @@ +{"commands":[{"name":"help","usage":["help [--xml] [--format FORMAT] [--raw] [--] []"],"description":"Displays help for a command","help":"The help<\/info> command displays help for a given command:\n\n php app\/console help list<\/info>\n\nYou can also output the help in other formats by using the --format<\/comment> option:\n\n php app\/console help --format=xml list<\/info>\n\nTo display the list of available commands, please use the list<\/info> command.","definition":{"arguments":{"command_name":{"name":"command_name","is_required":false,"is_array":false,"description":"The command name","default":"help"}},"options":{"xml":{"name":"--xml","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"To output help as XML","default":false},"format":{"name":"--format","shortcut":"","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"The output format (txt, xml, json, or md)","default":"txt"},"raw":{"name":"--raw","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"To output raw command help","default":false},"help":{"name":"--help","shortcut":"-h","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this help message","default":false},"quiet":{"name":"--quiet","shortcut":"-q","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not output any message","default":false},"verbose":{"name":"--verbose","shortcut":"-v|-vv|-vvv","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug","default":false},"version":{"name":"--version","shortcut":"-V","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this application version","default":false},"ansi":{"name":"--ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Force ANSI output","default":false},"no-ansi":{"name":"--no-ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Disable ANSI output","default":false},"no-interaction":{"name":"--no-interaction","shortcut":"-n","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not ask any interactive question","default":false}}}},{"name":"list","usage":["list [--xml] [--raw] [--format FORMAT] [--] []"],"description":"Lists commands","help":"The list<\/info> command lists all commands:\n\n php app\/console list<\/info>\n\nYou can also display the commands for a specific namespace:\n\n php app\/console list test<\/info>\n\nYou can also output the information in other formats by using the --format<\/comment> option:\n\n php app\/console list --format=xml<\/info>\n\nIt's also possible to get raw list of commands (useful for embedding command runner):\n\n php app\/console list --raw<\/info>","definition":{"arguments":{"namespace":{"name":"namespace","is_required":false,"is_array":false,"description":"The namespace name","default":null}},"options":{"xml":{"name":"--xml","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"To output list as XML","default":false},"raw":{"name":"--raw","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"To output raw command list","default":false},"format":{"name":"--format","shortcut":"","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"The output format (txt, xml, json, or md)","default":"txt"}}}}],"namespaces":[{"id":"_global","commands":["help","list"]}]} diff --git a/vendor/symfony/console/Tests/Fixtures/application_1.md b/vendor/symfony/console/Tests/Fixtures/application_1.md new file mode 100644 index 0000000000..82a605da69 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_1.md @@ -0,0 +1,201 @@ +UNKNOWN +======= + +* help +* list + +help +---- + +* Description: Displays help for a command +* Usage: + + * `help [--xml] [--format FORMAT] [--raw] [--] []` + +The help command displays help for a given command: + + php app/console help list + +You can also output the help in other formats by using the --format option: + + php app/console help --format=xml list + +To display the list of available commands, please use the list command. + +### Arguments: + +**command_name:** + +* Name: command_name +* Is required: no +* Is array: no +* Description: The command name +* Default: `'help'` + +### Options: + +**xml:** + +* Name: `--xml` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: To output help as XML +* Default: `false` + +**format:** + +* Name: `--format` +* Shortcut: +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Description: The output format (txt, xml, json, or md) +* Default: `'txt'` + +**raw:** + +* Name: `--raw` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: To output raw command help +* Default: `false` + +**help:** + +* Name: `--help` +* Shortcut: `-h` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Display this help message +* Default: `false` + +**quiet:** + +* Name: `--quiet` +* Shortcut: `-q` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Do not output any message +* Default: `false` + +**verbose:** + +* Name: `--verbose` +* Shortcut: `-v|-vv|-vvv` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug +* Default: `false` + +**version:** + +* Name: `--version` +* Shortcut: `-V` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Display this application version +* Default: `false` + +**ansi:** + +* Name: `--ansi` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Force ANSI output +* Default: `false` + +**no-ansi:** + +* Name: `--no-ansi` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Disable ANSI output +* Default: `false` + +**no-interaction:** + +* Name: `--no-interaction` +* Shortcut: `-n` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Do not ask any interactive question +* Default: `false` + +list +---- + +* Description: Lists commands +* Usage: + + * `list [--xml] [--raw] [--format FORMAT] [--] []` + +The list command lists all commands: + + php app/console list + +You can also display the commands for a specific namespace: + + php app/console list test + +You can also output the information in other formats by using the --format option: + + php app/console list --format=xml + +It's also possible to get raw list of commands (useful for embedding command runner): + + php app/console list --raw + +### Arguments: + +**namespace:** + +* Name: namespace +* Is required: no +* Is array: no +* Description: The namespace name +* Default: `NULL` + +### Options: + +**xml:** + +* Name: `--xml` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: To output list as XML +* Default: `false` + +**raw:** + +* Name: `--raw` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: To output raw command list +* Default: `false` + +**format:** + +* Name: `--format` +* Shortcut: +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Description: The output format (txt, xml, json, or md) +* Default: `'txt'` diff --git a/vendor/symfony/console/Tests/Fixtures/application_1.txt b/vendor/symfony/console/Tests/Fixtures/application_1.txt new file mode 100644 index 0000000000..c4cf8f2164 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_1.txt @@ -0,0 +1,17 @@ +Console Tool + +Usage: + command [options] [arguments] + +Options: + -h, --help Display this help message + -q, --quiet Do not output any message + -V, --version Display this application version + --ansi Force ANSI output + --no-ansi Disable ANSI output + -n, --no-interaction Do not ask any interactive question + -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +Available commands: + help Displays help for a command + list Lists commands diff --git a/vendor/symfony/console/Tests/Fixtures/application_1.xml b/vendor/symfony/console/Tests/Fixtures/application_1.xml new file mode 100644 index 0000000000..35d1db4dc9 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_1.xml @@ -0,0 +1,110 @@ + + + + + + help [--xml] [--format FORMAT] [--raw] [--] [<command_name>] + + Displays help for a command + The <info>help</info> command displays help for a given command: + + <info>php app/console help list</info> + + You can also output the help in other formats by using the <comment>--format</comment> option: + + <info>php app/console help --format=xml list</info> + + To display the list of available commands, please use the <info>list</info> command. + + + The command name + + help + + + + + + + + + + + + + + + + + + + list [--xml] [--raw] [--format FORMAT] [--] [<namespace>] + + Lists commands + The <info>list</info> command lists all commands: + + <info>php app/console list</info> + + You can also display the commands for a specific namespace: + + <info>php app/console list test</info> + + You can also output the information in other formats by using the <comment>--format</comment> option: + + <info>php app/console list --format=xml</info> + + It's also possible to get raw list of commands (useful for embedding command runner): + + <info>php app/console list --raw</info> + + + The namespace name + + + + + + + + + + + + + help + list + + + diff --git a/vendor/symfony/console/Tests/Fixtures/application_2.json b/vendor/symfony/console/Tests/Fixtures/application_2.json new file mode 100644 index 0000000000..e47a7a9621 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_2.json @@ -0,0 +1 @@ +{"commands":[{"name":"help","usage":["help [--xml] [--format FORMAT] [--raw] [--] []"],"description":"Displays help for a command","help":"The help<\/info> command displays help for a given command:\n\n php app\/console help list<\/info>\n\nYou can also output the help in other formats by using the --format<\/comment> option:\n\n php app\/console help --format=xml list<\/info>\n\nTo display the list of available commands, please use the list<\/info> command.","definition":{"arguments":{"command_name":{"name":"command_name","is_required":false,"is_array":false,"description":"The command name","default":"help"}},"options":{"xml":{"name":"--xml","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"To output help as XML","default":false},"format":{"name":"--format","shortcut":"","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"The output format (txt, xml, json, or md)","default":"txt"},"raw":{"name":"--raw","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"To output raw command help","default":false},"help":{"name":"--help","shortcut":"-h","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this help message","default":false},"quiet":{"name":"--quiet","shortcut":"-q","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not output any message","default":false},"verbose":{"name":"--verbose","shortcut":"-v|-vv|-vvv","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug","default":false},"version":{"name":"--version","shortcut":"-V","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this application version","default":false},"ansi":{"name":"--ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Force ANSI output","default":false},"no-ansi":{"name":"--no-ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Disable ANSI output","default":false},"no-interaction":{"name":"--no-interaction","shortcut":"-n","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not ask any interactive question","default":false}}}},{"name":"list","usage":["list [--xml] [--raw] [--format FORMAT] [--] []"],"description":"Lists commands","help":"The list<\/info> command lists all commands:\n\n php app\/console list<\/info>\n\nYou can also display the commands for a specific namespace:\n\n php app\/console list test<\/info>\n\nYou can also output the information in other formats by using the --format<\/comment> option:\n\n php app\/console list --format=xml<\/info>\n\nIt's also possible to get raw list of commands (useful for embedding command runner):\n\n php app\/console list --raw<\/info>","definition":{"arguments":{"namespace":{"name":"namespace","is_required":false,"is_array":false,"description":"The namespace name","default":null}},"options":{"xml":{"name":"--xml","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"To output list as XML","default":false},"raw":{"name":"--raw","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"To output raw command list","default":false},"format":{"name":"--format","shortcut":"","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"The output format (txt, xml, json, or md)","default":"txt"}}}},{"name":"descriptor:command1","usage":["descriptor:command1", "alias1", "alias2"],"description":"command 1 description","help":"command 1 help","definition":{"arguments":[],"options":{"help":{"name":"--help","shortcut":"-h","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this help message","default":false},"quiet":{"name":"--quiet","shortcut":"-q","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not output any message","default":false},"verbose":{"name":"--verbose","shortcut":"-v|-vv|-vvv","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug","default":false},"version":{"name":"--version","shortcut":"-V","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this application version","default":false},"ansi":{"name":"--ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Force ANSI output","default":false},"no-ansi":{"name":"--no-ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Disable ANSI output","default":false},"no-interaction":{"name":"--no-interaction","shortcut":"-n","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not ask any interactive question","default":false}}}},{"name":"descriptor:command2","usage":["descriptor:command2 [-o|--option_name] [--] ", "descriptor:command2 -o|--option_name ", "descriptor:command2 "],"description":"command 2 description","help":"command 2 help","definition":{"arguments":{"argument_name":{"name":"argument_name","is_required":true,"is_array":false,"description":"","default":null}},"options":{"option_name":{"name":"--option_name","shortcut":"-o","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"","default":false},"help":{"name":"--help","shortcut":"-h","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this help message","default":false},"quiet":{"name":"--quiet","shortcut":"-q","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not output any message","default":false},"verbose":{"name":"--verbose","shortcut":"-v|-vv|-vvv","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug","default":false},"version":{"name":"--version","shortcut":"-V","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this application version","default":false},"ansi":{"name":"--ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Force ANSI output","default":false},"no-ansi":{"name":"--no-ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Disable ANSI output","default":false},"no-interaction":{"name":"--no-interaction","shortcut":"-n","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not ask any interactive question","default":false}}}}],"namespaces":[{"id":"_global","commands":["alias1","alias2","help","list"]},{"id":"descriptor","commands":["descriptor:command1","descriptor:command2"]}]} \ No newline at end of file diff --git a/vendor/symfony/console/Tests/Fixtures/application_2.md b/vendor/symfony/console/Tests/Fixtures/application_2.md new file mode 100644 index 0000000000..f031c9e5c3 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_2.md @@ -0,0 +1,396 @@ +My Symfony application +====================== + +* alias1 +* alias2 +* help +* list + +**descriptor:** + +* descriptor:command1 +* descriptor:command2 + +help +---- + +* Description: Displays help for a command +* Usage: + + * `help [--xml] [--format FORMAT] [--raw] [--] []` + +The help command displays help for a given command: + + php app/console help list + +You can also output the help in other formats by using the --format option: + + php app/console help --format=xml list + +To display the list of available commands, please use the list command. + +### Arguments: + +**command_name:** + +* Name: command_name +* Is required: no +* Is array: no +* Description: The command name +* Default: `'help'` + +### Options: + +**xml:** + +* Name: `--xml` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: To output help as XML +* Default: `false` + +**format:** + +* Name: `--format` +* Shortcut: +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Description: The output format (txt, xml, json, or md) +* Default: `'txt'` + +**raw:** + +* Name: `--raw` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: To output raw command help +* Default: `false` + +**help:** + +* Name: `--help` +* Shortcut: `-h` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Display this help message +* Default: `false` + +**quiet:** + +* Name: `--quiet` +* Shortcut: `-q` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Do not output any message +* Default: `false` + +**verbose:** + +* Name: `--verbose` +* Shortcut: `-v|-vv|-vvv` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug +* Default: `false` + +**version:** + +* Name: `--version` +* Shortcut: `-V` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Display this application version +* Default: `false` + +**ansi:** + +* Name: `--ansi` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Force ANSI output +* Default: `false` + +**no-ansi:** + +* Name: `--no-ansi` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Disable ANSI output +* Default: `false` + +**no-interaction:** + +* Name: `--no-interaction` +* Shortcut: `-n` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Do not ask any interactive question +* Default: `false` + +list +---- + +* Description: Lists commands +* Usage: + + * `list [--xml] [--raw] [--format FORMAT] [--] []` + +The list command lists all commands: + + php app/console list + +You can also display the commands for a specific namespace: + + php app/console list test + +You can also output the information in other formats by using the --format option: + + php app/console list --format=xml + +It's also possible to get raw list of commands (useful for embedding command runner): + + php app/console list --raw + +### Arguments: + +**namespace:** + +* Name: namespace +* Is required: no +* Is array: no +* Description: The namespace name +* Default: `NULL` + +### Options: + +**xml:** + +* Name: `--xml` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: To output list as XML +* Default: `false` + +**raw:** + +* Name: `--raw` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: To output raw command list +* Default: `false` + +**format:** + +* Name: `--format` +* Shortcut: +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Description: The output format (txt, xml, json, or md) +* Default: `'txt'` + +descriptor:command1 +------------------- + +* Description: command 1 description +* Usage: + + * `descriptor:command1` + * `alias1` + * `alias2` + +command 1 help + +### Options: + +**help:** + +* Name: `--help` +* Shortcut: `-h` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Display this help message +* Default: `false` + +**quiet:** + +* Name: `--quiet` +* Shortcut: `-q` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Do not output any message +* Default: `false` + +**verbose:** + +* Name: `--verbose` +* Shortcut: `-v|-vv|-vvv` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug +* Default: `false` + +**version:** + +* Name: `--version` +* Shortcut: `-V` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Display this application version +* Default: `false` + +**ansi:** + +* Name: `--ansi` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Force ANSI output +* Default: `false` + +**no-ansi:** + +* Name: `--no-ansi` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Disable ANSI output +* Default: `false` + +**no-interaction:** + +* Name: `--no-interaction` +* Shortcut: `-n` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Do not ask any interactive question +* Default: `false` + +descriptor:command2 +------------------- + +* Description: command 2 description +* Usage: + + * `descriptor:command2 [-o|--option_name] [--] ` + * `descriptor:command2 -o|--option_name ` + * `descriptor:command2 ` + +command 2 help + +### Arguments: + +**argument_name:** + +* Name: argument_name +* Is required: yes +* Is array: no +* Description: +* Default: `NULL` + +### Options: + +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: +* Default: `false` + +**help:** + +* Name: `--help` +* Shortcut: `-h` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Display this help message +* Default: `false` + +**quiet:** + +* Name: `--quiet` +* Shortcut: `-q` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Do not output any message +* Default: `false` + +**verbose:** + +* Name: `--verbose` +* Shortcut: `-v|-vv|-vvv` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug +* Default: `false` + +**version:** + +* Name: `--version` +* Shortcut: `-V` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Display this application version +* Default: `false` + +**ansi:** + +* Name: `--ansi` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Force ANSI output +* Default: `false` + +**no-ansi:** + +* Name: `--no-ansi` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Disable ANSI output +* Default: `false` + +**no-interaction:** + +* Name: `--no-interaction` +* Shortcut: `-n` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Do not ask any interactive question +* Default: `false` diff --git a/vendor/symfony/console/Tests/Fixtures/application_2.txt b/vendor/symfony/console/Tests/Fixtures/application_2.txt new file mode 100644 index 0000000000..292aa829d8 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_2.txt @@ -0,0 +1,22 @@ +My Symfony application version v1.0 + +Usage: + command [options] [arguments] + +Options: + -h, --help Display this help message + -q, --quiet Do not output any message + -V, --version Display this application version + --ansi Force ANSI output + --no-ansi Disable ANSI output + -n, --no-interaction Do not ask any interactive question + -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +Available commands: + alias1 command 1 description + alias2 command 1 description + help Displays help for a command + list Lists commands + descriptor + descriptor:command1 command 1 description + descriptor:command2 command 2 description diff --git a/vendor/symfony/console/Tests/Fixtures/application_2.xml b/vendor/symfony/console/Tests/Fixtures/application_2.xml new file mode 100644 index 0000000000..bc8ab219d8 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_2.xml @@ -0,0 +1,190 @@ + + + + + + help [--xml] [--format FORMAT] [--raw] [--] [<command_name>] + + Displays help for a command + The <info>help</info> command displays help for a given command: + + <info>php app/console help list</info> + + You can also output the help in other formats by using the <comment>--format</comment> option: + + <info>php app/console help --format=xml list</info> + + To display the list of available commands, please use the <info>list</info> command. + + + The command name + + help + + + + + + + + + + + + + + + + + + + list [--xml] [--raw] [--format FORMAT] [--] [<namespace>] + + Lists commands + The <info>list</info> command lists all commands: + + <info>php app/console list</info> + + You can also display the commands for a specific namespace: + + <info>php app/console list test</info> + + You can also output the information in other formats by using the <comment>--format</comment> option: + + <info>php app/console list --format=xml</info> + + It's also possible to get raw list of commands (useful for embedding command runner): + + <info>php app/console list --raw</info> + + + The namespace name + + + + + + + + + + + + descriptor:command1 + alias1 + alias2 + + command 1 description + command 1 help + + + + + + + + + + + + + + descriptor:command2 [-o|--option_name] [--] <argument_name> + descriptor:command2 -o|--option_name <argument_name> + descriptor:command2 <argument_name> + + command 2 description + command 2 help + + + + + + + + + + + + + + + + + + + + + alias1 + alias2 + help + list + + + descriptor:command1 + descriptor:command2 + + + diff --git a/vendor/symfony/console/Tests/Fixtures/application_astext1.txt b/vendor/symfony/console/Tests/Fixtures/application_astext1.txt new file mode 100644 index 0000000000..19dacb23bf --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_astext1.txt @@ -0,0 +1,20 @@ +Console Tool + +Usage: + command [options] [arguments] + +Options: + -h, --help Display this help message + -q, --quiet Do not output any message + -V, --version Display this application version + --ansi Force ANSI output + --no-ansi Disable ANSI output + -n, --no-interaction Do not ask any interactive question + -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +Available commands: + afoobar The foo:bar command + help Displays help for a command + list Lists commands + foo + foo:bar The foo:bar command diff --git a/vendor/symfony/console/Tests/Fixtures/application_astext2.txt b/vendor/symfony/console/Tests/Fixtures/application_astext2.txt new file mode 100644 index 0000000000..c99ccdda7a --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_astext2.txt @@ -0,0 +1,16 @@ +Console Tool + +Usage: + command [options] [arguments] + +Options: + -h, --help Display this help message + -q, --quiet Do not output any message + -V, --version Display this application version + --ansi Force ANSI output + --no-ansi Disable ANSI output + -n, --no-interaction Do not ask any interactive question + -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +Available commands for the "foo" namespace: + foo:bar The foo:bar command diff --git a/vendor/symfony/console/Tests/Fixtures/application_asxml1.txt b/vendor/symfony/console/Tests/Fixtures/application_asxml1.txt new file mode 100644 index 0000000000..8277d9e66b --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_asxml1.txt @@ -0,0 +1,146 @@ + + + + + + help [--xml] [--format FORMAT] [--raw] [--] [<command_name>] + + Displays help for a command + The <info>help</info> command displays help for a given command: + + <info>php app/console help list</info> + + You can also output the help in other formats by using the <comment>--format</comment> option: + + <info>php app/console help --format=xml list</info> + + To display the list of available commands, please use the <info>list</info> command. + + + The command name + + help + + + + + + + + + + + + + + + + + + + list [--xml] [--raw] [--format FORMAT] [--] [<namespace>] + + Lists commands + The <info>list</info> command lists all commands: + + <info>php app/console list</info> + + You can also display the commands for a specific namespace: + + <info>php app/console list test</info> + + You can also output the information in other formats by using the <comment>--format</comment> option: + + <info>php app/console list --format=xml</info> + + It's also possible to get raw list of commands (useful for embedding command runner): + + <info>php app/console list --raw</info> + + + The namespace name + + + + + + + + + + + + foo:bar + afoobar + + The foo:bar command + The foo:bar command + + + + + + + + + + + + + + + afoobar + help + list + + + foo:bar + + + diff --git a/vendor/symfony/console/Tests/Fixtures/application_asxml2.txt b/vendor/symfony/console/Tests/Fixtures/application_asxml2.txt new file mode 100644 index 0000000000..93d6d4e996 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_asxml2.txt @@ -0,0 +1,37 @@ + + + + + + foo:bar + afoobar + + The foo:bar command + The foo:bar command + + + + + + + + + + + + + diff --git a/vendor/symfony/console/Tests/Fixtures/application_gethelp.txt b/vendor/symfony/console/Tests/Fixtures/application_gethelp.txt new file mode 100644 index 0000000000..0c16e3c84b --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_gethelp.txt @@ -0,0 +1 @@ +Console Tool \ No newline at end of file diff --git a/vendor/symfony/console/Tests/Fixtures/application_renderexception1.txt b/vendor/symfony/console/Tests/Fixtures/application_renderexception1.txt new file mode 100644 index 0000000000..919cec4214 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_renderexception1.txt @@ -0,0 +1,6 @@ + + + [Symfony\Component\Console\Exception\CommandNotFoundException] + Command "foo" is not defined. + + diff --git a/vendor/symfony/console/Tests/Fixtures/application_renderexception2.txt b/vendor/symfony/console/Tests/Fixtures/application_renderexception2.txt new file mode 100644 index 0000000000..d9e93da459 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_renderexception2.txt @@ -0,0 +1,8 @@ + + + [Symfony\Component\Console\Exception\InvalidOptionException] + The "--foo" option does not exist. + + +list [--xml] [--raw] [--format FORMAT] [--] [] + diff --git a/vendor/symfony/console/Tests/Fixtures/application_renderexception3.txt b/vendor/symfony/console/Tests/Fixtures/application_renderexception3.txt new file mode 100644 index 0000000000..8276137bd8 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_renderexception3.txt @@ -0,0 +1,18 @@ + + + [Exception] + Third exception comment + + + + [Exception] + Second exception comment + + + + [Exception] + First exception

this is html

+ + +foo3:bar + diff --git a/vendor/symfony/console/Tests/Fixtures/application_renderexception3decorated.txt b/vendor/symfony/console/Tests/Fixtures/application_renderexception3decorated.txt new file mode 100644 index 0000000000..b4a7b018af --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_renderexception3decorated.txt @@ -0,0 +1,18 @@ + +  + [Exception]  + Third exception comment  +  + +  + [Exception]  + Second exception comment  +  + +  + [Exception]  + First exception 

this is html

  +  + +foo3:bar + diff --git a/vendor/symfony/console/Tests/Fixtures/application_renderexception4.txt b/vendor/symfony/console/Tests/Fixtures/application_renderexception4.txt new file mode 100644 index 0000000000..cb080e9cb5 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_renderexception4.txt @@ -0,0 +1,7 @@ + + + [Symfony\Component\Console\Exception\CommandNotFoundException] + Command "foo" is not define + d. + + diff --git a/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth1.txt b/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth1.txt new file mode 100644 index 0000000000..1ba5f8fdd9 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth1.txt @@ -0,0 +1,8 @@ + + + [Exception] + エラーメッセージ + + +foo + diff --git a/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth1decorated.txt b/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth1decorated.txt new file mode 100644 index 0000000000..20644251c2 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth1decorated.txt @@ -0,0 +1,8 @@ + +  + [Exception]  + エラーメッセージ  +  + +foo + diff --git a/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth2.txt b/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth2.txt new file mode 100644 index 0000000000..e41fcfcf67 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth2.txt @@ -0,0 +1,9 @@ + + + [Exception] + コマンドの実行中にエラーが + 発生しました。 + + +foo + diff --git a/vendor/symfony/console/Tests/Fixtures/application_run1.txt b/vendor/symfony/console/Tests/Fixtures/application_run1.txt new file mode 100644 index 0000000000..0dc2730983 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_run1.txt @@ -0,0 +1,17 @@ +Console Tool + +Usage: + command [options] [arguments] + +Options: + -h, --help Display this help message + -q, --quiet Do not output any message + -V, --version Display this application version + --ansi Force ANSI output + --no-ansi Disable ANSI output + -n, --no-interaction Do not ask any interactive question + -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +Available commands: + help Displays help for a command + list Lists commands diff --git a/vendor/symfony/console/Tests/Fixtures/application_run2.txt b/vendor/symfony/console/Tests/Fixtures/application_run2.txt new file mode 100644 index 0000000000..d28b928ec3 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_run2.txt @@ -0,0 +1,29 @@ +Usage: + help [options] [--] [] + +Arguments: + command The command to execute + command_name The command name [default: "help"] + +Options: + --xml To output help as XML + --format=FORMAT The output format (txt, xml, json, or md) [default: "txt"] + --raw To output raw command help + -h, --help Display this help message + -q, --quiet Do not output any message + -V, --version Display this application version + --ansi Force ANSI output + --no-ansi Disable ANSI output + -n, --no-interaction Do not ask any interactive question + -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +Help: + The help command displays help for a given command: + + php app/console help list + + You can also output the help in other formats by using the --format option: + + php app/console help --format=xml list + + To display the list of available commands, please use the list command. diff --git a/vendor/symfony/console/Tests/Fixtures/application_run3.txt b/vendor/symfony/console/Tests/Fixtures/application_run3.txt new file mode 100644 index 0000000000..bc51995f85 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_run3.txt @@ -0,0 +1,27 @@ +Usage: + list [options] [--] [] + +Arguments: + namespace The namespace name + +Options: + --xml To output list as XML + --raw To output raw command list + --format=FORMAT The output format (txt, xml, json, or md) [default: "txt"] + +Help: + The list command lists all commands: + + php app/console list + + You can also display the commands for a specific namespace: + + php app/console list test + + You can also output the information in other formats by using the --format option: + + php app/console list --format=xml + + It's also possible to get raw list of commands (useful for embedding command runner): + + php app/console list --raw diff --git a/vendor/symfony/console/Tests/Fixtures/application_run4.txt b/vendor/symfony/console/Tests/Fixtures/application_run4.txt new file mode 100644 index 0000000000..47187fc267 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_run4.txt @@ -0,0 +1 @@ +Console Tool diff --git a/vendor/symfony/console/Tests/Fixtures/command_1.json b/vendor/symfony/console/Tests/Fixtures/command_1.json new file mode 100644 index 0000000000..20f310b457 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/command_1.json @@ -0,0 +1 @@ +{"name":"descriptor:command1","usage":["descriptor:command1", "alias1", "alias2"],"description":"command 1 description","help":"command 1 help","definition":{"arguments":[],"options":[]}} diff --git a/vendor/symfony/console/Tests/Fixtures/command_1.md b/vendor/symfony/console/Tests/Fixtures/command_1.md new file mode 100644 index 0000000000..34ed3ea770 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/command_1.md @@ -0,0 +1,11 @@ +descriptor:command1 +------------------- + +* Description: command 1 description +* Usage: + + * `descriptor:command1` + * `alias1` + * `alias2` + +command 1 help diff --git a/vendor/symfony/console/Tests/Fixtures/command_1.txt b/vendor/symfony/console/Tests/Fixtures/command_1.txt new file mode 100644 index 0000000000..28e14a057a --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/command_1.txt @@ -0,0 +1,7 @@ +Usage: + descriptor:command1 + alias1 + alias2 + +Help: + command 1 help diff --git a/vendor/symfony/console/Tests/Fixtures/command_1.xml b/vendor/symfony/console/Tests/Fixtures/command_1.xml new file mode 100644 index 0000000000..838b9bd91f --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/command_1.xml @@ -0,0 +1,12 @@ + + + + descriptor:command1 + alias1 + alias2 + + command 1 description + command 1 help + + + diff --git a/vendor/symfony/console/Tests/Fixtures/command_2.json b/vendor/symfony/console/Tests/Fixtures/command_2.json new file mode 100644 index 0000000000..38edd1e209 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/command_2.json @@ -0,0 +1 @@ +{"name":"descriptor:command2","usage":["descriptor:command2 [-o|--option_name] [--] ", "descriptor:command2 -o|--option_name ", "descriptor:command2 "],"description":"command 2 description","help":"command 2 help","definition":{"arguments":{"argument_name":{"name":"argument_name","is_required":true,"is_array":false,"description":"","default":null}},"options":{"option_name":{"name":"--option_name","shortcut":"-o","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"","default":false}}}} diff --git a/vendor/symfony/console/Tests/Fixtures/command_2.md b/vendor/symfony/console/Tests/Fixtures/command_2.md new file mode 100644 index 0000000000..6f538b6407 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/command_2.md @@ -0,0 +1,33 @@ +descriptor:command2 +------------------- + +* Description: command 2 description +* Usage: + + * `descriptor:command2 [-o|--option_name] [--] ` + * `descriptor:command2 -o|--option_name ` + * `descriptor:command2 ` + +command 2 help + +### Arguments: + +**argument_name:** + +* Name: argument_name +* Is required: yes +* Is array: no +* Description: +* Default: `NULL` + +### Options: + +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: +* Default: `false` diff --git a/vendor/symfony/console/Tests/Fixtures/command_2.txt b/vendor/symfony/console/Tests/Fixtures/command_2.txt new file mode 100644 index 0000000000..72f7ce0586 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/command_2.txt @@ -0,0 +1,13 @@ +Usage: + descriptor:command2 [options] [--] + descriptor:command2 -o|--option_name + descriptor:command2 + +Arguments: + argument_name + +Options: + -o, --option_name + +Help: + command 2 help diff --git a/vendor/symfony/console/Tests/Fixtures/command_2.xml b/vendor/symfony/console/Tests/Fixtures/command_2.xml new file mode 100644 index 0000000000..67364caa4e --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/command_2.xml @@ -0,0 +1,21 @@ + + + + descriptor:command2 [-o|--option_name] [--] <argument_name> + descriptor:command2 -o|--option_name <argument_name> + descriptor:command2 <argument_name> + + command 2 description + command 2 help + + + + + + + + + + diff --git a/vendor/symfony/console/Tests/Fixtures/command_astext.txt b/vendor/symfony/console/Tests/Fixtures/command_astext.txt new file mode 100644 index 0000000000..7e20638807 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/command_astext.txt @@ -0,0 +1,18 @@ +Usage: + namespace:name + name + +Arguments: + command The command to execute + +Options: + -h, --help Display this help message + -q, --quiet Do not output any message + -V, --version Display this application version + --ansi Force ANSI output + --no-ansi Disable ANSI output + -n, --no-interaction Do not ask any interactive question + -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +Help: + help diff --git a/vendor/symfony/console/Tests/Fixtures/command_asxml.txt b/vendor/symfony/console/Tests/Fixtures/command_asxml.txt new file mode 100644 index 0000000000..5e776238aa --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/command_asxml.txt @@ -0,0 +1,38 @@ + + + + namespace:name + name + + description + help + + + The command to execute + + + + + + + + + + + + + diff --git a/vendor/symfony/console/Tests/Fixtures/definition_astext.txt b/vendor/symfony/console/Tests/Fixtures/definition_astext.txt new file mode 100644 index 0000000000..0431c072ab --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/definition_astext.txt @@ -0,0 +1,11 @@ +Arguments: + foo The foo argument + baz The baz argument [default: true] + bar The bar argument [default: ["http://foo.com/"]] + +Options: + -f, --foo=FOO The foo option + --baz[=BAZ] The baz option [default: false] + -b, --bar[=BAR] The bar option [default: "bar"] + --qux[=QUX] The qux option [default: ["http://foo.com/","bar"]] (multiple values allowed) + --qux2[=QUX2] The qux2 option [default: {"foo":"bar"}] (multiple values allowed) \ No newline at end of file diff --git a/vendor/symfony/console/Tests/Fixtures/definition_asxml.txt b/vendor/symfony/console/Tests/Fixtures/definition_asxml.txt new file mode 100644 index 0000000000..eec8c079ee --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/definition_asxml.txt @@ -0,0 +1,39 @@ + + + + + The foo argument + + + + The baz argument + + true + + + + The bar argument + + bar + + + + + + + + + diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_1.json b/vendor/symfony/console/Tests/Fixtures/input_argument_1.json new file mode 100644 index 0000000000..b8173b6b3f --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_1.json @@ -0,0 +1 @@ +{"name":"argument_name","is_required":true,"is_array":false,"description":"","default":null} diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_1.md b/vendor/symfony/console/Tests/Fixtures/input_argument_1.md new file mode 100644 index 0000000000..88f311ab53 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_1.md @@ -0,0 +1,7 @@ +**argument_name:** + +* Name: argument_name +* Is required: yes +* Is array: no +* Description: +* Default: `NULL` diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_1.txt b/vendor/symfony/console/Tests/Fixtures/input_argument_1.txt new file mode 100644 index 0000000000..55035183f6 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_1.txt @@ -0,0 +1 @@ + argument_name diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_1.xml b/vendor/symfony/console/Tests/Fixtures/input_argument_1.xml new file mode 100644 index 0000000000..cb37f812c5 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_1.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_2.json b/vendor/symfony/console/Tests/Fixtures/input_argument_2.json new file mode 100644 index 0000000000..ef06b09a75 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_2.json @@ -0,0 +1 @@ +{"name":"argument_name","is_required":false,"is_array":true,"description":"argument description","default":[]} diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_2.md b/vendor/symfony/console/Tests/Fixtures/input_argument_2.md new file mode 100644 index 0000000000..3cdb00cc81 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_2.md @@ -0,0 +1,7 @@ +**argument_name:** + +* Name: argument_name +* Is required: no +* Is array: yes +* Description: argument description +* Default: `array ()` diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_2.txt b/vendor/symfony/console/Tests/Fixtures/input_argument_2.txt new file mode 100644 index 0000000000..e713660743 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_2.txt @@ -0,0 +1 @@ + argument_name argument description diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_2.xml b/vendor/symfony/console/Tests/Fixtures/input_argument_2.xml new file mode 100644 index 0000000000..629da5a98a --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_2.xml @@ -0,0 +1,5 @@ + + + argument description + + diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_3.json b/vendor/symfony/console/Tests/Fixtures/input_argument_3.json new file mode 100644 index 0000000000..de8484e6a7 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_3.json @@ -0,0 +1 @@ +{"name":"argument_name","is_required":false,"is_array":false,"description":"argument description","default":"default_value"} diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_3.md b/vendor/symfony/console/Tests/Fixtures/input_argument_3.md new file mode 100644 index 0000000000..be1c443ae0 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_3.md @@ -0,0 +1,7 @@ +**argument_name:** + +* Name: argument_name +* Is required: no +* Is array: no +* Description: argument description +* Default: `'default_value'` diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_3.txt b/vendor/symfony/console/Tests/Fixtures/input_argument_3.txt new file mode 100644 index 0000000000..6b76639e00 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_3.txt @@ -0,0 +1 @@ + argument_name argument description [default: "default_value"] diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_3.xml b/vendor/symfony/console/Tests/Fixtures/input_argument_3.xml new file mode 100644 index 0000000000..399a5c864d --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_3.xml @@ -0,0 +1,7 @@ + + + argument description + + default_value + + diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_4.json b/vendor/symfony/console/Tests/Fixtures/input_argument_4.json new file mode 100644 index 0000000000..8067a4d1b1 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_4.json @@ -0,0 +1 @@ +{"name":"argument_name","is_required":true,"is_array":false,"description":"multiline argument description","default":null} diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_4.md b/vendor/symfony/console/Tests/Fixtures/input_argument_4.md new file mode 100644 index 0000000000..f026ab3767 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_4.md @@ -0,0 +1,8 @@ +**argument_name:** + +* Name: argument_name +* Is required: yes +* Is array: no +* Description: multiline + argument description +* Default: `NULL` diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_4.txt b/vendor/symfony/console/Tests/Fixtures/input_argument_4.txt new file mode 100644 index 0000000000..aa74e8ceb2 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_4.txt @@ -0,0 +1,2 @@ + argument_name multiline + argument description diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_4.xml b/vendor/symfony/console/Tests/Fixtures/input_argument_4.xml new file mode 100644 index 0000000000..5ca135ec2c --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_4.xml @@ -0,0 +1,6 @@ + + + multiline +argument description + + diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_1.json b/vendor/symfony/console/Tests/Fixtures/input_definition_1.json new file mode 100644 index 0000000000..c7a7d838fd --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_1.json @@ -0,0 +1 @@ +{"arguments":[],"options":[]} diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_1.md b/vendor/symfony/console/Tests/Fixtures/input_definition_1.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_1.txt b/vendor/symfony/console/Tests/Fixtures/input_definition_1.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_1.xml b/vendor/symfony/console/Tests/Fixtures/input_definition_1.xml new file mode 100644 index 0000000000..b5481ce127 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_1.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_2.json b/vendor/symfony/console/Tests/Fixtures/input_definition_2.json new file mode 100644 index 0000000000..9964a55ae3 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_2.json @@ -0,0 +1 @@ +{"arguments":{"argument_name":{"name":"argument_name","is_required":true,"is_array":false,"description":"","default":null}},"options":[]} diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_2.md b/vendor/symfony/console/Tests/Fixtures/input_definition_2.md new file mode 100644 index 0000000000..923191cdcf --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_2.md @@ -0,0 +1,9 @@ +### Arguments: + +**argument_name:** + +* Name: argument_name +* Is required: yes +* Is array: no +* Description: +* Default: `NULL` diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_2.txt b/vendor/symfony/console/Tests/Fixtures/input_definition_2.txt new file mode 100644 index 0000000000..73b0f308a9 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_2.txt @@ -0,0 +1,2 @@ +Arguments: + argument_name diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_2.xml b/vendor/symfony/console/Tests/Fixtures/input_definition_2.xml new file mode 100644 index 0000000000..102efc1486 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_2.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_3.json b/vendor/symfony/console/Tests/Fixtures/input_definition_3.json new file mode 100644 index 0000000000..6a86056029 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_3.json @@ -0,0 +1 @@ +{"arguments":[],"options":{"option_name":{"name":"--option_name","shortcut":"-o","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"","default":false}}} diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_3.md b/vendor/symfony/console/Tests/Fixtures/input_definition_3.md new file mode 100644 index 0000000000..40fd7b0a97 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_3.md @@ -0,0 +1,11 @@ +### Options: + +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: +* Default: `false` diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_3.txt b/vendor/symfony/console/Tests/Fixtures/input_definition_3.txt new file mode 100644 index 0000000000..c02766fd3c --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_3.txt @@ -0,0 +1,2 @@ +Options: + -o, --option_name diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_3.xml b/vendor/symfony/console/Tests/Fixtures/input_definition_3.xml new file mode 100644 index 0000000000..bc95151548 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_3.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_4.json b/vendor/symfony/console/Tests/Fixtures/input_definition_4.json new file mode 100644 index 0000000000..c5a0019fe2 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_4.json @@ -0,0 +1 @@ +{"arguments":{"argument_name":{"name":"argument_name","is_required":true,"is_array":false,"description":"","default":null}},"options":{"option_name":{"name":"--option_name","shortcut":"-o","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"","default":false}}} diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_4.md b/vendor/symfony/console/Tests/Fixtures/input_definition_4.md new file mode 100644 index 0000000000..a31feea477 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_4.md @@ -0,0 +1,21 @@ +### Arguments: + +**argument_name:** + +* Name: argument_name +* Is required: yes +* Is array: no +* Description: +* Default: `NULL` + +### Options: + +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: +* Default: `false` diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_4.txt b/vendor/symfony/console/Tests/Fixtures/input_definition_4.txt new file mode 100644 index 0000000000..63aa81d2d1 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_4.txt @@ -0,0 +1,5 @@ +Arguments: + argument_name + +Options: + -o, --option_name diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_4.xml b/vendor/symfony/console/Tests/Fixtures/input_definition_4.xml new file mode 100644 index 0000000000..cffceecef1 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_4.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_1.json b/vendor/symfony/console/Tests/Fixtures/input_option_1.json new file mode 100644 index 0000000000..60c5b56cb4 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_1.json @@ -0,0 +1 @@ +{"name":"--option_name","shortcut":"-o","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"","default":false} diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_1.md b/vendor/symfony/console/Tests/Fixtures/input_option_1.md new file mode 100644 index 0000000000..6f9e9a7e07 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_1.md @@ -0,0 +1,9 @@ +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: +* Default: `false` diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_1.txt b/vendor/symfony/console/Tests/Fixtures/input_option_1.txt new file mode 100644 index 0000000000..3a5e4eede4 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_1.txt @@ -0,0 +1 @@ + -o, --option_name diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_1.xml b/vendor/symfony/console/Tests/Fixtures/input_option_1.xml new file mode 100644 index 0000000000..8a64ea6529 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_1.xml @@ -0,0 +1,4 @@ + + diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_2.json b/vendor/symfony/console/Tests/Fixtures/input_option_2.json new file mode 100644 index 0000000000..04e4228ec3 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_2.json @@ -0,0 +1 @@ +{"name":"--option_name","shortcut":"-o","accept_value":true,"is_value_required":false,"is_multiple":false,"description":"option description","default":"default_value"} diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_2.md b/vendor/symfony/console/Tests/Fixtures/input_option_2.md new file mode 100644 index 0000000000..634ac0b03c --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_2.md @@ -0,0 +1,9 @@ +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: yes +* Is value required: no +* Is multiple: no +* Description: option description +* Default: `'default_value'` diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_2.txt b/vendor/symfony/console/Tests/Fixtures/input_option_2.txt new file mode 100644 index 0000000000..1009eff162 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_2.txt @@ -0,0 +1 @@ + -o, --option_name[=OPTION_NAME] option description [default: "default_value"] diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_2.xml b/vendor/symfony/console/Tests/Fixtures/input_option_2.xml new file mode 100644 index 0000000000..4afac5b04e --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_2.xml @@ -0,0 +1,7 @@ + + diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_3.json b/vendor/symfony/console/Tests/Fixtures/input_option_3.json new file mode 100644 index 0000000000..c1ea120c7a --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_3.json @@ -0,0 +1 @@ +{"name":"--option_name","shortcut":"-o","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"option description","default":null} diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_3.md b/vendor/symfony/console/Tests/Fixtures/input_option_3.md new file mode 100644 index 0000000000..34282896ba --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_3.md @@ -0,0 +1,9 @@ +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Description: option description +* Default: `NULL` diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_3.txt b/vendor/symfony/console/Tests/Fixtures/input_option_3.txt new file mode 100644 index 0000000000..947bb6527b --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_3.txt @@ -0,0 +1 @@ + -o, --option_name=OPTION_NAME option description diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_3.xml b/vendor/symfony/console/Tests/Fixtures/input_option_3.xml new file mode 100644 index 0000000000..dcc0631cf4 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_3.xml @@ -0,0 +1,5 @@ + + diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_4.json b/vendor/symfony/console/Tests/Fixtures/input_option_4.json new file mode 100644 index 0000000000..1b671d8065 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_4.json @@ -0,0 +1 @@ +{"name":"--option_name","shortcut":"-o","accept_value":true,"is_value_required":false,"is_multiple":true,"description":"option description","default":[]} diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_4.md b/vendor/symfony/console/Tests/Fixtures/input_option_4.md new file mode 100644 index 0000000000..8ffba56e73 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_4.md @@ -0,0 +1,9 @@ +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: yes +* Is value required: no +* Is multiple: yes +* Description: option description +* Default: `array ()` diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_4.txt b/vendor/symfony/console/Tests/Fixtures/input_option_4.txt new file mode 100644 index 0000000000..27edf77b4b --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_4.txt @@ -0,0 +1 @@ + -o, --option_name[=OPTION_NAME] option description (multiple values allowed) diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_4.xml b/vendor/symfony/console/Tests/Fixtures/input_option_4.xml new file mode 100644 index 0000000000..5e2418b14a --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_4.xml @@ -0,0 +1,5 @@ + + diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_5.json b/vendor/symfony/console/Tests/Fixtures/input_option_5.json new file mode 100644 index 0000000000..35a1405fa4 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_5.json @@ -0,0 +1 @@ +{"name":"--option_name","shortcut":"-o","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"multiline option description","default":null} diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_5.md b/vendor/symfony/console/Tests/Fixtures/input_option_5.md new file mode 100644 index 0000000000..82f51cad30 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_5.md @@ -0,0 +1,10 @@ +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Description: multiline + option description +* Default: `NULL` diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_5.txt b/vendor/symfony/console/Tests/Fixtures/input_option_5.txt new file mode 100644 index 0000000000..4368883cc7 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_5.txt @@ -0,0 +1,2 @@ + -o, --option_name=OPTION_NAME multiline + option description diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_5.xml b/vendor/symfony/console/Tests/Fixtures/input_option_5.xml new file mode 100644 index 0000000000..90040ccd02 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_5.xml @@ -0,0 +1,6 @@ + + diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_6.json b/vendor/symfony/console/Tests/Fixtures/input_option_6.json new file mode 100644 index 0000000000..d84e872147 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_6.json @@ -0,0 +1 @@ +{"name":"--option_name","shortcut":"-o|-O","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"option with multiple shortcuts","default":null} diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_6.md b/vendor/symfony/console/Tests/Fixtures/input_option_6.md new file mode 100644 index 0000000000..ed1ea1c84b --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_6.md @@ -0,0 +1,9 @@ +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o|-O` +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Description: option with multiple shortcuts +* Default: `NULL` diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_6.txt b/vendor/symfony/console/Tests/Fixtures/input_option_6.txt new file mode 100644 index 0000000000..0e6c9759b5 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_6.txt @@ -0,0 +1 @@ + -o|O, --option_name=OPTION_NAME option with multiple shortcuts diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_6.xml b/vendor/symfony/console/Tests/Fixtures/input_option_6.xml new file mode 100644 index 0000000000..06126a2f57 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_6.xml @@ -0,0 +1,5 @@ + + diff --git a/vendor/symfony/console/Tests/Formatter/OutputFormatterStyleStackTest.php b/vendor/symfony/console/Tests/Formatter/OutputFormatterStyleStackTest.php new file mode 100644 index 0000000000..774df268ba --- /dev/null +++ b/vendor/symfony/console/Tests/Formatter/OutputFormatterStyleStackTest.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Formatter; + +use Symfony\Component\Console\Formatter\OutputFormatterStyleStack; +use Symfony\Component\Console\Formatter\OutputFormatterStyle; + +class OutputFormatterStyleStackTest extends \PHPUnit_Framework_TestCase +{ + public function testPush() + { + $stack = new OutputFormatterStyleStack(); + $stack->push($s1 = new OutputFormatterStyle('white', 'black')); + $stack->push($s2 = new OutputFormatterStyle('yellow', 'blue')); + + $this->assertEquals($s2, $stack->getCurrent()); + + $stack->push($s3 = new OutputFormatterStyle('green', 'red')); + + $this->assertEquals($s3, $stack->getCurrent()); + } + + public function testPop() + { + $stack = new OutputFormatterStyleStack(); + $stack->push($s1 = new OutputFormatterStyle('white', 'black')); + $stack->push($s2 = new OutputFormatterStyle('yellow', 'blue')); + + $this->assertEquals($s2, $stack->pop()); + $this->assertEquals($s1, $stack->pop()); + } + + public function testPopEmpty() + { + $stack = new OutputFormatterStyleStack(); + $style = new OutputFormatterStyle(); + + $this->assertEquals($style, $stack->pop()); + } + + public function testPopNotLast() + { + $stack = new OutputFormatterStyleStack(); + $stack->push($s1 = new OutputFormatterStyle('white', 'black')); + $stack->push($s2 = new OutputFormatterStyle('yellow', 'blue')); + $stack->push($s3 = new OutputFormatterStyle('green', 'red')); + + $this->assertEquals($s2, $stack->pop($s2)); + $this->assertEquals($s1, $stack->pop()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testInvalidPop() + { + $stack = new OutputFormatterStyleStack(); + $stack->push(new OutputFormatterStyle('white', 'black')); + $stack->pop(new OutputFormatterStyle('yellow', 'blue')); + } +} diff --git a/vendor/symfony/console/Tests/Formatter/OutputFormatterStyleTest.php b/vendor/symfony/console/Tests/Formatter/OutputFormatterStyleTest.php new file mode 100644 index 0000000000..0abfb3ce27 --- /dev/null +++ b/vendor/symfony/console/Tests/Formatter/OutputFormatterStyleTest.php @@ -0,0 +1,99 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Formatter; + +use Symfony\Component\Console\Formatter\OutputFormatterStyle; + +class OutputFormatterStyleTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $style = new OutputFormatterStyle('green', 'black', array('bold', 'underscore')); + $this->assertEquals("\033[32;40;1;4mfoo\033[39;49;22;24m", $style->apply('foo')); + + $style = new OutputFormatterStyle('red', null, array('blink')); + $this->assertEquals("\033[31;5mfoo\033[39;25m", $style->apply('foo')); + + $style = new OutputFormatterStyle(null, 'white'); + $this->assertEquals("\033[47mfoo\033[49m", $style->apply('foo')); + } + + public function testForeground() + { + $style = new OutputFormatterStyle(); + + $style->setForeground('black'); + $this->assertEquals("\033[30mfoo\033[39m", $style->apply('foo')); + + $style->setForeground('blue'); + $this->assertEquals("\033[34mfoo\033[39m", $style->apply('foo')); + + $style->setForeground('default'); + $this->assertEquals("\033[39mfoo\033[39m", $style->apply('foo')); + + $this->setExpectedException('InvalidArgumentException'); + $style->setForeground('undefined-color'); + } + + public function testBackground() + { + $style = new OutputFormatterStyle(); + + $style->setBackground('black'); + $this->assertEquals("\033[40mfoo\033[49m", $style->apply('foo')); + + $style->setBackground('yellow'); + $this->assertEquals("\033[43mfoo\033[49m", $style->apply('foo')); + + $style->setBackground('default'); + $this->assertEquals("\033[49mfoo\033[49m", $style->apply('foo')); + + $this->setExpectedException('InvalidArgumentException'); + $style->setBackground('undefined-color'); + } + + public function testOptions() + { + $style = new OutputFormatterStyle(); + + $style->setOptions(array('reverse', 'conceal')); + $this->assertEquals("\033[7;8mfoo\033[27;28m", $style->apply('foo')); + + $style->setOption('bold'); + $this->assertEquals("\033[7;8;1mfoo\033[27;28;22m", $style->apply('foo')); + + $style->unsetOption('reverse'); + $this->assertEquals("\033[8;1mfoo\033[28;22m", $style->apply('foo')); + + $style->setOption('bold'); + $this->assertEquals("\033[8;1mfoo\033[28;22m", $style->apply('foo')); + + $style->setOptions(array('bold')); + $this->assertEquals("\033[1mfoo\033[22m", $style->apply('foo')); + + try { + $style->setOption('foo'); + $this->fail('->setOption() throws an \InvalidArgumentException when the option does not exist in the available options'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->setOption() throws an \InvalidArgumentException when the option does not exist in the available options'); + $this->assertContains('Invalid option specified: "foo"', $e->getMessage(), '->setOption() throws an \InvalidArgumentException when the option does not exist in the available options'); + } + + try { + $style->unsetOption('foo'); + $this->fail('->unsetOption() throws an \InvalidArgumentException when the option does not exist in the available options'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->unsetOption() throws an \InvalidArgumentException when the option does not exist in the available options'); + $this->assertContains('Invalid option specified: "foo"', $e->getMessage(), '->unsetOption() throws an \InvalidArgumentException when the option does not exist in the available options'); + } + } +} diff --git a/vendor/symfony/console/Tests/Formatter/OutputFormatterTest.php b/vendor/symfony/console/Tests/Formatter/OutputFormatterTest.php new file mode 100644 index 0000000000..b8d5ca6d9b --- /dev/null +++ b/vendor/symfony/console/Tests/Formatter/OutputFormatterTest.php @@ -0,0 +1,273 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Formatter; + +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Formatter\OutputFormatterStyle; + +class OutputFormatterTest extends \PHPUnit_Framework_TestCase +{ + public function testEmptyTag() + { + $formatter = new OutputFormatter(true); + $this->assertEquals('foo<>bar', $formatter->format('foo<>bar')); + } + + public function testLGCharEscaping() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals('fooformat('foo\\assertEquals('some info', $formatter->format('\\some info\\')); + $this->assertEquals('\\some info\\', OutputFormatter::escape('some info')); + + $this->assertEquals( + "\033[33mSymfony\\Component\\Console does work very well!\033[39m", + $formatter->format('Symfony\Component\Console does work very well!') + ); + } + + public function testBundledStyles() + { + $formatter = new OutputFormatter(true); + + $this->assertTrue($formatter->hasStyle('error')); + $this->assertTrue($formatter->hasStyle('info')); + $this->assertTrue($formatter->hasStyle('comment')); + $this->assertTrue($formatter->hasStyle('question')); + + $this->assertEquals( + "\033[37;41msome error\033[39;49m", + $formatter->format('some error') + ); + $this->assertEquals( + "\033[32msome info\033[39m", + $formatter->format('some info') + ); + $this->assertEquals( + "\033[33msome comment\033[39m", + $formatter->format('some comment') + ); + $this->assertEquals( + "\033[30;46msome question\033[39;49m", + $formatter->format('some question') + ); + } + + public function testNestedStyles() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals( + "\033[37;41msome \033[39;49m\033[32msome info\033[39m\033[37;41m error\033[39;49m", + $formatter->format('some some info error') + ); + } + + public function testAdjacentStyles() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals( + "\033[37;41msome error\033[39;49m\033[32msome info\033[39m", + $formatter->format('some errorsome info') + ); + } + + public function testStyleMatchingNotGreedy() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals( + "(\033[32m>=2.0,<2.3\033[39m)", + $formatter->format('(>=2.0,<2.3)') + ); + } + + public function testStyleEscaping() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals( + "(\033[32mz>=2.0,<<format('('.$formatter->escape('z>=2.0,<\\<)') + ); + + $this->assertEquals( + "\033[32msome error\033[39m", + $formatter->format(''.$formatter->escape('some error').'') + ); + } + + public function testDeepNestedStyles() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals( + "\033[37;41merror\033[39;49m\033[32minfo\033[39m\033[33mcomment\033[39m\033[37;41merror\033[39;49m", + $formatter->format('errorinfocommenterror') + ); + } + + public function testNewStyle() + { + $formatter = new OutputFormatter(true); + + $style = new OutputFormatterStyle('blue', 'white'); + $formatter->setStyle('test', $style); + + $this->assertEquals($style, $formatter->getStyle('test')); + $this->assertNotEquals($style, $formatter->getStyle('info')); + + $style = new OutputFormatterStyle('blue', 'white'); + $formatter->setStyle('b', $style); + + $this->assertEquals("\033[34;47msome \033[39;49m\033[34;47mcustom\033[39;49m\033[34;47m msg\033[39;49m", $formatter->format('some custom msg')); + } + + public function testRedefineStyle() + { + $formatter = new OutputFormatter(true); + + $style = new OutputFormatterStyle('blue', 'white'); + $formatter->setStyle('info', $style); + + $this->assertEquals("\033[34;47msome custom msg\033[39;49m", $formatter->format('some custom msg')); + } + + public function testInlineStyle() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals("\033[34;41msome text\033[39;49m", $formatter->format('some text')); + $this->assertEquals("\033[34;41msome text\033[39;49m", $formatter->format('some text')); + } + + public function testNonStyleTag() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals("\033[32msome \033[39m\033[32m\033[39m\033[32m \033[39m\033[32m\033[39m\033[32m styled \033[39m\033[32m

\033[39m\033[32msingle-char tag\033[39m\033[32m

\033[39m", $formatter->format('some styled

single-char tag

')); + } + + public function testFormatLongString() + { + $formatter = new OutputFormatter(true); + $long = str_repeat('\\', 14000); + $this->assertEquals("\033[37;41msome error\033[39;49m".$long, $formatter->format('some error'.$long)); + } + + public function testFormatToStringObject() + { + $formatter = new OutputFormatter(false); + $this->assertEquals( + 'some info', $formatter->format(new TableCell()) + ); + } + + public function testNotDecoratedFormatter() + { + $formatter = new OutputFormatter(false); + + $this->assertTrue($formatter->hasStyle('error')); + $this->assertTrue($formatter->hasStyle('info')); + $this->assertTrue($formatter->hasStyle('comment')); + $this->assertTrue($formatter->hasStyle('question')); + + $this->assertEquals( + 'some error', $formatter->format('some error') + ); + $this->assertEquals( + 'some info', $formatter->format('some info') + ); + $this->assertEquals( + 'some comment', $formatter->format('some comment') + ); + $this->assertEquals( + 'some question', $formatter->format('some question') + ); + + $formatter->setDecorated(true); + + $this->assertEquals( + "\033[37;41msome error\033[39;49m", $formatter->format('some error') + ); + $this->assertEquals( + "\033[32msome info\033[39m", $formatter->format('some info') + ); + $this->assertEquals( + "\033[33msome comment\033[39m", $formatter->format('some comment') + ); + $this->assertEquals( + "\033[30;46msome question\033[39;49m", $formatter->format('some question') + ); + } + + public function testContentWithLineBreaks() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals(<<format(<<<'EOF' + +some text +EOF + )); + + $this->assertEquals(<<format(<<<'EOF' +some text + +EOF + )); + + $this->assertEquals(<<format(<<<'EOF' + +some text + +EOF + )); + + $this->assertEquals(<<format(<<<'EOF' + +some text +more text + +EOF + )); + } +} + +class TableCell +{ + public function __toString() + { + return 'some info'; + } +} diff --git a/vendor/symfony/console/Tests/Helper/FormatterHelperTest.php b/vendor/symfony/console/Tests/Helper/FormatterHelperTest.php new file mode 100644 index 0000000000..e0aa9211d3 --- /dev/null +++ b/vendor/symfony/console/Tests/Helper/FormatterHelperTest.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\FormatterHelper; + +class FormatterHelperTest extends \PHPUnit_Framework_TestCase +{ + public function testFormatSection() + { + $formatter = new FormatterHelper(); + + $this->assertEquals( + '[cli] Some text to display', + $formatter->formatSection('cli', 'Some text to display'), + '::formatSection() formats a message in a section' + ); + } + + public function testFormatBlock() + { + $formatter = new FormatterHelper(); + + $this->assertEquals( + ' Some text to display ', + $formatter->formatBlock('Some text to display', 'error'), + '::formatBlock() formats a message in a block' + ); + + $this->assertEquals( + ' Some text to display '."\n". + ' foo bar ', + $formatter->formatBlock(array('Some text to display', 'foo bar'), 'error'), + '::formatBlock() formats a message in a block' + ); + + $this->assertEquals( + ' '."\n". + ' Some text to display '."\n". + ' ', + $formatter->formatBlock('Some text to display', 'error', true), + '::formatBlock() formats a message in a block' + ); + } + + public function testFormatBlockWithDiacriticLetters() + { + $formatter = new FormatterHelper(); + + $this->assertEquals( + ' '."\n". + ' Du texte à afficher '."\n". + ' ', + $formatter->formatBlock('Du texte à afficher', 'error', true), + '::formatBlock() formats a message in a block' + ); + } + + public function testFormatBlockWithDoubleWidthDiacriticLetters() + { + $formatter = new FormatterHelper(); + $this->assertEquals( + ' '."\n". + ' 表示するテキスト '."\n". + ' ', + $formatter->formatBlock('表示するテキスト', 'error', true), + '::formatBlock() formats a message in a block' + ); + } + + public function testFormatBlockLGEscaping() + { + $formatter = new FormatterHelper(); + + $this->assertEquals( + ' '."\n". + ' \some info\ '."\n". + ' ', + $formatter->formatBlock('some info', 'error', true), + '::formatBlock() escapes \'<\' chars' + ); + } +} diff --git a/vendor/symfony/console/Tests/Helper/HelperSetTest.php b/vendor/symfony/console/Tests/Helper/HelperSetTest.php new file mode 100644 index 0000000000..04edd30411 --- /dev/null +++ b/vendor/symfony/console/Tests/Helper/HelperSetTest.php @@ -0,0 +1,133 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Command\Command; + +class HelperSetTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $mock_helper = $this->getGenericMockHelper('fake_helper'); + $helperset = new HelperSet(array('fake_helper_alias' => $mock_helper)); + + $this->assertEquals($mock_helper, $helperset->get('fake_helper_alias'), '__construct sets given helper to helpers'); + $this->assertTrue($helperset->has('fake_helper_alias'), '__construct sets helper alias for given helper'); + } + + public function testSet() + { + $helperset = new HelperSet(); + $helperset->set($this->getGenericMockHelper('fake_helper', $helperset)); + $this->assertTrue($helperset->has('fake_helper'), '->set() adds helper to helpers'); + + $helperset = new HelperSet(); + $helperset->set($this->getGenericMockHelper('fake_helper_01', $helperset)); + $helperset->set($this->getGenericMockHelper('fake_helper_02', $helperset)); + $this->assertTrue($helperset->has('fake_helper_01'), '->set() will set multiple helpers on consecutive calls'); + $this->assertTrue($helperset->has('fake_helper_02'), '->set() will set multiple helpers on consecutive calls'); + + $helperset = new HelperSet(); + $helperset->set($this->getGenericMockHelper('fake_helper', $helperset), 'fake_helper_alias'); + $this->assertTrue($helperset->has('fake_helper'), '->set() adds helper alias when set'); + $this->assertTrue($helperset->has('fake_helper_alias'), '->set() adds helper alias when set'); + } + + public function testHas() + { + $helperset = new HelperSet(array('fake_helper_alias' => $this->getGenericMockHelper('fake_helper'))); + $this->assertTrue($helperset->has('fake_helper'), '->has() finds set helper'); + $this->assertTrue($helperset->has('fake_helper_alias'), '->has() finds set helper by alias'); + } + + public function testGet() + { + $helper_01 = $this->getGenericMockHelper('fake_helper_01'); + $helper_02 = $this->getGenericMockHelper('fake_helper_02'); + $helperset = new HelperSet(array('fake_helper_01_alias' => $helper_01, 'fake_helper_02_alias' => $helper_02)); + $this->assertEquals($helper_01, $helperset->get('fake_helper_01'), '->get() returns correct helper by name'); + $this->assertEquals($helper_01, $helperset->get('fake_helper_01_alias'), '->get() returns correct helper by alias'); + $this->assertEquals($helper_02, $helperset->get('fake_helper_02'), '->get() returns correct helper by name'); + $this->assertEquals($helper_02, $helperset->get('fake_helper_02_alias'), '->get() returns correct helper by alias'); + + $helperset = new HelperSet(); + try { + $helperset->get('foo'); + $this->fail('->get() throws InvalidArgumentException when helper not found'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->get() throws InvalidArgumentException when helper not found'); + $this->assertInstanceOf('Symfony\Component\Console\Exception\ExceptionInterface', $e, '->get() throws domain specific exception when helper not found'); + $this->assertContains('The helper "foo" is not defined.', $e->getMessage(), '->get() throws InvalidArgumentException when helper not found'); + } + } + + public function testSetCommand() + { + $cmd_01 = new Command('foo'); + $cmd_02 = new Command('bar'); + + $helperset = new HelperSet(); + $helperset->setCommand($cmd_01); + $this->assertEquals($cmd_01, $helperset->getCommand(), '->setCommand() stores given command'); + + $helperset = new HelperSet(); + $helperset->setCommand($cmd_01); + $helperset->setCommand($cmd_02); + $this->assertEquals($cmd_02, $helperset->getCommand(), '->setCommand() overwrites stored command with consecutive calls'); + } + + public function testGetCommand() + { + $cmd = new Command('foo'); + $helperset = new HelperSet(); + $helperset->setCommand($cmd); + $this->assertEquals($cmd, $helperset->getCommand(), '->getCommand() retrieves stored command'); + } + + public function testIteration() + { + $helperset = new HelperSet(); + $helperset->set($this->getGenericMockHelper('fake_helper_01', $helperset)); + $helperset->set($this->getGenericMockHelper('fake_helper_02', $helperset)); + + $helpers = array('fake_helper_01', 'fake_helper_02'); + $i = 0; + + foreach ($helperset as $helper) { + $this->assertEquals($helpers[$i++], $helper->getName()); + } + } + + /** + * Create a generic mock for the helper interface. Optionally check for a call to setHelperSet with a specific + * helperset instance. + * + * @param string $name + * @param HelperSet $helperset allows a mock to verify a particular helperset set is being added to the Helper + */ + private function getGenericMockHelper($name, HelperSet $helperset = null) + { + $mock_helper = $this->getMock('\Symfony\Component\Console\Helper\HelperInterface'); + $mock_helper->expects($this->any()) + ->method('getName') + ->will($this->returnValue($name)); + + if ($helperset) { + $mock_helper->expects($this->any()) + ->method('setHelperSet') + ->with($this->equalTo($helperset)); + } + + return $mock_helper; + } +} diff --git a/vendor/symfony/console/Tests/Helper/HelperTest.php b/vendor/symfony/console/Tests/Helper/HelperTest.php new file mode 100644 index 0000000000..33fa22051a --- /dev/null +++ b/vendor/symfony/console/Tests/Helper/HelperTest.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\Helper; + +class HelperTest extends \PHPUnit_Framework_TestCase +{ + public function formatTimeProvider() + { + return array( + array(0, '< 1 sec'), + array(1, '1 sec'), + array(2, '2 secs'), + array(59, '59 secs'), + array(60, '1 min'), + array(61, '1 min'), + array(119, '1 min'), + array(120, '2 mins'), + array(121, '2 mins'), + array(3599, '59 mins'), + array(3600, '1 hr'), + array(7199, '1 hr'), + array(7200, '2 hrs'), + array(7201, '2 hrs'), + array(86399, '23 hrs'), + array(86400, '1 day'), + array(86401, '1 day'), + array(172799, '1 day'), + array(172800, '2 days'), + array(172801, '2 days'), + ); + } + + /** + * @dataProvider formatTimeProvider + * + * @param int $secs + * @param string $expectedFormat + */ + public function testFormatTime($secs, $expectedFormat) + { + $this->assertEquals($expectedFormat, Helper::formatTime($secs)); + } +} diff --git a/vendor/symfony/console/Tests/Helper/LegacyDialogHelperTest.php b/vendor/symfony/console/Tests/Helper/LegacyDialogHelperTest.php new file mode 100644 index 0000000000..97bf77565a --- /dev/null +++ b/vendor/symfony/console/Tests/Helper/LegacyDialogHelperTest.php @@ -0,0 +1,263 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Helper\DialogHelper; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Helper\FormatterHelper; +use Symfony\Component\Console\Output\ConsoleOutput; +use Symfony\Component\Console\Output\StreamOutput; +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * @group legacy + */ +class LegacyDialogHelperTest extends \PHPUnit_Framework_TestCase +{ + public function testSelect() + { + $dialog = new DialogHelper(); + + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $heroes = array('Superman', 'Batman', 'Spiderman'); + + $dialog->setInputStream($this->getInputStream("\n1\n 1 \nFabien\n1\nFabien\n1\n0,2\n 0 , 2 \n\n\n")); + $this->assertEquals('2', $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes, '2')); + $this->assertEquals('1', $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes)); + $this->assertEquals('1', $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes)); + $this->assertEquals('1', $dialog->select($output = $this->getOutputStream(), 'What is your favorite superhero?', $heroes, null, false, 'Input "%s" is not a superhero!', false)); + + rewind($output->getStream()); + $this->assertContains('Input "Fabien" is not a superhero!', stream_get_contents($output->getStream())); + + try { + $this->assertEquals('1', $dialog->select($output = $this->getOutputStream(), 'What is your favorite superhero?', $heroes, null, 1)); + $this->fail(); + } catch (\InvalidArgumentException $e) { + $this->assertEquals('Value "Fabien" is invalid', $e->getMessage()); + } + + $this->assertEquals(array('1'), $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes, null, false, 'Input "%s" is not a superhero!', true)); + $this->assertEquals(array('0', '2'), $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes, null, false, 'Input "%s" is not a superhero!', true)); + $this->assertEquals(array('0', '2'), $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes, null, false, 'Input "%s" is not a superhero!', true)); + $this->assertEquals(array('0', '1'), $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes, '0,1', false, 'Input "%s" is not a superhero!', true)); + $this->assertEquals(array('0', '1'), $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes, ' 0 , 1 ', false, 'Input "%s" is not a superhero!', true)); + } + + public function testSelectOnErrorOutput() + { + $dialog = new DialogHelper(); + + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $heroes = array('Superman', 'Batman', 'Spiderman'); + + $dialog->setInputStream($this->getInputStream("Stdout\n1\n")); + $this->assertEquals('1', $dialog->select($output = $this->getConsoleOutput($this->getOutputStream()), 'What is your favorite superhero?', $heroes, null, false, 'Input "%s" is not a superhero!', false)); + + rewind($output->getErrorOutput()->getStream()); + $this->assertContains('Input "Stdout" is not a superhero!', stream_get_contents($output->getErrorOutput()->getStream())); + } + + public function testAsk() + { + $dialog = new DialogHelper(); + + $dialog->setInputStream($this->getInputStream("\n8AM\n")); + + $this->assertEquals('2PM', $dialog->ask($this->getOutputStream(), 'What time is it?', '2PM')); + $this->assertEquals('8AM', $dialog->ask($output = $this->getOutputStream(), 'What time is it?', '2PM')); + + rewind($output->getStream()); + $this->assertEquals('What time is it?', stream_get_contents($output->getStream())); + } + + public function testAskOnErrorOutput() + { + if (!$this->hasSttyAvailable()) { + $this->markTestSkipped('`stderr` is required to test stderr output functionality'); + } + + $dialog = new DialogHelper(); + + $dialog->setInputStream($this->getInputStream("not stdout\n")); + + $this->assertEquals('not stdout', $dialog->ask($output = $this->getConsoleOutput($this->getOutputStream()), 'Where should output go?', 'stderr')); + + rewind($output->getErrorOutput()->getStream()); + $this->assertEquals('Where should output go?', stream_get_contents($output->getErrorOutput()->getStream())); + } + + public function testAskWithAutocomplete() + { + if (!$this->hasSttyAvailable()) { + $this->markTestSkipped('`stty` is required to test autocomplete functionality'); + } + + // Acm + // AcsTest + // + // + // Test + // + // S + // F00oo + $inputStream = $this->getInputStream("Acm\nAc\177\177s\tTest\n\n\033[A\033[A\n\033[A\033[A\033[A\033[A\033[A\tTest\n\033[B\nS\177\177\033[B\033[B\nF00\177\177oo\t\n"); + + $dialog = new DialogHelper(); + $dialog->setInputStream($inputStream); + + $bundles = array('AcmeDemoBundle', 'AsseticBundle', 'SecurityBundle', 'FooBundle'); + + $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles)); + $this->assertEquals('AsseticBundleTest', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles)); + $this->assertEquals('FrameworkBundle', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles)); + $this->assertEquals('SecurityBundle', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles)); + $this->assertEquals('FooBundleTest', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles)); + $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles)); + $this->assertEquals('AsseticBundle', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles)); + $this->assertEquals('FooBundle', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles)); + } + + /** + * @group tty + */ + public function testAskHiddenResponse() + { + if ('\\' === DIRECTORY_SEPARATOR) { + $this->markTestSkipped('This test is not supported on Windows'); + } + + $dialog = new DialogHelper(); + + $dialog->setInputStream($this->getInputStream("8AM\n")); + + $this->assertEquals('8AM', $dialog->askHiddenResponse($this->getOutputStream(), 'What time is it?')); + } + + /** + * @group tty + */ + public function testAskHiddenResponseOnErrorOutput() + { + if ('\\' === DIRECTORY_SEPARATOR) { + $this->markTestSkipped('This test is not supported on Windows'); + } + + $dialog = new DialogHelper(); + + $dialog->setInputStream($this->getInputStream("8AM\n")); + + $this->assertEquals('8AM', $dialog->askHiddenResponse($output = $this->getConsoleOutput($this->getOutputStream()), 'What time is it?')); + + rewind($output->getErrorOutput()->getStream()); + $this->assertContains('What time is it?', stream_get_contents($output->getErrorOutput()->getStream())); + } + + public function testAskConfirmation() + { + $dialog = new DialogHelper(); + + $dialog->setInputStream($this->getInputStream("\n\n")); + $this->assertTrue($dialog->askConfirmation($this->getOutputStream(), 'Do you like French fries?')); + $this->assertFalse($dialog->askConfirmation($this->getOutputStream(), 'Do you like French fries?', false)); + + $dialog->setInputStream($this->getInputStream("y\nyes\n")); + $this->assertTrue($dialog->askConfirmation($this->getOutputStream(), 'Do you like French fries?', false)); + $this->assertTrue($dialog->askConfirmation($this->getOutputStream(), 'Do you like French fries?', false)); + + $dialog->setInputStream($this->getInputStream("n\nno\n")); + $this->assertFalse($dialog->askConfirmation($this->getOutputStream(), 'Do you like French fries?', true)); + $this->assertFalse($dialog->askConfirmation($this->getOutputStream(), 'Do you like French fries?', true)); + } + + public function testAskAndValidate() + { + $dialog = new DialogHelper(); + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $question = 'What color was the white horse of Henry IV?'; + $error = 'This is not a color!'; + $validator = function ($color) use ($error) { + if (!in_array($color, array('white', 'black'))) { + throw new InvalidArgumentException($error); + } + + return $color; + }; + + $dialog->setInputStream($this->getInputStream("\nblack\n")); + $this->assertEquals('white', $dialog->askAndValidate($this->getOutputStream(), $question, $validator, 2, 'white')); + $this->assertEquals('black', $dialog->askAndValidate($this->getOutputStream(), $question, $validator, 2, 'white')); + + $dialog->setInputStream($this->getInputStream("green\nyellow\norange\n")); + try { + $this->assertEquals('white', $dialog->askAndValidate($output = $this->getConsoleOutput($this->getOutputStream()), $question, $validator, 2, 'white')); + $this->fail(); + } catch (\InvalidArgumentException $e) { + $this->assertEquals($error, $e->getMessage()); + rewind($output->getErrorOutput()->getStream()); + $this->assertContains('What color was the white horse of Henry IV?', stream_get_contents($output->getErrorOutput()->getStream())); + } + } + + public function testNoInteraction() + { + $dialog = new DialogHelper(); + + $input = new ArrayInput(array()); + $input->setInteractive(false); + + $dialog->setInput($input); + + $this->assertEquals('not yet', $dialog->ask($this->getOutputStream(), 'Do you have a job?', 'not yet')); + } + + protected function getInputStream($input) + { + $stream = fopen('php://memory', 'r+', false); + fwrite($stream, $input); + rewind($stream); + + return $stream; + } + + protected function getOutputStream() + { + return new StreamOutput(fopen('php://memory', 'r+', false)); + } + + protected function getConsoleOutput($stderr) + { + $output = new ConsoleOutput(); + $output->setErrorOutput($stderr); + + return $output; + } + + private function hasStderrSupport() + { + return false === $this->isRunningOS400(); + } + + private function hasSttyAvailable() + { + exec('stty 2>&1', $output, $exitcode); + + return $exitcode === 0; + } +} diff --git a/vendor/symfony/console/Tests/Helper/LegacyProgressHelperTest.php b/vendor/symfony/console/Tests/Helper/LegacyProgressHelperTest.php new file mode 100644 index 0000000000..f835a71e77 --- /dev/null +++ b/vendor/symfony/console/Tests/Helper/LegacyProgressHelperTest.php @@ -0,0 +1,224 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\ProgressHelper; +use Symfony\Component\Console\Output\StreamOutput; + +/** + * @group legacy + * @group time-sensitive + */ +class LegacyProgressHelperTest extends \PHPUnit_Framework_TestCase +{ + public function testAdvance() + { + $progress = new ProgressHelper(); + $progress->start($output = $this->getOutputStream()); + $progress->advance(); + + rewind($output->getStream()); + $this->assertEquals($this->generateOutput(' 1 [->--------------------------]'), stream_get_contents($output->getStream())); + } + + public function testAdvanceWithStep() + { + $progress = new ProgressHelper(); + $progress->start($output = $this->getOutputStream()); + $progress->advance(5); + + rewind($output->getStream()); + $this->assertEquals($this->generateOutput(' 5 [----->----------------------]'), stream_get_contents($output->getStream())); + } + + public function testAdvanceMultipleTimes() + { + $progress = new ProgressHelper(); + $progress->start($output = $this->getOutputStream()); + $progress->advance(3); + $progress->advance(2); + + rewind($output->getStream()); + $this->assertEquals($this->generateOutput(' 3 [--->------------------------]').$this->generateOutput(' 5 [----->----------------------]'), stream_get_contents($output->getStream())); + } + + public function testCustomizations() + { + $progress = new ProgressHelper(); + $progress->setBarWidth(10); + $progress->setBarCharacter('_'); + $progress->setEmptyBarCharacter(' '); + $progress->setProgressCharacter('/'); + $progress->setFormat(' %current%/%max% [%bar%] %percent%%'); + $progress->start($output = $this->getOutputStream(), 10); + $progress->advance(); + + rewind($output->getStream()); + $this->assertEquals($this->generateOutput(' 1/10 [_/ ] 10%'), stream_get_contents($output->getStream())); + } + + public function testPercent() + { + $progress = new ProgressHelper(); + $progress->start($output = $this->getOutputStream(), 50); + $progress->display(); + $progress->advance(); + $progress->advance(); + + rewind($output->getStream()); + $this->assertEquals($this->generateOutput(' 0/50 [>---------------------------] 0%').$this->generateOutput(' 1/50 [>---------------------------] 2%').$this->generateOutput(' 2/50 [=>--------------------------] 4%'), stream_get_contents($output->getStream())); + } + + public function testOverwriteWithShorterLine() + { + $progress = new ProgressHelper(); + $progress->setFormat(' %current%/%max% [%bar%] %percent%%'); + $progress->start($output = $this->getOutputStream(), 50); + $progress->display(); + $progress->advance(); + + // set shorter format + $progress->setFormat(' %current%/%max% [%bar%]'); + $progress->advance(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/50 [>---------------------------] 0%'). + $this->generateOutput(' 1/50 [>---------------------------] 2%'). + $this->generateOutput(' 2/50 [=>--------------------------] '), + stream_get_contents($output->getStream()) + ); + } + + public function testSetCurrentProgress() + { + $progress = new ProgressHelper(); + $progress->start($output = $this->getOutputStream(), 50); + $progress->display(); + $progress->advance(); + $progress->setCurrent(15); + $progress->setCurrent(25); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/50 [>---------------------------] 0%'). + $this->generateOutput(' 1/50 [>---------------------------] 2%'). + $this->generateOutput(' 15/50 [========>-------------------] 30%'). + $this->generateOutput(' 25/50 [==============>-------------] 50%'), + stream_get_contents($output->getStream()) + ); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage You must start the progress bar + */ + public function testSetCurrentBeforeStarting() + { + $progress = new ProgressHelper(); + $progress->setCurrent(15); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage You can't regress the progress bar + */ + public function testRegressProgress() + { + $progress = new ProgressHelper(); + $progress->start($output = $this->getOutputStream(), 50); + $progress->setCurrent(15); + $progress->setCurrent(10); + } + + public function testRedrawFrequency() + { + $progress = $this->getMock('Symfony\Component\Console\Helper\ProgressHelper', array('display')); + $progress->expects($this->exactly(4)) + ->method('display'); + + $progress->setRedrawFrequency(2); + + $progress->start($output = $this->getOutputStream(), 6); + $progress->setCurrent(1); + $progress->advance(2); + $progress->advance(2); + $progress->advance(1); + } + + public function testMultiByteSupport() + { + $progress = new ProgressHelper(); + $progress->start($output = $this->getOutputStream()); + $progress->setBarCharacter('■'); + $progress->advance(3); + + rewind($output->getStream()); + $this->assertEquals($this->generateOutput(' 3 [■■■>------------------------]'), stream_get_contents($output->getStream())); + } + + public function testClear() + { + $progress = new ProgressHelper(); + $progress->start($output = $this->getOutputStream(), 50); + $progress->setCurrent(25); + $progress->clear(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 25/50 [==============>-------------] 50%').$this->generateOutput(''), + stream_get_contents($output->getStream()) + ); + } + + public function testPercentNotHundredBeforeComplete() + { + $progress = new ProgressHelper(); + $progress->start($output = $this->getOutputStream(), 200); + $progress->display(); + $progress->advance(199); + $progress->advance(); + + rewind($output->getStream()); + $this->assertEquals($this->generateOutput(' 0/200 [>---------------------------] 0%').$this->generateOutput(' 199/200 [===========================>] 99%').$this->generateOutput(' 200/200 [============================] 100%'), stream_get_contents($output->getStream())); + } + + public function testNonDecoratedOutput() + { + $progress = new ProgressHelper(); + $progress->start($output = $this->getOutputStream(false)); + $progress->advance(); + + rewind($output->getStream()); + $this->assertEquals('', stream_get_contents($output->getStream())); + } + + protected function getOutputStream($decorated = true) + { + return new StreamOutput(fopen('php://memory', 'r+', false), StreamOutput::VERBOSITY_NORMAL, $decorated); + } + + protected $lastMessagesLength; + + protected function generateOutput($expected) + { + $expectedout = $expected; + + if ($this->lastMessagesLength !== null) { + $expectedout = str_pad($expected, $this->lastMessagesLength, "\x20", STR_PAD_RIGHT); + } + + $this->lastMessagesLength = strlen($expectedout); + + return "\x0D".$expectedout; + } +} diff --git a/vendor/symfony/console/Tests/Helper/LegacyTableHelperTest.php b/vendor/symfony/console/Tests/Helper/LegacyTableHelperTest.php new file mode 100644 index 0000000000..557dc14fcd --- /dev/null +++ b/vendor/symfony/console/Tests/Helper/LegacyTableHelperTest.php @@ -0,0 +1,316 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\TableHelper; +use Symfony\Component\Console\Output\StreamOutput; + +/** + * @group legacy + */ +class LegacyTableHelperTest extends \PHPUnit_Framework_TestCase +{ + protected $stream; + + protected function setUp() + { + $this->stream = fopen('php://memory', 'r+'); + } + + protected function tearDown() + { + fclose($this->stream); + $this->stream = null; + } + + /** + * @dataProvider testRenderProvider + */ + public function testRender($headers, $rows, $layout, $expected) + { + $table = new TableHelper(); + $table + ->setHeaders($headers) + ->setRows($rows) + ->setLayout($layout) + ; + $table->render($output = $this->getOutputStream()); + + $this->assertEquals($expected, $this->getOutputContent($output)); + } + + /** + * @dataProvider testRenderProvider + */ + public function testRenderAddRows($headers, $rows, $layout, $expected) + { + $table = new TableHelper(); + $table + ->setHeaders($headers) + ->addRows($rows) + ->setLayout($layout) + ; + $table->render($output = $this->getOutputStream()); + + $this->assertEquals($expected, $this->getOutputContent($output)); + } + + /** + * @dataProvider testRenderProvider + */ + public function testRenderAddRowsOneByOne($headers, $rows, $layout, $expected) + { + $table = new TableHelper(); + $table + ->setHeaders($headers) + ->setLayout($layout) + ; + foreach ($rows as $row) { + $table->addRow($row); + } + $table->render($output = $this->getOutputStream()); + + $this->assertEquals($expected, $this->getOutputContent($output)); + } + + public function testRenderProvider() + { + $books = array( + array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'), + array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'), + array('960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'), + array('80-902734-1-6', 'And Then There Were None', 'Agatha Christie'), + ); + + return array( + array( + array('ISBN', 'Title', 'Author'), + $books, + TableHelper::LAYOUT_DEFAULT, +<<<'TABLE' ++---------------+--------------------------+------------------+ +| ISBN | Title | Author | ++---------------+--------------------------+------------------+ +| 99921-58-10-7 | Divine Comedy | Dante Alighieri | +| 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | +| 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien | +| 80-902734-1-6 | And Then There Were None | Agatha Christie | ++---------------+--------------------------+------------------+ + +TABLE + ), + array( + array('ISBN', 'Title', 'Author'), + $books, + TableHelper::LAYOUT_COMPACT, +<< array( + array('ISBN', 'Title', 'Author'), + array( + array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'), + array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'), + ), + TableHelper::LAYOUT_DEFAULT, +<<<'TABLE' ++---------------+----------------------+-----------------+ +| ISBN | Title | Author | ++---------------+----------------------+-----------------+ +| 99921-58-10-7 | Divine Comedy | Dante Alighieri | +| 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | ++---------------+----------------------+-----------------+ + +TABLE + ), + 'Cell text with tags not used for Output styling' => array( + array('ISBN', 'Title', 'Author'), + array( + array('99921-58-10-700', 'Divine Com', 'Dante Alighieri'), + array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'), + ), + TableHelper::LAYOUT_DEFAULT, +<<<'TABLE' ++----------------------------------+----------------------+-----------------+ +| ISBN | Title | Author | ++----------------------------------+----------------------+-----------------+ +| 99921-58-10-700 | Divine Com | Dante Alighieri | +| 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | ++----------------------------------+----------------------+-----------------+ + +TABLE + ), + ); + } + + public function testRenderMultiByte() + { + $table = new TableHelper(); + $table + ->setHeaders(array('■■')) + ->setRows(array(array(1234))) + ->setLayout(TableHelper::LAYOUT_DEFAULT) + ; + $table->render($output = $this->getOutputStream()); + + $expected = +<<<'TABLE' ++------+ +| ■■ | ++------+ +| 1234 | ++------+ + +TABLE; + + $this->assertEquals($expected, $this->getOutputContent($output)); + } + + public function testRenderFullWidthCharacters() + { + $table = new TableHelper(); + $table + ->setHeaders(array('あいうえお')) + ->setRows(array(array(1234567890))) + ->setLayout(TableHelper::LAYOUT_DEFAULT) + ; + $table->render($output = $this->getOutputStream()); + + $expected = + <<<'TABLE' ++------------+ +| あいうえお | ++------------+ +| 1234567890 | ++------------+ + +TABLE; + + $this->assertEquals($expected, $this->getOutputContent($output)); + } + + protected function getOutputStream() + { + return new StreamOutput($this->stream, StreamOutput::VERBOSITY_NORMAL, false); + } + + protected function getOutputContent(StreamOutput $output) + { + rewind($output->getStream()); + + return str_replace(PHP_EOL, "\n", stream_get_contents($output->getStream())); + } +} diff --git a/vendor/symfony/console/Tests/Helper/ProcessHelperTest.php b/vendor/symfony/console/Tests/Helper/ProcessHelperTest.php new file mode 100644 index 0000000000..a51fb4359d --- /dev/null +++ b/vendor/symfony/console/Tests/Helper/ProcessHelperTest.php @@ -0,0 +1,117 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\DebugFormatterHelper; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Output\StreamOutput; +use Symfony\Component\Console\Helper\ProcessHelper; +use Symfony\Component\Process\Process; + +class ProcessHelperTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider provideCommandsAndOutput + */ + public function testVariousProcessRuns($expected, $cmd, $verbosity, $error) + { + $helper = new ProcessHelper(); + $helper->setHelperSet(new HelperSet(array(new DebugFormatterHelper()))); + $output = $this->getOutputStream($verbosity); + $helper->run($output, $cmd, $error); + $this->assertEquals($expected, $this->getOutput($output)); + } + + public function testPassedCallbackIsExecuted() + { + $helper = new ProcessHelper(); + $helper->setHelperSet(new HelperSet(array(new DebugFormatterHelper()))); + $output = $this->getOutputStream(StreamOutput::VERBOSITY_NORMAL); + + $executed = false; + $callback = function () use (&$executed) { $executed = true; }; + + $helper->run($output, 'php -r "echo 42;"', null, $callback); + $this->assertTrue($executed); + } + + public function provideCommandsAndOutput() + { + $successOutputVerbose = <<42';" + OUT 42 + RES Command ran successfully + +EOT; + $successOutputProcessDebug = <<42\';"', StreamOutput::VERBOSITY_DEBUG, null), + array('', 'php -r "syntax error"', StreamOutput::VERBOSITY_VERBOSE, null), + array($syntaxErrorOutputVerbose, 'php -r "fwrite(STDERR, \'error message\');usleep(50000);fwrite(STDOUT, \'out message\');exit(252);"', StreamOutput::VERBOSITY_VERY_VERBOSE, null), + array($syntaxErrorOutputDebug, 'php -r "fwrite(STDERR, \'error message\');usleep(500000);fwrite(STDOUT, \'out message\');exit(252);"', StreamOutput::VERBOSITY_DEBUG, null), + array($errorMessage.PHP_EOL, 'php -r "fwrite(STDERR, \'error message\');usleep(50000);fwrite(STDOUT, \'out message\');exit(252);"', StreamOutput::VERBOSITY_VERBOSE, $errorMessage), + array($syntaxErrorOutputVerbose.$errorMessage.PHP_EOL, 'php -r "fwrite(STDERR, \'error message\');usleep(50000);fwrite(STDOUT, \'out message\');exit(252);"', StreamOutput::VERBOSITY_VERY_VERBOSE, $errorMessage), + array($syntaxErrorOutputDebug.$errorMessage.PHP_EOL, 'php -r "fwrite(STDERR, \'error message\');usleep(500000);fwrite(STDOUT, \'out message\');exit(252);"', StreamOutput::VERBOSITY_DEBUG, $errorMessage), + array($successOutputProcessDebug, array('php', '-r', 'echo 42;'), StreamOutput::VERBOSITY_DEBUG, null), + array($successOutputDebug, new Process('php -r "echo 42;"'), StreamOutput::VERBOSITY_DEBUG, null), + ); + } + + private function getOutputStream($verbosity) + { + return new StreamOutput(fopen('php://memory', 'r+', false), $verbosity, false); + } + + private function getOutput(StreamOutput $output) + { + rewind($output->getStream()); + + return stream_get_contents($output->getStream()); + } +} diff --git a/vendor/symfony/console/Tests/Helper/ProgressBarTest.php b/vendor/symfony/console/Tests/Helper/ProgressBarTest.php new file mode 100644 index 0000000000..261908b542 --- /dev/null +++ b/vendor/symfony/console/Tests/Helper/ProgressBarTest.php @@ -0,0 +1,664 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\ProgressBar; +use Symfony\Component\Console\Helper\Helper; +use Symfony\Component\Console\Output\StreamOutput; + +/** + * @group time-sensitive + */ +class ProgressBarTest extends \PHPUnit_Framework_TestCase +{ + public function testMultipleStart() + { + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->start(); + $bar->advance(); + $bar->start(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0 [>---------------------------]'). + $this->generateOutput(' 1 [->--------------------------]'). + $this->generateOutput(' 0 [>---------------------------]'), + stream_get_contents($output->getStream()) + ); + } + + public function testAdvance() + { + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->start(); + $bar->advance(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0 [>---------------------------]'). + $this->generateOutput(' 1 [->--------------------------]'), + stream_get_contents($output->getStream()) + ); + } + + public function testAdvanceWithStep() + { + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->start(); + $bar->advance(5); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0 [>---------------------------]'). + $this->generateOutput(' 5 [----->----------------------]'), + stream_get_contents($output->getStream()) + ); + } + + public function testAdvanceMultipleTimes() + { + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->start(); + $bar->advance(3); + $bar->advance(2); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0 [>---------------------------]'). + $this->generateOutput(' 3 [--->------------------------]'). + $this->generateOutput(' 5 [----->----------------------]'), + stream_get_contents($output->getStream()) + ); + } + + public function testAdvanceOverMax() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 10); + $bar->setProgress(9); + $bar->advance(); + $bar->advance(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 9/10 [=========================>--] 90%'). + $this->generateOutput(' 10/10 [============================] 100%'). + $this->generateOutput(' 11/11 [============================] 100%'), + stream_get_contents($output->getStream()) + ); + } + + public function testFormat() + { + $expected = + $this->generateOutput(' 0/10 [>---------------------------] 0%'). + $this->generateOutput(' 10/10 [============================] 100%'). + $this->generateOutput(' 10/10 [============================] 100%') + ; + + // max in construct, no format + $bar = new ProgressBar($output = $this->getOutputStream(), 10); + $bar->start(); + $bar->advance(10); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals($expected, stream_get_contents($output->getStream())); + + // max in start, no format + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->start(10); + $bar->advance(10); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals($expected, stream_get_contents($output->getStream())); + + // max in construct, explicit format before + $bar = new ProgressBar($output = $this->getOutputStream(), 10); + $bar->setFormat('normal'); + $bar->start(); + $bar->advance(10); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals($expected, stream_get_contents($output->getStream())); + + // max in start, explicit format before + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->setFormat('normal'); + $bar->start(10); + $bar->advance(10); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals($expected, stream_get_contents($output->getStream())); + } + + public function testCustomizations() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 10); + $bar->setBarWidth(10); + $bar->setBarCharacter('_'); + $bar->setEmptyBarCharacter(' '); + $bar->setProgressCharacter('/'); + $bar->setFormat(' %current%/%max% [%bar%] %percent:3s%%'); + $bar->start(); + $bar->advance(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/10 [/ ] 0%'). + $this->generateOutput(' 1/10 [_/ ] 10%'), + stream_get_contents($output->getStream()) + ); + } + + public function testDisplayWithoutStart() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 50); + $bar->display(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/50 [>---------------------------] 0%'), + stream_get_contents($output->getStream()) + ); + } + + public function testDisplayWithQuietVerbosity() + { + $bar = new ProgressBar($output = $this->getOutputStream(true, StreamOutput::VERBOSITY_QUIET), 50); + $bar->display(); + + rewind($output->getStream()); + $this->assertEquals( + '', + stream_get_contents($output->getStream()) + ); + } + + public function testFinishWithoutStart() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 50); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 50/50 [============================] 100%'), + stream_get_contents($output->getStream()) + ); + } + + public function testPercent() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 50); + $bar->start(); + $bar->display(); + $bar->advance(); + $bar->advance(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/50 [>---------------------------] 0%'). + $this->generateOutput(' 0/50 [>---------------------------] 0%'). + $this->generateOutput(' 1/50 [>---------------------------] 2%'). + $this->generateOutput(' 2/50 [=>--------------------------] 4%'), + stream_get_contents($output->getStream()) + ); + } + + public function testOverwriteWithShorterLine() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 50); + $bar->setFormat(' %current%/%max% [%bar%] %percent:3s%%'); + $bar->start(); + $bar->display(); + $bar->advance(); + + // set shorter format + $bar->setFormat(' %current%/%max% [%bar%]'); + $bar->advance(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/50 [>---------------------------] 0%'). + $this->generateOutput(' 0/50 [>---------------------------] 0%'). + $this->generateOutput(' 1/50 [>---------------------------] 2%'). + $this->generateOutput(' 2/50 [=>--------------------------]'), + stream_get_contents($output->getStream()) + ); + } + + public function testStartWithMax() + { + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->setFormat('%current%/%max% [%bar%]'); + $bar->start(50); + $bar->advance(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/50 [>---------------------------]'). + $this->generateOutput(' 1/50 [>---------------------------]'), + stream_get_contents($output->getStream()) + ); + } + + public function testSetCurrentProgress() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 50); + $bar->start(); + $bar->display(); + $bar->advance(); + $bar->setProgress(15); + $bar->setProgress(25); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/50 [>---------------------------] 0%'). + $this->generateOutput(' 0/50 [>---------------------------] 0%'). + $this->generateOutput(' 1/50 [>---------------------------] 2%'). + $this->generateOutput(' 15/50 [========>-------------------] 30%'). + $this->generateOutput(' 25/50 [==============>-------------] 50%'), + stream_get_contents($output->getStream()) + ); + } + + /** + */ + public function testSetCurrentBeforeStarting() + { + $bar = new ProgressBar($this->getOutputStream()); + $bar->setProgress(15); + $this->assertNotNull($bar->getStartTime()); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage You can't regress the progress bar + */ + public function testRegressProgress() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 50); + $bar->start(); + $bar->setProgress(15); + $bar->setProgress(10); + } + + public function testRedrawFrequency() + { + $bar = $this->getMock('Symfony\Component\Console\Helper\ProgressBar', array('display'), array($this->getOutputStream(), 6)); + $bar->expects($this->exactly(4))->method('display'); + + $bar->setRedrawFrequency(2); + $bar->start(); + $bar->setProgress(1); + $bar->advance(2); + $bar->advance(2); + $bar->advance(1); + } + + public function testRedrawFrequencyIsAtLeastOneIfZeroGiven() + { + $bar = $this->getMock('Symfony\Component\Console\Helper\ProgressBar', array('display'), array($this->getOutputStream())); + + $bar->expects($this->exactly(2))->method('display'); + $bar->setRedrawFrequency(0); + $bar->start(); + $bar->advance(); + } + + public function testRedrawFrequencyIsAtLeastOneIfSmallerOneGiven() + { + $bar = $this->getMock('Symfony\Component\Console\Helper\ProgressBar', array('display'), array($this->getOutputStream())); + + $bar->expects($this->exactly(2))->method('display'); + $bar->setRedrawFrequency(0.9); + $bar->start(); + $bar->advance(); + } + + public function testMultiByteSupport() + { + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->start(); + $bar->setBarCharacter('■'); + $bar->advance(3); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0 [>---------------------------]'). + $this->generateOutput(' 3 [■■■>------------------------]'), + stream_get_contents($output->getStream()) + ); + } + + public function testClear() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 50); + $bar->start(); + $bar->setProgress(25); + $bar->clear(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/50 [>---------------------------] 0%'). + $this->generateOutput(' 25/50 [==============>-------------] 50%'). + $this->generateOutput(''), + stream_get_contents($output->getStream()) + ); + } + + public function testPercentNotHundredBeforeComplete() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 200); + $bar->start(); + $bar->display(); + $bar->advance(199); + $bar->advance(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/200 [>---------------------------] 0%'). + $this->generateOutput(' 0/200 [>---------------------------] 0%'). + $this->generateOutput(' 199/200 [===========================>] 99%'). + $this->generateOutput(' 200/200 [============================] 100%'), + stream_get_contents($output->getStream()) + ); + } + + public function testNonDecoratedOutput() + { + $bar = new ProgressBar($output = $this->getOutputStream(false), 200); + $bar->start(); + + for ($i = 0; $i < 200; ++$i) { + $bar->advance(); + } + + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals( + ' 0/200 [>---------------------------] 0%'.PHP_EOL. + ' 20/200 [==>-------------------------] 10%'.PHP_EOL. + ' 40/200 [=====>----------------------] 20%'.PHP_EOL. + ' 60/200 [========>-------------------] 30%'.PHP_EOL. + ' 80/200 [===========>----------------] 40%'.PHP_EOL. + ' 100/200 [==============>-------------] 50%'.PHP_EOL. + ' 120/200 [================>-----------] 60%'.PHP_EOL. + ' 140/200 [===================>--------] 70%'.PHP_EOL. + ' 160/200 [======================>-----] 80%'.PHP_EOL. + ' 180/200 [=========================>--] 90%'.PHP_EOL. + ' 200/200 [============================] 100%', + stream_get_contents($output->getStream()) + ); + } + + public function testNonDecoratedOutputWithClear() + { + $bar = new ProgressBar($output = $this->getOutputStream(false), 50); + $bar->start(); + $bar->setProgress(25); + $bar->clear(); + $bar->setProgress(50); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals( + ' 0/50 [>---------------------------] 0%'.PHP_EOL. + ' 25/50 [==============>-------------] 50%'.PHP_EOL. + ' 50/50 [============================] 100%', + stream_get_contents($output->getStream()) + ); + } + + public function testNonDecoratedOutputWithoutMax() + { + $bar = new ProgressBar($output = $this->getOutputStream(false)); + $bar->start(); + $bar->advance(); + + rewind($output->getStream()); + $this->assertEquals( + ' 0 [>---------------------------]'.PHP_EOL. + ' 1 [->--------------------------]', + stream_get_contents($output->getStream()) + ); + } + + public function testParallelBars() + { + $output = $this->getOutputStream(); + $bar1 = new ProgressBar($output, 2); + $bar2 = new ProgressBar($output, 3); + $bar2->setProgressCharacter('#'); + $bar3 = new ProgressBar($output); + + $bar1->start(); + $output->write("\n"); + $bar2->start(); + $output->write("\n"); + $bar3->start(); + + for ($i = 1; $i <= 3; ++$i) { + // up two lines + $output->write("\033[2A"); + if ($i <= 2) { + $bar1->advance(); + } + $output->write("\n"); + $bar2->advance(); + $output->write("\n"); + $bar3->advance(); + } + $output->write("\033[2A"); + $output->write("\n"); + $output->write("\n"); + $bar3->finish(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/2 [>---------------------------] 0%')."\n". + $this->generateOutput(' 0/3 [#---------------------------] 0%')."\n". + rtrim($this->generateOutput(' 0 [>---------------------------]')). + + "\033[2A". + $this->generateOutput(' 1/2 [==============>-------------] 50%')."\n". + $this->generateOutput(' 1/3 [=========#------------------] 33%')."\n". + rtrim($this->generateOutput(' 1 [->--------------------------]')). + + "\033[2A". + $this->generateOutput(' 2/2 [============================] 100%')."\n". + $this->generateOutput(' 2/3 [==================#---------] 66%')."\n". + rtrim($this->generateOutput(' 2 [-->-------------------------]')). + + "\033[2A". + "\n". + $this->generateOutput(' 3/3 [============================] 100%')."\n". + rtrim($this->generateOutput(' 3 [--->------------------------]')). + + "\033[2A". + "\n". + "\n". + rtrim($this->generateOutput(' 3 [============================]')), + stream_get_contents($output->getStream()) + ); + } + + public function testWithoutMax() + { + $output = $this->getOutputStream(); + + $bar = new ProgressBar($output); + $bar->start(); + $bar->advance(); + $bar->advance(); + $bar->advance(); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals( + rtrim($this->generateOutput(' 0 [>---------------------------]')). + rtrim($this->generateOutput(' 1 [->--------------------------]')). + rtrim($this->generateOutput(' 2 [-->-------------------------]')). + rtrim($this->generateOutput(' 3 [--->------------------------]')). + rtrim($this->generateOutput(' 3 [============================]')), + stream_get_contents($output->getStream()) + ); + } + + public function testAddingPlaceholderFormatter() + { + ProgressBar::setPlaceholderFormatterDefinition('remaining_steps', function (ProgressBar $bar) { + return $bar->getMaxSteps() - $bar->getProgress(); + }); + $bar = new ProgressBar($output = $this->getOutputStream(), 3); + $bar->setFormat(' %remaining_steps% [%bar%]'); + + $bar->start(); + $bar->advance(); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 3 [>---------------------------]'). + $this->generateOutput(' 2 [=========>------------------]'). + $this->generateOutput(' 0 [============================]'), + stream_get_contents($output->getStream()) + ); + } + + public function testMultilineFormat() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 3); + $bar->setFormat("%bar%\nfoobar"); + + $bar->start(); + $bar->advance(); + $bar->clear(); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(">---------------------------\nfoobar"). + $this->generateOutput("=========>------------------\nfoobar"). + "\x0D\x1B[2K\x1B[1A\x1B[2K". + $this->generateOutput("============================\nfoobar"), + stream_get_contents($output->getStream()) + ); + } + + public function testAnsiColorsAndEmojis() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 15); + ProgressBar::setPlaceholderFormatterDefinition('memory', function (ProgressBar $bar) { + static $i = 0; + $mem = 100000 * $i; + $colors = $i++ ? '41;37' : '44;37'; + + return "\033[".$colors.'m '.Helper::formatMemory($mem)." \033[0m"; + }); + $bar->setFormat(" \033[44;37m %title:-37s% \033[0m\n %current%/%max% %bar% %percent:3s%%\n 🏁 %remaining:-10s% %memory:37s%"); + $bar->setBarCharacter($done = "\033[32m●\033[0m"); + $bar->setEmptyBarCharacter($empty = "\033[31m●\033[0m"); + $bar->setProgressCharacter($progress = "\033[32m➤ \033[0m"); + + $bar->setMessage('Starting the demo... fingers crossed', 'title'); + $bar->start(); + $bar->setMessage('Looks good to me...', 'title'); + $bar->advance(4); + $bar->setMessage('Thanks, bye', 'title'); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput( + " \033[44;37m Starting the demo... fingers crossed \033[0m\n". + ' 0/15 '.$progress.str_repeat($empty, 26)." 0%\n". + " \xf0\x9f\x8f\x81 < 1 sec \033[44;37m 0 B \033[0m" + ). + $this->generateOutput( + " \033[44;37m Looks good to me... \033[0m\n". + ' 4/15 '.str_repeat($done, 7).$progress.str_repeat($empty, 19)." 26%\n". + " \xf0\x9f\x8f\x81 < 1 sec \033[41;37m 97 KiB \033[0m" + ). + $this->generateOutput( + " \033[44;37m Thanks, bye \033[0m\n". + ' 15/15 '.str_repeat($done, 28)." 100%\n". + " \xf0\x9f\x8f\x81 < 1 sec \033[41;37m 195 KiB \033[0m" + ), + stream_get_contents($output->getStream()) + ); + } + + public function testSetFormat() + { + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->setFormat('normal'); + $bar->start(); + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0 [>---------------------------]'), + stream_get_contents($output->getStream()) + ); + + $bar = new ProgressBar($output = $this->getOutputStream(), 10); + $bar->setFormat('normal'); + $bar->start(); + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/10 [>---------------------------] 0%'), + stream_get_contents($output->getStream()) + ); + } + + /** + * @dataProvider provideFormat + */ + public function testFormatsWithoutMax($format) + { + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->setFormat($format); + $bar->start(); + + rewind($output->getStream()); + $this->assertNotEmpty(stream_get_contents($output->getStream())); + } + + /** + * Provides each defined format. + * + * @return array + */ + public function provideFormat() + { + return array( + array('normal'), + array('verbose'), + array('very_verbose'), + array('debug'), + ); + } + + protected function getOutputStream($decorated = true, $verbosity = StreamOutput::VERBOSITY_NORMAL) + { + return new StreamOutput(fopen('php://memory', 'r+', false), $verbosity, $decorated); + } + + protected function generateOutput($expected) + { + $count = substr_count($expected, "\n"); + + return "\x0D\x1B[2K".($count ? str_repeat("\x1B[1A\x1B[2K", $count) : '').$expected; + } +} diff --git a/vendor/symfony/console/Tests/Helper/ProgressIndicatorTest.php b/vendor/symfony/console/Tests/Helper/ProgressIndicatorTest.php new file mode 100644 index 0000000000..192625263d --- /dev/null +++ b/vendor/symfony/console/Tests/Helper/ProgressIndicatorTest.php @@ -0,0 +1,182 @@ +getOutputStream()); + $bar->start('Starting...'); + usleep(101000); + $bar->advance(); + usleep(101000); + $bar->advance(); + usleep(101000); + $bar->advance(); + usleep(101000); + $bar->advance(); + usleep(101000); + $bar->advance(); + usleep(101000); + $bar->setMessage('Advancing...'); + $bar->advance(); + $bar->finish('Done...'); + $bar->start('Starting Again...'); + usleep(101000); + $bar->advance(); + $bar->finish('Done Again...'); + + rewind($output->getStream()); + + $this->assertEquals( + $this->generateOutput(' - Starting...'). + $this->generateOutput(' \\ Starting...'). + $this->generateOutput(' | Starting...'). + $this->generateOutput(' / Starting...'). + $this->generateOutput(' - Starting...'). + $this->generateOutput(' \\ Starting...'). + $this->generateOutput(' \\ Advancing...'). + $this->generateOutput(' | Advancing...'). + $this->generateOutput(' | Done... '). + PHP_EOL. + $this->generateOutput(' - Starting Again...'). + $this->generateOutput(' \\ Starting Again...'). + $this->generateOutput(' \\ Done Again... '). + PHP_EOL, + stream_get_contents($output->getStream()) + ); + } + + public function testNonDecoratedOutput() + { + $bar = new ProgressIndicator($output = $this->getOutputStream(false)); + + $bar->start('Starting...'); + $bar->advance(); + $bar->advance(); + $bar->setMessage('Midway...'); + $bar->advance(); + $bar->advance(); + $bar->finish('Done...'); + + rewind($output->getStream()); + + $this->assertEquals( + ' Starting...'.PHP_EOL. + ' Midway... '.PHP_EOL. + ' Done... '.PHP_EOL.PHP_EOL, + stream_get_contents($output->getStream()) + ); + } + + public function testCustomIndicatorValues() + { + $bar = new ProgressIndicator($output = $this->getOutputStream(), null, 100, array('a', 'b', 'c')); + + $bar->start('Starting...'); + usleep(101000); + $bar->advance(); + usleep(101000); + $bar->advance(); + usleep(101000); + $bar->advance(); + + rewind($output->getStream()); + + $this->assertEquals( + $this->generateOutput(' a Starting...'). + $this->generateOutput(' b Starting...'). + $this->generateOutput(' c Starting...'). + $this->generateOutput(' a Starting...'), + stream_get_contents($output->getStream()) + ); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Must have at least 2 indicator value characters. + */ + public function testCannotSetInvalidIndicatorCharacters() + { + $bar = new ProgressIndicator($this->getOutputStream(), null, 100, array('1')); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Progress indicator already started. + */ + public function testCannotStartAlreadyStartedIndicator() + { + $bar = new ProgressIndicator($this->getOutputStream()); + $bar->start('Starting...'); + $bar->start('Starting Again.'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Progress indicator has not yet been started. + */ + public function testCannotAdvanceUnstartedIndicator() + { + $bar = new ProgressIndicator($this->getOutputStream()); + $bar->advance(); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Progress indicator has not yet been started. + */ + public function testCannotFinishUnstartedIndicator() + { + $bar = new ProgressIndicator($this->getOutputStream()); + $bar->finish('Finished'); + } + + /** + * @dataProvider provideFormat + */ + public function testFormats($format) + { + $bar = new ProgressIndicator($output = $this->getOutputStream(), $format); + $bar->start('Starting...'); + $bar->advance(); + + rewind($output->getStream()); + + $this->assertNotEmpty(stream_get_contents($output->getStream())); + } + + /** + * Provides each defined format. + * + * @return array + */ + public function provideFormat() + { + return array( + array('normal'), + array('verbose'), + array('very_verbose'), + array('debug'), + ); + } + + protected function getOutputStream($decorated = true, $verbosity = StreamOutput::VERBOSITY_NORMAL) + { + return new StreamOutput(fopen('php://memory', 'r+', false), $verbosity, $decorated); + } + + protected function generateOutput($expected) + { + $count = substr_count($expected, "\n"); + + return "\x0D".($count ? sprintf("\033[%dA", $count) : '').$expected; + } +} diff --git a/vendor/symfony/console/Tests/Helper/QuestionHelperTest.php b/vendor/symfony/console/Tests/Helper/QuestionHelperTest.php new file mode 100644 index 0000000000..6a4f8aceae --- /dev/null +++ b/vendor/symfony/console/Tests/Helper/QuestionHelperTest.php @@ -0,0 +1,435 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Helper\QuestionHelper; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Helper\FormatterHelper; +use Symfony\Component\Console\Output\StreamOutput; +use Symfony\Component\Console\Question\ChoiceQuestion; +use Symfony\Component\Console\Question\ConfirmationQuestion; +use Symfony\Component\Console\Question\Question; + +/** + * @group tty + */ +class QuestionHelperTest extends \PHPUnit_Framework_TestCase +{ + public function testAskChoice() + { + $questionHelper = new QuestionHelper(); + + $helperSet = new HelperSet(array(new FormatterHelper())); + $questionHelper->setHelperSet($helperSet); + + $heroes = array('Superman', 'Batman', 'Spiderman'); + + $questionHelper->setInputStream($this->getInputStream("\n1\n 1 \nFabien\n1\nFabien\n1\n0,2\n 0 , 2 \n\n\n")); + + $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '2'); + $question->setMaxAttempts(1); + // first answer is an empty answer, we're supposed to receive the default value + $this->assertEquals('Spiderman', $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + + $question = new ChoiceQuestion('What is your favorite superhero?', $heroes); + $question->setMaxAttempts(1); + $this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + + $question = new ChoiceQuestion('What is your favorite superhero?', $heroes); + $question->setErrorMessage('Input "%s" is not a superhero!'); + $question->setMaxAttempts(2); + $this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question)); + + rewind($output->getStream()); + $stream = stream_get_contents($output->getStream()); + $this->assertContains('Input "Fabien" is not a superhero!', $stream); + + try { + $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '1'); + $question->setMaxAttempts(1); + $questionHelper->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question); + $this->fail(); + } catch (\InvalidArgumentException $e) { + $this->assertEquals('Value "Fabien" is invalid', $e->getMessage()); + } + + $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, null); + $question->setMaxAttempts(1); + $question->setMultiselect(true); + + $this->assertEquals(array('Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals(array('Superman', 'Spiderman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals(array('Superman', 'Spiderman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + + $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '0,1'); + $question->setMaxAttempts(1); + $question->setMultiselect(true); + + $this->assertEquals(array('Superman', 'Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + + $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, ' 0 , 1 '); + $question->setMaxAttempts(1); + $question->setMultiselect(true); + + $this->assertEquals(array('Superman', 'Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + } + + public function testAsk() + { + $dialog = new QuestionHelper(); + + $dialog->setInputStream($this->getInputStream("\n8AM\n")); + + $question = new Question('What time is it?', '2PM'); + $this->assertEquals('2PM', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + + $question = new Question('What time is it?', '2PM'); + $this->assertEquals('8AM', $dialog->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question)); + + rewind($output->getStream()); + $this->assertEquals('What time is it?', stream_get_contents($output->getStream())); + } + + public function testAskWithAutocomplete() + { + if (!$this->hasSttyAvailable()) { + $this->markTestSkipped('`stty` is required to test autocomplete functionality'); + } + + // Acm + // AcsTest + // + // + // Test + // + // S + // F00oo + $inputStream = $this->getInputStream("Acm\nAc\177\177s\tTest\n\n\033[A\033[A\n\033[A\033[A\033[A\033[A\033[A\tTest\n\033[B\nS\177\177\033[B\033[B\nF00\177\177oo\t\n"); + + $dialog = new QuestionHelper(); + $dialog->setInputStream($inputStream); + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $question = new Question('Please select a bundle', 'FrameworkBundle'); + $question->setAutocompleterValues(array('AcmeDemoBundle', 'AsseticBundle', 'SecurityBundle', 'FooBundle')); + + $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('AsseticBundleTest', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('FrameworkBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('SecurityBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('FooBundleTest', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('AsseticBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('FooBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + } + + public function testAskWithAutocompleteWithNonSequentialKeys() + { + if (!$this->hasSttyAvailable()) { + $this->markTestSkipped('`stty` is required to test autocomplete functionality'); + } + + // + $inputStream = $this->getInputStream("\033[A\033[A\n\033[B\033[B\n"); + + $dialog = new QuestionHelper(); + $dialog->setInputStream($inputStream); + $dialog->setHelperSet(new HelperSet(array(new FormatterHelper()))); + + $question = new ChoiceQuestion('Please select a bundle', array(1 => 'AcmeDemoBundle', 4 => 'AsseticBundle')); + $question->setMaxAttempts(1); + + $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('AsseticBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + } + + public function testAskHiddenResponse() + { + if ('\\' === DIRECTORY_SEPARATOR) { + $this->markTestSkipped('This test is not supported on Windows'); + } + + $dialog = new QuestionHelper(); + $dialog->setInputStream($this->getInputStream("8AM\n")); + + $question = new Question('What time is it?'); + $question->setHidden(true); + + $this->assertEquals('8AM', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + } + + /** + * @dataProvider getAskConfirmationData + */ + public function testAskConfirmation($question, $expected, $default = true) + { + $dialog = new QuestionHelper(); + + $dialog->setInputStream($this->getInputStream($question."\n")); + $question = new ConfirmationQuestion('Do you like French fries?', $default); + $this->assertEquals($expected, $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question), 'confirmation question should '.($expected ? 'pass' : 'cancel')); + } + + public function getAskConfirmationData() + { + return array( + array('', true), + array('', false, false), + array('y', true), + array('yes', true), + array('n', false), + array('no', false), + ); + } + + public function testAskConfirmationWithCustomTrueAnswer() + { + $dialog = new QuestionHelper(); + + $dialog->setInputStream($this->getInputStream("j\ny\n")); + $question = new ConfirmationQuestion('Do you like French fries?', false, '/^(j|y)/i'); + $this->assertTrue($dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $question = new ConfirmationQuestion('Do you like French fries?', false, '/^(j|y)/i'); + $this->assertTrue($dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + } + + public function testAskAndValidate() + { + $dialog = new QuestionHelper(); + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $error = 'This is not a color!'; + $validator = function ($color) use ($error) { + if (!in_array($color, array('white', 'black'))) { + throw new \InvalidArgumentException($error); + } + + return $color; + }; + + $question = new Question('What color was the white horse of Henry IV?', 'white'); + $question->setValidator($validator); + $question->setMaxAttempts(2); + + $dialog->setInputStream($this->getInputStream("\nblack\n")); + $this->assertEquals('white', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('black', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + + $dialog->setInputStream($this->getInputStream("green\nyellow\norange\n")); + try { + $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question); + $this->fail(); + } catch (\InvalidArgumentException $e) { + $this->assertEquals($error, $e->getMessage()); + } + } + + /** + * @dataProvider simpleAnswerProvider + */ + public function testSelectChoiceFromSimpleChoices($providedAnswer, $expectedValue) + { + $possibleChoices = array( + 'My environment 1', + 'My environment 2', + 'My environment 3', + ); + + $dialog = new QuestionHelper(); + $dialog->setInputStream($this->getInputStream($providedAnswer."\n")); + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices); + $question->setMaxAttempts(1); + $answer = $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question); + + $this->assertSame($expectedValue, $answer); + } + + public function simpleAnswerProvider() + { + return array( + array(0, 'My environment 1'), + array(1, 'My environment 2'), + array(2, 'My environment 3'), + array('My environment 1', 'My environment 1'), + array('My environment 2', 'My environment 2'), + array('My environment 3', 'My environment 3'), + ); + } + + /** + * @dataProvider mixedKeysChoiceListAnswerProvider + */ + public function testChoiceFromChoicelistWithMixedKeys($providedAnswer, $expectedValue) + { + $possibleChoices = array( + '0' => 'No environment', + '1' => 'My environment 1', + 'env_2' => 'My environment 2', + 3 => 'My environment 3', + ); + + $dialog = new QuestionHelper(); + $dialog->setInputStream($this->getInputStream($providedAnswer."\n")); + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices); + $question->setMaxAttempts(1); + $answer = $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question); + + $this->assertSame($expectedValue, $answer); + } + + public function mixedKeysChoiceListAnswerProvider() + { + return array( + array('0', '0'), + array('No environment', '0'), + array('1', '1'), + array('env_2', 'env_2'), + array(3, '3'), + array('My environment 1', '1'), + ); + } + + /** + * @dataProvider answerProvider + */ + public function testSelectChoiceFromChoiceList($providedAnswer, $expectedValue) + { + $possibleChoices = array( + 'env_1' => 'My environment 1', + 'env_2' => 'My environment', + 'env_3' => 'My environment', + ); + + $dialog = new QuestionHelper(); + $dialog->setInputStream($this->getInputStream($providedAnswer."\n")); + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices); + $question->setMaxAttempts(1); + $answer = $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question); + + $this->assertSame($expectedValue, $answer); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The provided answer is ambiguous. Value should be one of env_2 or env_3. + */ + public function testAmbiguousChoiceFromChoicelist() + { + $possibleChoices = array( + 'env_1' => 'My first environment', + 'env_2' => 'My environment', + 'env_3' => 'My environment', + ); + + $dialog = new QuestionHelper(); + $dialog->setInputStream($this->getInputStream("My environment\n")); + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices); + $question->setMaxAttempts(1); + + $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question); + } + + public function answerProvider() + { + return array( + array('env_1', 'env_1'), + array('env_2', 'env_2'), + array('env_3', 'env_3'), + array('My environment 1', 'env_1'), + ); + } + + public function testNoInteraction() + { + $dialog = new QuestionHelper(); + $question = new Question('Do you have a job?', 'not yet'); + $this->assertEquals('not yet', $dialog->ask($this->createInputInterfaceMock(false), $this->createOutputInterface(), $question)); + } + + /** + * @requires function mb_strwidth + */ + public function testChoiceOutputFormattingQuestionForUtf8Keys() + { + $question = 'Lorem ipsum?'; + $possibleChoices = array( + 'foo' => 'foo', + 'żółw' => 'bar', + 'łabądź' => 'baz', + ); + $outputShown = array( + $question, + ' [foo ] foo', + ' [żółw ] bar', + ' [łabądź] baz', + ); + $output = $this->getMock('\Symfony\Component\Console\Output\OutputInterface'); + $output->method('getFormatter')->willReturn(new OutputFormatter()); + + $dialog = new QuestionHelper(); + $dialog->setInputStream($this->getInputStream("\n")); + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $output->expects($this->once())->method('writeln')->with($this->equalTo($outputShown)); + + $question = new ChoiceQuestion($question, $possibleChoices, 'foo'); + $dialog->ask($this->createInputInterfaceMock(), $output, $question); + } + + protected function getInputStream($input) + { + $stream = fopen('php://memory', 'r+', false); + fwrite($stream, $input); + rewind($stream); + + return $stream; + } + + protected function createOutputInterface() + { + return new StreamOutput(fopen('php://memory', 'r+', false)); + } + + protected function createInputInterfaceMock($interactive = true) + { + $mock = $this->getMock('Symfony\Component\Console\Input\InputInterface'); + $mock->expects($this->any()) + ->method('isInteractive') + ->will($this->returnValue($interactive)); + + return $mock; + } + + private function hasSttyAvailable() + { + exec('stty 2>&1', $output, $exitcode); + + return $exitcode === 0; + } +} diff --git a/vendor/symfony/console/Tests/Helper/TableStyleTest.php b/vendor/symfony/console/Tests/Helper/TableStyleTest.php new file mode 100644 index 0000000000..587d8414e6 --- /dev/null +++ b/vendor/symfony/console/Tests/Helper/TableStyleTest.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\TableStyle; + +class TableStyleTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Invalid padding type. Expected one of (STR_PAD_LEFT, STR_PAD_RIGHT, STR_PAD_BOTH). + */ + public function testSetPadTypeWithInvalidType() + { + $style = new TableStyle(); + $style->setPadType('TEST'); + } +} diff --git a/vendor/symfony/console/Tests/Helper/TableTest.php b/vendor/symfony/console/Tests/Helper/TableTest.php new file mode 100644 index 0000000000..9ecb381a80 --- /dev/null +++ b/vendor/symfony/console/Tests/Helper/TableTest.php @@ -0,0 +1,645 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Helper\TableStyle; +use Symfony\Component\Console\Helper\TableSeparator; +use Symfony\Component\Console\Helper\TableCell; +use Symfony\Component\Console\Output\StreamOutput; + +class TableTest extends \PHPUnit_Framework_TestCase +{ + protected $stream; + + protected function setUp() + { + $this->stream = fopen('php://memory', 'r+'); + } + + protected function tearDown() + { + fclose($this->stream); + $this->stream = null; + } + + /** + * @dataProvider testRenderProvider + */ + public function testRender($headers, $rows, $style, $expected) + { + $table = new Table($output = $this->getOutputStream()); + $table + ->setHeaders($headers) + ->setRows($rows) + ->setStyle($style) + ; + $table->render(); + + $this->assertEquals($expected, $this->getOutputContent($output)); + } + + /** + * @dataProvider testRenderProvider + */ + public function testRenderAddRows($headers, $rows, $style, $expected) + { + $table = new Table($output = $this->getOutputStream()); + $table + ->setHeaders($headers) + ->addRows($rows) + ->setStyle($style) + ; + $table->render(); + + $this->assertEquals($expected, $this->getOutputContent($output)); + } + + /** + * @dataProvider testRenderProvider + */ + public function testRenderAddRowsOneByOne($headers, $rows, $style, $expected) + { + $table = new Table($output = $this->getOutputStream()); + $table + ->setHeaders($headers) + ->setStyle($style) + ; + foreach ($rows as $row) { + $table->addRow($row); + } + $table->render(); + + $this->assertEquals($expected, $this->getOutputContent($output)); + } + + public function testRenderProvider() + { + $books = array( + array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'), + array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'), + array('960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'), + array('80-902734-1-6', 'And Then There Were None', 'Agatha Christie'), + ); + + return array( + array( + array('ISBN', 'Title', 'Author'), + $books, + 'default', +<<
array( + array('ISBN', 'Title', 'Author'), + array( + array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'), + array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'), + ), + 'default', +<<
array( + array('ISBN', 'Title', 'Author'), + array( + array('99921-58-10-700', 'Divine Com', 'Dante Alighieri'), + array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'), + ), + 'default', +<<
99921-58-10-700 | Divine Com | Dante Alighieri | +| 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | ++----------------------------------+----------------------+-----------------+ + +TABLE + ), + 'Cell with colspan' => array( + array('ISBN', 'Title', 'Author'), + array( + array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'), + new TableSeparator(), + array(new TableCell('Divine Comedy(Dante Alighieri)', array('colspan' => 3))), + new TableSeparator(), + array( + new TableCell('Arduino: A Quick-Start Guide', array('colspan' => 2)), + 'Mark Schmidt', + ), + new TableSeparator(), + array( + '9971-5-0210-0', + new TableCell("A Tale of \nTwo Cities", array('colspan' => 2)), + ), + new TableSeparator(), + array( + new TableCell('Cupiditate dicta atque porro, tempora exercitationem modi animi nulla nemo vel nihil!', array('colspan' => 3)), + ), + ), + 'default', +<<
array( + array('ISBN', 'Title', 'Author'), + array( + array( + new TableCell('9971-5-0210-0', array('rowspan' => 3)), + 'Divine Comedy', + 'Dante Alighieri', + ), + array('A Tale of Two Cities', 'Charles Dickens'), + array("The Lord of \nthe Rings", "J. R. \nR. Tolkien"), + new TableSeparator(), + array('80-902734-1-6', new TableCell("And Then \nThere \nWere None", array('rowspan' => 3)), 'Agatha Christie'), + array('80-902734-1-7', 'Test'), + ), + 'default', +<<
array( + array('ISBN', 'Title', 'Author'), + array( + array( + new TableCell('9971-5-0210-0', array('rowspan' => 2, 'colspan' => 2)), + 'Dante Alighieri', + ), + array('Charles Dickens'), + new TableSeparator(), + array( + 'Dante Alighieri', + new TableCell('9971-5-0210-0', array('rowspan' => 3, 'colspan' => 2)), + ), + array('J. R. R. Tolkien'), + array('J. R. R'), + ), + 'default', +<<
array( + array('ISBN', 'Title', 'Author'), + array( + array( + new TableCell("9971\n-5-\n021\n0-0", array('rowspan' => 2, 'colspan' => 2)), + 'Dante Alighieri', + ), + array('Charles Dickens'), + new TableSeparator(), + array( + 'Dante Alighieri', + new TableCell("9971\n-5-\n021\n0-0", array('rowspan' => 2, 'colspan' => 2)), + ), + array('Charles Dickens'), + new TableSeparator(), + array( + new TableCell("9971\n-5-\n021\n0-0", array('rowspan' => 2, 'colspan' => 2)), + new TableCell("Dante \nAlighieri", array('rowspan' => 2, 'colspan' => 1)), + ), + ), + 'default', +<<
array( + array('ISBN', 'Title', 'Author'), + array( + array( + new TableCell("9971\n-5-\n021\n0-0", array('rowspan' => 2, 'colspan' => 2)), + 'Dante Alighieri', + ), + array('Charles Dickens'), + array( + 'Dante Alighieri', + new TableCell("9971\n-5-\n021\n0-0", array('rowspan' => 2, 'colspan' => 2)), + ), + array('Charles Dickens'), + ), + 'default', +<<
array( + array('ISBN', 'Author'), + array( + array( + new TableCell('9971-5-0210-0', array('rowspan' => 3, 'colspan' => 1)), + 'Dante Alighieri', + ), + array(new TableSeparator()), + array('Charles Dickens'), + ), + 'default', +<<
array( + array( + array(new TableCell('Main title', array('colspan' => 3))), + array('ISBN', 'Title', 'Author'), + ), + array(), + 'default', +<<
array( + array(), + array( + array( + new TableCell('1', array('colspan' => 3)), + new TableCell('2', array('colspan' => 2)), + new TableCell('3', array('colspan' => 2)), + new TableCell('4', array('colspan' => 2)), + ), + ), + 'default', +<<
getOutputStream()); + $table + ->setHeaders(array('■■')) + ->setRows(array(array(1234))) + ->setStyle('default') + ; + $table->render(); + + $expected = +<<
assertEquals($expected, $this->getOutputContent($output)); + } + + public function testStyle() + { + $style = new TableStyle(); + $style + ->setHorizontalBorderChar('.') + ->setVerticalBorderChar('.') + ->setCrossingChar('.') + ; + + Table::setStyleDefinition('dotfull', $style); + $table = new Table($output = $this->getOutputStream()); + $table + ->setHeaders(array('Foo')) + ->setRows(array(array('Bar'))) + ->setStyle('dotfull'); + $table->render(); + + $expected = +<<
assertEquals($expected, $this->getOutputContent($output)); + } + + public function testRowSeparator() + { + $table = new Table($output = $this->getOutputStream()); + $table + ->setHeaders(array('Foo')) + ->setRows(array( + array('Bar1'), + new TableSeparator(), + array('Bar2'), + new TableSeparator(), + array('Bar3'), + )); + $table->render(); + + $expected = +<<
assertEquals($expected, $this->getOutputContent($output)); + + $this->assertEquals($table, $table->addRow(new TableSeparator()), 'fluent interface on addRow() with a single TableSeparator() works'); + } + + public function testRenderMultiCalls() + { + $table = new Table($output = $this->getOutputStream()); + $table->setRows(array( + array(new TableCell('foo', array('colspan' => 2))), + )); + $table->render(); + $table->render(); + $table->render(); + + $expected = +<<
assertEquals($expected, $this->getOutputContent($output)); + } + + public function testColumnStyle() + { + $table = new Table($output = $this->getOutputStream()); + $table + ->setHeaders(array('ISBN', 'Title', 'Author', 'Price')) + ->setRows(array( + array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri', '9.95'), + array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens', '139.25'), + )); + + $style = new TableStyle(); + $style->setPadType(STR_PAD_LEFT); + $table->setColumnStyle(3, $style); + + $table->render(); + + $expected = + <<
assertEquals($expected, $this->getOutputContent($output)); + } + + protected function getOutputStream() + { + return new StreamOutput($this->stream, StreamOutput::VERBOSITY_NORMAL, false); + } + + protected function getOutputContent(StreamOutput $output) + { + rewind($output->getStream()); + + return str_replace(PHP_EOL, "\n", stream_get_contents($output->getStream())); + } +} diff --git a/vendor/symfony/console/Tests/Input/ArgvInputTest.php b/vendor/symfony/console/Tests/Input/ArgvInputTest.php new file mode 100644 index 0000000000..d2c540e6fe --- /dev/null +++ b/vendor/symfony/console/Tests/Input/ArgvInputTest.php @@ -0,0 +1,317 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Input; + +use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; + +class ArgvInputTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $_SERVER['argv'] = array('cli.php', 'foo'); + $input = new ArgvInput(); + $r = new \ReflectionObject($input); + $p = $r->getProperty('tokens'); + $p->setAccessible(true); + + $this->assertEquals(array('foo'), $p->getValue($input), '__construct() automatically get its input from the argv server variable'); + } + + public function testParseArguments() + { + $input = new ArgvInput(array('cli.php', 'foo')); + $input->bind(new InputDefinition(array(new InputArgument('name')))); + $this->assertEquals(array('name' => 'foo'), $input->getArguments(), '->parse() parses required arguments'); + + $input->bind(new InputDefinition(array(new InputArgument('name')))); + $this->assertEquals(array('name' => 'foo'), $input->getArguments(), '->parse() is stateless'); + } + + /** + * @dataProvider provideOptions + */ + public function testParseOptions($input, $options, $expectedOptions, $message) + { + $input = new ArgvInput($input); + $input->bind(new InputDefinition($options)); + + $this->assertEquals($expectedOptions, $input->getOptions(), $message); + } + + public function provideOptions() + { + return array( + array( + array('cli.php', '--foo'), + array(new InputOption('foo')), + array('foo' => true), + '->parse() parses long options without a value', + ), + array( + array('cli.php', '--foo=bar'), + array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED)), + array('foo' => 'bar'), + '->parse() parses long options with a required value (with a = separator)', + ), + array( + array('cli.php', '--foo', 'bar'), + array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED)), + array('foo' => 'bar'), + '->parse() parses long options with a required value (with a space separator)', + ), + array( + array('cli.php', '-f'), + array(new InputOption('foo', 'f')), + array('foo' => true), + '->parse() parses short options without a value', + ), + array( + array('cli.php', '-fbar'), + array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED)), + array('foo' => 'bar'), + '->parse() parses short options with a required value (with no separator)', + ), + array( + array('cli.php', '-f', 'bar'), + array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED)), + array('foo' => 'bar'), + '->parse() parses short options with a required value (with a space separator)', + ), + array( + array('cli.php', '-f', ''), + array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)), + array('foo' => ''), + '->parse() parses short options with an optional empty value', + ), + array( + array('cli.php', '-f', '', 'foo'), + array(new InputArgument('name'), new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)), + array('foo' => ''), + '->parse() parses short options with an optional empty value followed by an argument', + ), + array( + array('cli.php', '-f', '', '-b'), + array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputOption('bar', 'b')), + array('foo' => '', 'bar' => true), + '->parse() parses short options with an optional empty value followed by an option', + ), + array( + array('cli.php', '-f', '-b', 'foo'), + array(new InputArgument('name'), new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputOption('bar', 'b')), + array('foo' => null, 'bar' => true), + '->parse() parses short options with an optional value which is not present', + ), + array( + array('cli.php', '-fb'), + array(new InputOption('foo', 'f'), new InputOption('bar', 'b')), + array('foo' => true, 'bar' => true), + '->parse() parses short options when they are aggregated as a single one', + ), + array( + array('cli.php', '-fb', 'bar'), + array(new InputOption('foo', 'f'), new InputOption('bar', 'b', InputOption::VALUE_REQUIRED)), + array('foo' => true, 'bar' => 'bar'), + '->parse() parses short options when they are aggregated as a single one and the last one has a required value', + ), + array( + array('cli.php', '-fb', 'bar'), + array(new InputOption('foo', 'f'), new InputOption('bar', 'b', InputOption::VALUE_OPTIONAL)), + array('foo' => true, 'bar' => 'bar'), + '->parse() parses short options when they are aggregated as a single one and the last one has an optional value', + ), + array( + array('cli.php', '-fbbar'), + array(new InputOption('foo', 'f'), new InputOption('bar', 'b', InputOption::VALUE_OPTIONAL)), + array('foo' => true, 'bar' => 'bar'), + '->parse() parses short options when they are aggregated as a single one and the last one has an optional value with no separator', + ), + array( + array('cli.php', '-fbbar'), + array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputOption('bar', 'b', InputOption::VALUE_OPTIONAL)), + array('foo' => 'bbar', 'bar' => null), + '->parse() parses short options when they are aggregated as a single one and one of them takes a value', + ), + ); + } + + /** + * @dataProvider provideInvalidInput + */ + public function testInvalidInput($argv, $definition, $expectedExceptionMessage) + { + $this->setExpectedException('RuntimeException', $expectedExceptionMessage); + + $input = new ArgvInput($argv); + $input->bind($definition); + } + + public function provideInvalidInput() + { + return array( + array( + array('cli.php', '--foo'), + new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED))), + 'The "--foo" option requires a value.', + ), + array( + array('cli.php', '-f'), + new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED))), + 'The "--foo" option requires a value.', + ), + array( + array('cli.php', '-ffoo'), + new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_NONE))), + 'The "-o" option does not exist.', + ), + array( + array('cli.php', '--foo=bar'), + new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_NONE))), + 'The "--foo" option does not accept a value.', + ), + array( + array('cli.php', 'foo', 'bar'), + new InputDefinition(), + 'Too many arguments.', + ), + array( + array('cli.php', '--foo'), + new InputDefinition(), + 'The "--foo" option does not exist.', + ), + array( + array('cli.php', '-f'), + new InputDefinition(), + 'The "-f" option does not exist.', + ), + array( + array('cli.php', '-1'), + new InputDefinition(array(new InputArgument('number'))), + 'The "-1" option does not exist.', + ), + ); + } + + public function testParseArrayArgument() + { + $input = new ArgvInput(array('cli.php', 'foo', 'bar', 'baz', 'bat')); + $input->bind(new InputDefinition(array(new InputArgument('name', InputArgument::IS_ARRAY)))); + + $this->assertEquals(array('name' => array('foo', 'bar', 'baz', 'bat')), $input->getArguments(), '->parse() parses array arguments'); + } + + public function testParseArrayOption() + { + $input = new ArgvInput(array('cli.php', '--name=foo', '--name=bar', '--name=baz')); + $input->bind(new InputDefinition(array(new InputOption('name', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY)))); + + $this->assertEquals(array('name' => array('foo', 'bar', 'baz')), $input->getOptions(), '->parse() parses array options ("--option=value" syntax)'); + + $input = new ArgvInput(array('cli.php', '--name', 'foo', '--name', 'bar', '--name', 'baz')); + $input->bind(new InputDefinition(array(new InputOption('name', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY)))); + $this->assertEquals(array('name' => array('foo', 'bar', 'baz')), $input->getOptions(), '->parse() parses array options ("--option value" syntax)'); + + $input = new ArgvInput(array('cli.php', '--name=foo', '--name=bar', '--name=')); + $input->bind(new InputDefinition(array(new InputOption('name', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY)))); + $this->assertSame(array('name' => array('foo', 'bar', null)), $input->getOptions(), '->parse() parses empty array options as null ("--option=value" syntax)'); + + $input = new ArgvInput(array('cli.php', '--name', 'foo', '--name', 'bar', '--name', '--anotherOption')); + $input->bind(new InputDefinition(array( + new InputOption('name', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY), + new InputOption('anotherOption', null, InputOption::VALUE_NONE), + ))); + $this->assertSame(array('name' => array('foo', 'bar', null), 'anotherOption' => true), $input->getOptions(), '->parse() parses empty array options as null ("--option value" syntax)'); + } + + public function testParseNegativeNumberAfterDoubleDash() + { + $input = new ArgvInput(array('cli.php', '--', '-1')); + $input->bind(new InputDefinition(array(new InputArgument('number')))); + $this->assertEquals(array('number' => '-1'), $input->getArguments(), '->parse() parses arguments with leading dashes as arguments after having encountered a double-dash sequence'); + + $input = new ArgvInput(array('cli.php', '-f', 'bar', '--', '-1')); + $input->bind(new InputDefinition(array(new InputArgument('number'), new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)))); + $this->assertEquals(array('foo' => 'bar'), $input->getOptions(), '->parse() parses arguments with leading dashes as options before having encountered a double-dash sequence'); + $this->assertEquals(array('number' => '-1'), $input->getArguments(), '->parse() parses arguments with leading dashes as arguments after having encountered a double-dash sequence'); + } + + public function testParseEmptyStringArgument() + { + $input = new ArgvInput(array('cli.php', '-f', 'bar', '')); + $input->bind(new InputDefinition(array(new InputArgument('empty'), new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)))); + + $this->assertEquals(array('empty' => ''), $input->getArguments(), '->parse() parses empty string arguments'); + } + + public function testGetFirstArgument() + { + $input = new ArgvInput(array('cli.php', '-fbbar')); + $this->assertNull($input->getFirstArgument(), '->getFirstArgument() returns null when there is no arguments'); + + $input = new ArgvInput(array('cli.php', '-fbbar', 'foo')); + $this->assertEquals('foo', $input->getFirstArgument(), '->getFirstArgument() returns the first argument from the raw input'); + } + + public function testHasParameterOption() + { + $input = new ArgvInput(array('cli.php', '-f', 'foo')); + $this->assertTrue($input->hasParameterOption('-f'), '->hasParameterOption() returns true if the given short option is in the raw input'); + + $input = new ArgvInput(array('cli.php', '--foo', 'foo')); + $this->assertTrue($input->hasParameterOption('--foo'), '->hasParameterOption() returns true if the given short option is in the raw input'); + + $input = new ArgvInput(array('cli.php', 'foo')); + $this->assertFalse($input->hasParameterOption('--foo'), '->hasParameterOption() returns false if the given short option is not in the raw input'); + + $input = new ArgvInput(array('cli.php', '--foo=bar')); + $this->assertTrue($input->hasParameterOption('--foo'), '->hasParameterOption() returns true if the given option with provided value is in the raw input'); + } + + public function testToString() + { + $input = new ArgvInput(array('cli.php', '-f', 'foo')); + $this->assertEquals('-f foo', (string) $input); + + $input = new ArgvInput(array('cli.php', '-f', '--bar=foo', 'a b c d', "A\nB'C")); + $this->assertEquals('-f --bar=foo '.escapeshellarg('a b c d').' '.escapeshellarg("A\nB'C"), (string) $input); + } + + /** + * @dataProvider provideGetParameterOptionValues + */ + public function testGetParameterOptionEqualSign($argv, $key, $expected) + { + $input = new ArgvInput($argv); + $this->assertEquals($expected, $input->getParameterOption($key), '->getParameterOption() returns the expected value'); + } + + public function provideGetParameterOptionValues() + { + return array( + array(array('app/console', 'foo:bar', '-e', 'dev'), '-e', 'dev'), + array(array('app/console', 'foo:bar', '--env=dev'), '--env', 'dev'), + array(array('app/console', 'foo:bar', '-e', 'dev'), array('-e', '--env'), 'dev'), + array(array('app/console', 'foo:bar', '--env=dev'), array('-e', '--env'), 'dev'), + array(array('app/console', 'foo:bar', '--env=dev', '--en=1'), array('--en'), '1'), + array(array('app/console', 'foo:bar', '--env=dev', '', '--en=1'), array('--en'), '1'), + ); + } + + public function testParseSingleDashAsArgument() + { + $input = new ArgvInput(array('cli.php', '-')); + $input->bind(new InputDefinition(array(new InputArgument('file')))); + $this->assertEquals(array('file' => '-'), $input->getArguments(), '->parse() parses single dash as an argument'); + } +} diff --git a/vendor/symfony/console/Tests/Input/ArrayInputTest.php b/vendor/symfony/console/Tests/Input/ArrayInputTest.php new file mode 100644 index 0000000000..cc89083c6b --- /dev/null +++ b/vendor/symfony/console/Tests/Input/ArrayInputTest.php @@ -0,0 +1,138 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Input; + +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; + +class ArrayInputTest extends \PHPUnit_Framework_TestCase +{ + public function testGetFirstArgument() + { + $input = new ArrayInput(array()); + $this->assertNull($input->getFirstArgument(), '->getFirstArgument() returns null if no argument were passed'); + $input = new ArrayInput(array('name' => 'Fabien')); + $this->assertEquals('Fabien', $input->getFirstArgument(), '->getFirstArgument() returns the first passed argument'); + $input = new ArrayInput(array('--foo' => 'bar', 'name' => 'Fabien')); + $this->assertEquals('Fabien', $input->getFirstArgument(), '->getFirstArgument() returns the first passed argument'); + } + + public function testHasParameterOption() + { + $input = new ArrayInput(array('name' => 'Fabien', '--foo' => 'bar')); + $this->assertTrue($input->hasParameterOption('--foo'), '->hasParameterOption() returns true if an option is present in the passed parameters'); + $this->assertFalse($input->hasParameterOption('--bar'), '->hasParameterOption() returns false if an option is not present in the passed parameters'); + + $input = new ArrayInput(array('--foo')); + $this->assertTrue($input->hasParameterOption('--foo'), '->hasParameterOption() returns true if an option is present in the passed parameters'); + } + + public function testGetParameterOption() + { + $input = new ArrayInput(array('name' => 'Fabien', '--foo' => 'bar')); + $this->assertEquals('bar', $input->getParameterOption('--foo'), '->getParameterOption() returns the option of specified name'); + + $input = new ArrayInput(array('Fabien', '--foo' => 'bar')); + $this->assertEquals('bar', $input->getParameterOption('--foo'), '->getParameterOption() returns the option of specified name'); + } + + public function testParseArguments() + { + $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name')))); + + $this->assertEquals(array('name' => 'foo'), $input->getArguments(), '->parse() parses required arguments'); + } + + /** + * @dataProvider provideOptions + */ + public function testParseOptions($input, $options, $expectedOptions, $message) + { + $input = new ArrayInput($input, new InputDefinition($options)); + + $this->assertEquals($expectedOptions, $input->getOptions(), $message); + } + + public function provideOptions() + { + return array( + array( + array('--foo' => 'bar'), + array(new InputOption('foo')), + array('foo' => 'bar'), + '->parse() parses long options', + ), + array( + array('--foo' => 'bar'), + array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, '', 'default')), + array('foo' => 'bar'), + '->parse() parses long options with a default value', + ), + array( + array('--foo' => null), + array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, '', 'default')), + array('foo' => 'default'), + '->parse() parses long options with a default value', + ), + array( + array('-f' => 'bar'), + array(new InputOption('foo', 'f')), + array('foo' => 'bar'), + '->parse() parses short options', + ), + ); + } + + /** + * @dataProvider provideInvalidInput + */ + public function testParseInvalidInput($parameters, $definition, $expectedExceptionMessage) + { + $this->setExpectedException('InvalidArgumentException', $expectedExceptionMessage); + + new ArrayInput($parameters, $definition); + } + + public function provideInvalidInput() + { + return array( + array( + array('foo' => 'foo'), + new InputDefinition(array(new InputArgument('name'))), + 'The "foo" argument does not exist.', + ), + array( + array('--foo' => null), + new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED))), + 'The "--foo" option requires a value.', + ), + array( + array('--foo' => 'foo'), + new InputDefinition(), + 'The "--foo" option does not exist.', + ), + array( + array('-o' => 'foo'), + new InputDefinition(), + 'The "-o" option does not exist.', + ), + ); + } + + public function testToString() + { + $input = new ArrayInput(array('-f' => null, '-b' => 'bar', '--foo' => 'b a z', '--lala' => null, 'test' => 'Foo', 'test2' => "A\nB'C")); + $this->assertEquals('-f -b=bar --foo='.escapeshellarg('b a z').' --lala Foo '.escapeshellarg("A\nB'C"), (string) $input); + } +} diff --git a/vendor/symfony/console/Tests/Input/InputArgumentTest.php b/vendor/symfony/console/Tests/Input/InputArgumentTest.php new file mode 100644 index 0000000000..cfb37cd410 --- /dev/null +++ b/vendor/symfony/console/Tests/Input/InputArgumentTest.php @@ -0,0 +1,111 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Input; + +use Symfony\Component\Console\Input\InputArgument; + +class InputArgumentTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $argument = new InputArgument('foo'); + $this->assertEquals('foo', $argument->getName(), '__construct() takes a name as its first argument'); + } + + public function testModes() + { + $argument = new InputArgument('foo'); + $this->assertFalse($argument->isRequired(), '__construct() gives a "InputArgument::OPTIONAL" mode by default'); + + $argument = new InputArgument('foo', null); + $this->assertFalse($argument->isRequired(), '__construct() can take "InputArgument::OPTIONAL" as its mode'); + + $argument = new InputArgument('foo', InputArgument::OPTIONAL); + $this->assertFalse($argument->isRequired(), '__construct() can take "InputArgument::OPTIONAL" as its mode'); + + $argument = new InputArgument('foo', InputArgument::REQUIRED); + $this->assertTrue($argument->isRequired(), '__construct() can take "InputArgument::REQUIRED" as its mode'); + } + + /** + * @dataProvider provideInvalidModes + */ + public function testInvalidModes($mode) + { + $this->setExpectedException('InvalidArgumentException', sprintf('Argument mode "%s" is not valid.', $mode)); + + new InputArgument('foo', $mode); + } + + public function provideInvalidModes() + { + return array( + array('ANOTHER_ONE'), + array(-1), + ); + } + + public function testIsArray() + { + $argument = new InputArgument('foo', InputArgument::IS_ARRAY); + $this->assertTrue($argument->isArray(), '->isArray() returns true if the argument can be an array'); + $argument = new InputArgument('foo', InputArgument::OPTIONAL | InputArgument::IS_ARRAY); + $this->assertTrue($argument->isArray(), '->isArray() returns true if the argument can be an array'); + $argument = new InputArgument('foo', InputArgument::OPTIONAL); + $this->assertFalse($argument->isArray(), '->isArray() returns false if the argument can not be an array'); + } + + public function testGetDescription() + { + $argument = new InputArgument('foo', null, 'Some description'); + $this->assertEquals('Some description', $argument->getDescription(), '->getDescription() return the message description'); + } + + public function testGetDefault() + { + $argument = new InputArgument('foo', InputArgument::OPTIONAL, '', 'default'); + $this->assertEquals('default', $argument->getDefault(), '->getDefault() return the default value'); + } + + public function testSetDefault() + { + $argument = new InputArgument('foo', InputArgument::OPTIONAL, '', 'default'); + $argument->setDefault(null); + $this->assertNull($argument->getDefault(), '->setDefault() can reset the default value by passing null'); + $argument->setDefault('another'); + $this->assertEquals('another', $argument->getDefault(), '->setDefault() changes the default value'); + + $argument = new InputArgument('foo', InputArgument::OPTIONAL | InputArgument::IS_ARRAY); + $argument->setDefault(array(1, 2)); + $this->assertEquals(array(1, 2), $argument->getDefault(), '->setDefault() changes the default value'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Cannot set a default value except for InputArgument::OPTIONAL mode. + */ + public function testSetDefaultWithRequiredArgument() + { + $argument = new InputArgument('foo', InputArgument::REQUIRED); + $argument->setDefault('default'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage A default value for an array argument must be an array. + */ + public function testSetDefaultWithArrayArgument() + { + $argument = new InputArgument('foo', InputArgument::IS_ARRAY); + $argument->setDefault('default'); + } +} diff --git a/vendor/symfony/console/Tests/Input/InputDefinitionTest.php b/vendor/symfony/console/Tests/Input/InputDefinitionTest.php new file mode 100644 index 0000000000..7e0a24258f --- /dev/null +++ b/vendor/symfony/console/Tests/Input/InputDefinitionTest.php @@ -0,0 +1,437 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Input; + +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; + +class InputDefinitionTest extends \PHPUnit_Framework_TestCase +{ + protected static $fixtures; + + protected $foo, $bar, $foo1, $foo2; + + public static function setUpBeforeClass() + { + self::$fixtures = __DIR__.'/../Fixtures/'; + } + + public function testConstructorArguments() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $this->assertEquals(array(), $definition->getArguments(), '__construct() creates a new InputDefinition object'); + + $definition = new InputDefinition(array($this->foo, $this->bar)); + $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getArguments(), '__construct() takes an array of InputArgument objects as its first argument'); + } + + public function testConstructorOptions() + { + $this->initializeOptions(); + + $definition = new InputDefinition(); + $this->assertEquals(array(), $definition->getOptions(), '__construct() creates a new InputDefinition object'); + + $definition = new InputDefinition(array($this->foo, $this->bar)); + $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getOptions(), '__construct() takes an array of InputOption objects as its first argument'); + } + + public function testSetArguments() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->setArguments(array($this->foo)); + $this->assertEquals(array('foo' => $this->foo), $definition->getArguments(), '->setArguments() sets the array of InputArgument objects'); + $definition->setArguments(array($this->bar)); + + $this->assertEquals(array('bar' => $this->bar), $definition->getArguments(), '->setArguments() clears all InputArgument objects'); + } + + public function testAddArguments() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArguments(array($this->foo)); + $this->assertEquals(array('foo' => $this->foo), $definition->getArguments(), '->addArguments() adds an array of InputArgument objects'); + $definition->addArguments(array($this->bar)); + $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getArguments(), '->addArguments() does not clear existing InputArgument objects'); + } + + public function testAddArgument() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArgument($this->foo); + $this->assertEquals(array('foo' => $this->foo), $definition->getArguments(), '->addArgument() adds a InputArgument object'); + $definition->addArgument($this->bar); + $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getArguments(), '->addArgument() adds a InputArgument object'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage An argument with name "foo" already exists. + */ + public function testArgumentsMustHaveDifferentNames() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArgument($this->foo); + $definition->addArgument($this->foo1); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Cannot add an argument after an array argument. + */ + public function testArrayArgumentHasToBeLast() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArgument(new InputArgument('fooarray', InputArgument::IS_ARRAY)); + $definition->addArgument(new InputArgument('anotherbar')); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Cannot add a required argument after an optional one. + */ + public function testRequiredArgumentCannotFollowAnOptionalOne() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArgument($this->foo); + $definition->addArgument($this->foo2); + } + + public function testGetArgument() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArguments(array($this->foo)); + $this->assertEquals($this->foo, $definition->getArgument('foo'), '->getArgument() returns a InputArgument by its name'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "bar" argument does not exist. + */ + public function testGetInvalidArgument() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArguments(array($this->foo)); + $definition->getArgument('bar'); + } + + public function testHasArgument() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArguments(array($this->foo)); + + $this->assertTrue($definition->hasArgument('foo'), '->hasArgument() returns true if a InputArgument exists for the given name'); + $this->assertFalse($definition->hasArgument('bar'), '->hasArgument() returns false if a InputArgument exists for the given name'); + } + + public function testGetArgumentRequiredCount() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArgument($this->foo2); + $this->assertEquals(1, $definition->getArgumentRequiredCount(), '->getArgumentRequiredCount() returns the number of required arguments'); + $definition->addArgument($this->foo); + $this->assertEquals(1, $definition->getArgumentRequiredCount(), '->getArgumentRequiredCount() returns the number of required arguments'); + } + + public function testGetArgumentCount() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArgument($this->foo2); + $this->assertEquals(1, $definition->getArgumentCount(), '->getArgumentCount() returns the number of arguments'); + $definition->addArgument($this->foo); + $this->assertEquals(2, $definition->getArgumentCount(), '->getArgumentCount() returns the number of arguments'); + } + + public function testGetArgumentDefaults() + { + $definition = new InputDefinition(array( + new InputArgument('foo1', InputArgument::OPTIONAL), + new InputArgument('foo2', InputArgument::OPTIONAL, '', 'default'), + new InputArgument('foo3', InputArgument::OPTIONAL | InputArgument::IS_ARRAY), + // new InputArgument('foo4', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, '', array(1, 2)), + )); + $this->assertEquals(array('foo1' => null, 'foo2' => 'default', 'foo3' => array()), $definition->getArgumentDefaults(), '->getArgumentDefaults() return the default values for each argument'); + + $definition = new InputDefinition(array( + new InputArgument('foo4', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, '', array(1, 2)), + )); + $this->assertEquals(array('foo4' => array(1, 2)), $definition->getArgumentDefaults(), '->getArgumentDefaults() return the default values for each argument'); + } + + public function testSetOptions() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $this->assertEquals(array('foo' => $this->foo), $definition->getOptions(), '->setOptions() sets the array of InputOption objects'); + $definition->setOptions(array($this->bar)); + $this->assertEquals(array('bar' => $this->bar), $definition->getOptions(), '->setOptions() clears all InputOption objects'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "-f" option does not exist. + */ + public function testSetOptionsClearsOptions() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $definition->setOptions(array($this->bar)); + $definition->getOptionForShortcut('f'); + } + + public function testAddOptions() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $this->assertEquals(array('foo' => $this->foo), $definition->getOptions(), '->addOptions() adds an array of InputOption objects'); + $definition->addOptions(array($this->bar)); + $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getOptions(), '->addOptions() does not clear existing InputOption objects'); + } + + public function testAddOption() + { + $this->initializeOptions(); + + $definition = new InputDefinition(); + $definition->addOption($this->foo); + $this->assertEquals(array('foo' => $this->foo), $definition->getOptions(), '->addOption() adds a InputOption object'); + $definition->addOption($this->bar); + $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getOptions(), '->addOption() adds a InputOption object'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage An option named "foo" already exists. + */ + public function testAddDuplicateOption() + { + $this->initializeOptions(); + + $definition = new InputDefinition(); + $definition->addOption($this->foo); + $definition->addOption($this->foo2); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage An option with shortcut "f" already exists. + */ + public function testAddDuplicateShortcutOption() + { + $this->initializeOptions(); + + $definition = new InputDefinition(); + $definition->addOption($this->foo); + $definition->addOption($this->foo1); + } + + public function testGetOption() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $this->assertEquals($this->foo, $definition->getOption('foo'), '->getOption() returns a InputOption by its name'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "--bar" option does not exist. + */ + public function testGetInvalidOption() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $definition->getOption('bar'); + } + + public function testHasOption() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $this->assertTrue($definition->hasOption('foo'), '->hasOption() returns true if a InputOption exists for the given name'); + $this->assertFalse($definition->hasOption('bar'), '->hasOption() returns false if a InputOption exists for the given name'); + } + + public function testHasShortcut() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $this->assertTrue($definition->hasShortcut('f'), '->hasShortcut() returns true if a InputOption exists for the given shortcut'); + $this->assertFalse($definition->hasShortcut('b'), '->hasShortcut() returns false if a InputOption exists for the given shortcut'); + } + + public function testGetOptionForShortcut() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $this->assertEquals($this->foo, $definition->getOptionForShortcut('f'), '->getOptionForShortcut() returns a InputOption by its shortcut'); + } + + public function testGetOptionForMultiShortcut() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->multi)); + $this->assertEquals($this->multi, $definition->getOptionForShortcut('m'), '->getOptionForShortcut() returns a InputOption by its shortcut'); + $this->assertEquals($this->multi, $definition->getOptionForShortcut('mmm'), '->getOptionForShortcut() returns a InputOption by its shortcut'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "-l" option does not exist. + */ + public function testGetOptionForInvalidShortcut() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $definition->getOptionForShortcut('l'); + } + + public function testGetOptionDefaults() + { + $definition = new InputDefinition(array( + new InputOption('foo1', null, InputOption::VALUE_NONE), + new InputOption('foo2', null, InputOption::VALUE_REQUIRED), + new InputOption('foo3', null, InputOption::VALUE_REQUIRED, '', 'default'), + new InputOption('foo4', null, InputOption::VALUE_OPTIONAL), + new InputOption('foo5', null, InputOption::VALUE_OPTIONAL, '', 'default'), + new InputOption('foo6', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY), + new InputOption('foo7', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, '', array(1, 2)), + )); + $defaults = array( + 'foo1' => false, + 'foo2' => null, + 'foo3' => 'default', + 'foo4' => null, + 'foo5' => 'default', + 'foo6' => array(), + 'foo7' => array(1, 2), + ); + $this->assertSame($defaults, $definition->getOptionDefaults(), '->getOptionDefaults() returns the default values for all options'); + } + + /** + * @dataProvider getGetSynopsisData + */ + public function testGetSynopsis(InputDefinition $definition, $expectedSynopsis, $message = null) + { + $this->assertEquals($expectedSynopsis, $definition->getSynopsis(), $message ? '->getSynopsis() '.$message : ''); + } + + public function getGetSynopsisData() + { + return array( + array(new InputDefinition(array(new InputOption('foo'))), '[--foo]', 'puts optional options in square brackets'), + array(new InputDefinition(array(new InputOption('foo', 'f'))), '[-f|--foo]', 'separates shortcut with a pipe'), + array(new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED))), '[-f|--foo FOO]', 'uses shortcut as value placeholder'), + array(new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL))), '[-f|--foo [FOO]]', 'puts optional values in square brackets'), + + array(new InputDefinition(array(new InputArgument('foo', InputArgument::REQUIRED))), '', 'puts arguments in angle brackets'), + array(new InputDefinition(array(new InputArgument('foo'))), '[]', 'puts optional arguments in square brackets'), + array(new InputDefinition(array(new InputArgument('foo', InputArgument::IS_ARRAY))), '[]...', 'uses an ellipsis for array arguments'), + array(new InputDefinition(array(new InputArgument('foo', InputArgument::REQUIRED | InputArgument::IS_ARRAY))), ' ()...', 'uses parenthesis and ellipsis for required array arguments'), + + array(new InputDefinition(array(new InputOption('foo'), new InputArgument('foo', InputArgument::REQUIRED))), '[--foo] [--] ', 'puts [--] between options and arguments'), + ); + } + + public function testGetShortSynopsis() + { + $definition = new InputDefinition(array(new InputOption('foo'), new InputOption('bar'), new InputArgument('cat'))); + $this->assertEquals('[options] [--] []', $definition->getSynopsis(true), '->getSynopsis(true) groups options in [options]'); + } + + /** + * @group legacy + */ + public function testLegacyAsText() + { + $definition = new InputDefinition(array( + new InputArgument('foo', InputArgument::OPTIONAL, 'The foo argument'), + new InputArgument('baz', InputArgument::OPTIONAL, 'The baz argument', true), + new InputArgument('bar', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'The bar argument', array('http://foo.com/')), + new InputOption('foo', 'f', InputOption::VALUE_REQUIRED, 'The foo option'), + new InputOption('baz', null, InputOption::VALUE_OPTIONAL, 'The baz option', false), + new InputOption('bar', 'b', InputOption::VALUE_OPTIONAL, 'The bar option', 'bar'), + new InputOption('qux', '', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'The qux option', array('http://foo.com/', 'bar')), + new InputOption('qux2', '', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'The qux2 option', array('foo' => 'bar')), + )); + + $this->assertStringEqualsFile(self::$fixtures.'/definition_astext.txt', $definition->asText(), '->asText() returns a textual representation of the InputDefinition'); + } + + /** + * @group legacy + */ + public function testLegacyAsXml() + { + $definition = new InputDefinition(array( + new InputArgument('foo', InputArgument::OPTIONAL, 'The foo argument'), + new InputArgument('baz', InputArgument::OPTIONAL, 'The baz argument', true), + new InputArgument('bar', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'The bar argument', array('bar')), + new InputOption('foo', 'f', InputOption::VALUE_REQUIRED, 'The foo option'), + new InputOption('baz', null, InputOption::VALUE_OPTIONAL, 'The baz option', false), + new InputOption('bar', 'b', InputOption::VALUE_OPTIONAL, 'The bar option', 'bar'), + )); + $this->assertXmlStringEqualsXmlFile(self::$fixtures.'/definition_asxml.txt', $definition->asXml(), '->asXml() returns an XML representation of the InputDefinition'); + } + + protected function initializeArguments() + { + $this->foo = new InputArgument('foo'); + $this->bar = new InputArgument('bar'); + $this->foo1 = new InputArgument('foo'); + $this->foo2 = new InputArgument('foo2', InputArgument::REQUIRED); + } + + protected function initializeOptions() + { + $this->foo = new InputOption('foo', 'f'); + $this->bar = new InputOption('bar', 'b'); + $this->foo1 = new InputOption('fooBis', 'f'); + $this->foo2 = new InputOption('foo', 'p'); + $this->multi = new InputOption('multi', 'm|mm|mmm'); + } +} diff --git a/vendor/symfony/console/Tests/Input/InputOptionTest.php b/vendor/symfony/console/Tests/Input/InputOptionTest.php new file mode 100644 index 0000000000..53ce1df8cf --- /dev/null +++ b/vendor/symfony/console/Tests/Input/InputOptionTest.php @@ -0,0 +1,204 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Input; + +use Symfony\Component\Console\Input\InputOption; + +class InputOptionTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $option = new InputOption('foo'); + $this->assertEquals('foo', $option->getName(), '__construct() takes a name as its first argument'); + $option = new InputOption('--foo'); + $this->assertEquals('foo', $option->getName(), '__construct() removes the leading -- of the option name'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Impossible to have an option mode VALUE_IS_ARRAY if the option does not accept a value. + */ + public function testArrayModeWithoutValue() + { + new InputOption('foo', 'f', InputOption::VALUE_IS_ARRAY); + } + + public function testShortcut() + { + $option = new InputOption('foo', 'f'); + $this->assertEquals('f', $option->getShortcut(), '__construct() can take a shortcut as its second argument'); + $option = new InputOption('foo', '-f|-ff|fff'); + $this->assertEquals('f|ff|fff', $option->getShortcut(), '__construct() removes the leading - of the shortcuts'); + $option = new InputOption('foo', array('f', 'ff', '-fff')); + $this->assertEquals('f|ff|fff', $option->getShortcut(), '__construct() removes the leading - of the shortcuts'); + $option = new InputOption('foo'); + $this->assertNull($option->getShortcut(), '__construct() makes the shortcut null by default'); + } + + public function testModes() + { + $option = new InputOption('foo', 'f'); + $this->assertFalse($option->acceptValue(), '__construct() gives a "InputOption::VALUE_NONE" mode by default'); + $this->assertFalse($option->isValueRequired(), '__construct() gives a "InputOption::VALUE_NONE" mode by default'); + $this->assertFalse($option->isValueOptional(), '__construct() gives a "InputOption::VALUE_NONE" mode by default'); + + $option = new InputOption('foo', 'f', null); + $this->assertFalse($option->acceptValue(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); + $this->assertFalse($option->isValueRequired(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); + $this->assertFalse($option->isValueOptional(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); + + $option = new InputOption('foo', 'f', InputOption::VALUE_NONE); + $this->assertFalse($option->acceptValue(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); + $this->assertFalse($option->isValueRequired(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); + $this->assertFalse($option->isValueOptional(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); + + $option = new InputOption('foo', 'f', InputOption::VALUE_REQUIRED); + $this->assertTrue($option->acceptValue(), '__construct() can take "InputOption::VALUE_REQUIRED" as its mode'); + $this->assertTrue($option->isValueRequired(), '__construct() can take "InputOption::VALUE_REQUIRED" as its mode'); + $this->assertFalse($option->isValueOptional(), '__construct() can take "InputOption::VALUE_REQUIRED" as its mode'); + + $option = new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL); + $this->assertTrue($option->acceptValue(), '__construct() can take "InputOption::VALUE_OPTIONAL" as its mode'); + $this->assertFalse($option->isValueRequired(), '__construct() can take "InputOption::VALUE_OPTIONAL" as its mode'); + $this->assertTrue($option->isValueOptional(), '__construct() can take "InputOption::VALUE_OPTIONAL" as its mode'); + } + + /** + * @dataProvider provideInvalidModes + */ + public function testInvalidModes($mode) + { + $this->setExpectedException('InvalidArgumentException', sprintf('Option mode "%s" is not valid.', $mode)); + + new InputOption('foo', 'f', $mode); + } + + public function provideInvalidModes() + { + return array( + array('ANOTHER_ONE'), + array(-1), + ); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testEmptyNameIsInvalid() + { + new InputOption(''); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testDoubleDashNameIsInvalid() + { + new InputOption('--'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testSingleDashOptionIsInvalid() + { + new InputOption('foo', '-'); + } + + public function testIsArray() + { + $option = new InputOption('foo', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY); + $this->assertTrue($option->isArray(), '->isArray() returns true if the option can be an array'); + $option = new InputOption('foo', null, InputOption::VALUE_NONE); + $this->assertFalse($option->isArray(), '->isArray() returns false if the option can not be an array'); + } + + public function testGetDescription() + { + $option = new InputOption('foo', 'f', null, 'Some description'); + $this->assertEquals('Some description', $option->getDescription(), '->getDescription() returns the description message'); + } + + public function testGetDefault() + { + $option = new InputOption('foo', null, InputOption::VALUE_OPTIONAL, '', 'default'); + $this->assertEquals('default', $option->getDefault(), '->getDefault() returns the default value'); + + $option = new InputOption('foo', null, InputOption::VALUE_REQUIRED, '', 'default'); + $this->assertEquals('default', $option->getDefault(), '->getDefault() returns the default value'); + + $option = new InputOption('foo', null, InputOption::VALUE_REQUIRED); + $this->assertNull($option->getDefault(), '->getDefault() returns null if no default value is configured'); + + $option = new InputOption('foo', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY); + $this->assertEquals(array(), $option->getDefault(), '->getDefault() returns an empty array if option is an array'); + + $option = new InputOption('foo', null, InputOption::VALUE_NONE); + $this->assertFalse($option->getDefault(), '->getDefault() returns false if the option does not take a value'); + } + + public function testSetDefault() + { + $option = new InputOption('foo', null, InputOption::VALUE_REQUIRED, '', 'default'); + $option->setDefault(null); + $this->assertNull($option->getDefault(), '->setDefault() can reset the default value by passing null'); + $option->setDefault('another'); + $this->assertEquals('another', $option->getDefault(), '->setDefault() changes the default value'); + + $option = new InputOption('foo', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY); + $option->setDefault(array(1, 2)); + $this->assertEquals(array(1, 2), $option->getDefault(), '->setDefault() changes the default value'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Cannot set a default value when using InputOption::VALUE_NONE mode. + */ + public function testDefaultValueWithValueNoneMode() + { + $option = new InputOption('foo', 'f', InputOption::VALUE_NONE); + $option->setDefault('default'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage A default value for an array option must be an array. + */ + public function testDefaultValueWithIsArrayMode() + { + $option = new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY); + $option->setDefault('default'); + } + + public function testEquals() + { + $option = new InputOption('foo', 'f', null, 'Some description'); + $option2 = new InputOption('foo', 'f', null, 'Alternative description'); + $this->assertTrue($option->equals($option2)); + + $option = new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, 'Some description'); + $option2 = new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, 'Some description', true); + $this->assertFalse($option->equals($option2)); + + $option = new InputOption('foo', 'f', null, 'Some description'); + $option2 = new InputOption('bar', 'f', null, 'Some description'); + $this->assertFalse($option->equals($option2)); + + $option = new InputOption('foo', 'f', null, 'Some description'); + $option2 = new InputOption('foo', '', null, 'Some description'); + $this->assertFalse($option->equals($option2)); + + $option = new InputOption('foo', 'f', null, 'Some description'); + $option2 = new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, 'Some description'); + $this->assertFalse($option->equals($option2)); + } +} diff --git a/vendor/symfony/console/Tests/Input/InputTest.php b/vendor/symfony/console/Tests/Input/InputTest.php new file mode 100644 index 0000000000..eb1c6617f5 --- /dev/null +++ b/vendor/symfony/console/Tests/Input/InputTest.php @@ -0,0 +1,132 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Input; + +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; + +class InputTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name')))); + $this->assertEquals('foo', $input->getArgument('name'), '->__construct() takes a InputDefinition as an argument'); + } + + public function testOptions() + { + $input = new ArrayInput(array('--name' => 'foo'), new InputDefinition(array(new InputOption('name')))); + $this->assertEquals('foo', $input->getOption('name'), '->getOption() returns the value for the given option'); + + $input->setOption('name', 'bar'); + $this->assertEquals('bar', $input->getOption('name'), '->setOption() sets the value for a given option'); + $this->assertEquals(array('name' => 'bar'), $input->getOptions(), '->getOptions() returns all option values'); + + $input = new ArrayInput(array('--name' => 'foo'), new InputDefinition(array(new InputOption('name'), new InputOption('bar', '', InputOption::VALUE_OPTIONAL, '', 'default')))); + $this->assertEquals('default', $input->getOption('bar'), '->getOption() returns the default value for optional options'); + $this->assertEquals(array('name' => 'foo', 'bar' => 'default'), $input->getOptions(), '->getOptions() returns all option values, even optional ones'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "foo" option does not exist. + */ + public function testSetInvalidOption() + { + $input = new ArrayInput(array('--name' => 'foo'), new InputDefinition(array(new InputOption('name'), new InputOption('bar', '', InputOption::VALUE_OPTIONAL, '', 'default')))); + $input->setOption('foo', 'bar'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "foo" option does not exist. + */ + public function testGetInvalidOption() + { + $input = new ArrayInput(array('--name' => 'foo'), new InputDefinition(array(new InputOption('name'), new InputOption('bar', '', InputOption::VALUE_OPTIONAL, '', 'default')))); + $input->getOption('foo'); + } + + public function testArguments() + { + $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name')))); + $this->assertEquals('foo', $input->getArgument('name'), '->getArgument() returns the value for the given argument'); + + $input->setArgument('name', 'bar'); + $this->assertEquals('bar', $input->getArgument('name'), '->setArgument() sets the value for a given argument'); + $this->assertEquals(array('name' => 'bar'), $input->getArguments(), '->getArguments() returns all argument values'); + + $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name'), new InputArgument('bar', InputArgument::OPTIONAL, '', 'default')))); + $this->assertEquals('default', $input->getArgument('bar'), '->getArgument() returns the default value for optional arguments'); + $this->assertEquals(array('name' => 'foo', 'bar' => 'default'), $input->getArguments(), '->getArguments() returns all argument values, even optional ones'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "foo" argument does not exist. + */ + public function testSetInvalidArgument() + { + $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name'), new InputArgument('bar', InputArgument::OPTIONAL, '', 'default')))); + $input->setArgument('foo', 'bar'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "foo" argument does not exist. + */ + public function testGetInvalidArgument() + { + $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name'), new InputArgument('bar', InputArgument::OPTIONAL, '', 'default')))); + $input->getArgument('foo'); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Not enough arguments (missing: "name"). + */ + public function testValidateWithMissingArguments() + { + $input = new ArrayInput(array()); + $input->bind(new InputDefinition(array(new InputArgument('name', InputArgument::REQUIRED)))); + $input->validate(); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Not enough arguments (missing: "name"). + */ + public function testValidateWithMissingRequiredArguments() + { + $input = new ArrayInput(array('bar' => 'baz')); + $input->bind(new InputDefinition(array(new InputArgument('name', InputArgument::REQUIRED), new InputArgument('bar', InputArgument::OPTIONAL)))); + $input->validate(); + } + + public function testValidate() + { + $input = new ArrayInput(array('name' => 'foo')); + $input->bind(new InputDefinition(array(new InputArgument('name', InputArgument::REQUIRED)))); + + $this->assertNull($input->validate()); + } + + public function testSetGetInteractive() + { + $input = new ArrayInput(array()); + $this->assertTrue($input->isInteractive(), '->isInteractive() returns whether the input should be interactive or not'); + $input->setInteractive(false); + $this->assertFalse($input->isInteractive(), '->setInteractive() changes the interactive flag'); + } +} diff --git a/vendor/symfony/console/Tests/Input/StringInputTest.php b/vendor/symfony/console/Tests/Input/StringInputTest.php new file mode 100644 index 0000000000..c8a560f6b2 --- /dev/null +++ b/vendor/symfony/console/Tests/Input/StringInputTest.php @@ -0,0 +1,99 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Input; + +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\StringInput; + +class StringInputTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider getTokenizeData + */ + public function testTokenize($input, $tokens, $message) + { + $input = new StringInput($input); + $r = new \ReflectionClass('Symfony\Component\Console\Input\ArgvInput'); + $p = $r->getProperty('tokens'); + $p->setAccessible(true); + $this->assertEquals($tokens, $p->getValue($input), $message); + } + + public function testInputOptionWithGivenString() + { + $definition = new InputDefinition( + array(new InputOption('foo', null, InputOption::VALUE_REQUIRED)) + ); + + // call to bind + $input = new StringInput('--foo=bar'); + $input->bind($definition); + $this->assertEquals('bar', $input->getOption('foo')); + } + + /** + * @group legacy + */ + public function testLegacyInputOptionDefinitionInConstructor() + { + $definition = new InputDefinition( + array(new InputOption('foo', null, InputOption::VALUE_REQUIRED)) + ); + + $input = new StringInput('--foo=bar', $definition); + $this->assertEquals('bar', $input->getOption('foo')); + } + + public function getTokenizeData() + { + return array( + array('', array(), '->tokenize() parses an empty string'), + array('foo', array('foo'), '->tokenize() parses arguments'), + array(' foo bar ', array('foo', 'bar'), '->tokenize() ignores whitespaces between arguments'), + array('"quoted"', array('quoted'), '->tokenize() parses quoted arguments'), + array("'quoted'", array('quoted'), '->tokenize() parses quoted arguments'), + array("'a\rb\nc\td'", array("a\rb\nc\td"), '->tokenize() parses whitespace chars in strings'), + array("'a'\r'b'\n'c'\t'd'", array('a', 'b', 'c', 'd'), '->tokenize() parses whitespace chars between args as spaces'), + array('\"quoted\"', array('"quoted"'), '->tokenize() parses escaped-quoted arguments'), + array("\'quoted\'", array('\'quoted\''), '->tokenize() parses escaped-quoted arguments'), + array('-a', array('-a'), '->tokenize() parses short options'), + array('-azc', array('-azc'), '->tokenize() parses aggregated short options'), + array('-awithavalue', array('-awithavalue'), '->tokenize() parses short options with a value'), + array('-a"foo bar"', array('-afoo bar'), '->tokenize() parses short options with a value'), + array('-a"foo bar""foo bar"', array('-afoo barfoo bar'), '->tokenize() parses short options with a value'), + array('-a\'foo bar\'', array('-afoo bar'), '->tokenize() parses short options with a value'), + array('-a\'foo bar\'\'foo bar\'', array('-afoo barfoo bar'), '->tokenize() parses short options with a value'), + array('-a\'foo bar\'"foo bar"', array('-afoo barfoo bar'), '->tokenize() parses short options with a value'), + array('--long-option', array('--long-option'), '->tokenize() parses long options'), + array('--long-option=foo', array('--long-option=foo'), '->tokenize() parses long options with a value'), + array('--long-option="foo bar"', array('--long-option=foo bar'), '->tokenize() parses long options with a value'), + array('--long-option="foo bar""another"', array('--long-option=foo baranother'), '->tokenize() parses long options with a value'), + array('--long-option=\'foo bar\'', array('--long-option=foo bar'), '->tokenize() parses long options with a value'), + array("--long-option='foo bar''another'", array('--long-option=foo baranother'), '->tokenize() parses long options with a value'), + array("--long-option='foo bar'\"another\"", array('--long-option=foo baranother'), '->tokenize() parses long options with a value'), + array('foo -a -ffoo --long bar', array('foo', '-a', '-ffoo', '--long', 'bar'), '->tokenize() parses when several arguments and options'), + ); + } + + public function testToString() + { + $input = new StringInput('-f foo'); + $this->assertEquals('-f foo', (string) $input); + + $input = new StringInput('-f --bar=foo "a b c d"'); + $this->assertEquals('-f --bar=foo '.escapeshellarg('a b c d'), (string) $input); + + $input = new StringInput('-f --bar=foo \'a b c d\' '."'A\nB\\'C'"); + $this->assertEquals('-f --bar=foo '.escapeshellarg('a b c d').' '.escapeshellarg("A\nB'C"), (string) $input); + } +} diff --git a/vendor/symfony/console/Tests/Logger/ConsoleLoggerTest.php b/vendor/symfony/console/Tests/Logger/ConsoleLoggerTest.php new file mode 100644 index 0000000000..c5eca2cafd --- /dev/null +++ b/vendor/symfony/console/Tests/Logger/ConsoleLoggerTest.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Logger; + +use Psr\Log\Test\LoggerInterfaceTest; +use Psr\Log\LogLevel; +use Symfony\Component\Console\Logger\ConsoleLogger; +use Symfony\Component\Console\Tests\Fixtures\DummyOutput; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Console logger test. + * + * @author Kévin Dunglas + */ +class ConsoleLoggerTest extends LoggerInterfaceTest +{ + /** + * @var DummyOutput + */ + protected $output; + + /** + * {@inheritdoc} + */ + public function getLogger() + { + $this->output = new DummyOutput(OutputInterface::VERBOSITY_VERBOSE); + + return new ConsoleLogger($this->output, array( + LogLevel::EMERGENCY => OutputInterface::VERBOSITY_NORMAL, + LogLevel::ALERT => OutputInterface::VERBOSITY_NORMAL, + LogLevel::CRITICAL => OutputInterface::VERBOSITY_NORMAL, + LogLevel::ERROR => OutputInterface::VERBOSITY_NORMAL, + LogLevel::WARNING => OutputInterface::VERBOSITY_NORMAL, + LogLevel::NOTICE => OutputInterface::VERBOSITY_NORMAL, + LogLevel::INFO => OutputInterface::VERBOSITY_NORMAL, + LogLevel::DEBUG => OutputInterface::VERBOSITY_NORMAL, + )); + } + + /** + * {@inheritdoc} + */ + public function getLogs() + { + return $this->output->getLogs(); + } +} diff --git a/vendor/symfony/console/Tests/Output/ConsoleOutputTest.php b/vendor/symfony/console/Tests/Output/ConsoleOutputTest.php new file mode 100644 index 0000000000..1afbbb6e6c --- /dev/null +++ b/vendor/symfony/console/Tests/Output/ConsoleOutputTest.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Output; + +use Symfony\Component\Console\Output\ConsoleOutput; +use Symfony\Component\Console\Output\Output; + +class ConsoleOutputTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $output = new ConsoleOutput(Output::VERBOSITY_QUIET, true); + $this->assertEquals(Output::VERBOSITY_QUIET, $output->getVerbosity(), '__construct() takes the verbosity as its first argument'); + $this->assertSame($output->getFormatter(), $output->getErrorOutput()->getFormatter(), '__construct() takes a formatter or null as the third argument'); + } +} diff --git a/vendor/symfony/console/Tests/Output/NullOutputTest.php b/vendor/symfony/console/Tests/Output/NullOutputTest.php new file mode 100644 index 0000000000..b20ae4e8d0 --- /dev/null +++ b/vendor/symfony/console/Tests/Output/NullOutputTest.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Output; + +use Symfony\Component\Console\Output\NullOutput; +use Symfony\Component\Console\Output\OutputInterface; + +class NullOutputTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $output = new NullOutput(); + + ob_start(); + $output->write('foo'); + $buffer = ob_get_clean(); + + $this->assertSame('', $buffer, '->write() does nothing (at least nothing is printed)'); + $this->assertFalse($output->isDecorated(), '->isDecorated() returns false'); + } + + public function testVerbosity() + { + $output = new NullOutput(); + $this->assertSame(OutputInterface::VERBOSITY_QUIET, $output->getVerbosity(), '->getVerbosity() returns VERBOSITY_QUIET for NullOutput by default'); + + $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE); + $this->assertSame(OutputInterface::VERBOSITY_QUIET, $output->getVerbosity(), '->getVerbosity() always returns VERBOSITY_QUIET for NullOutput'); + } +} diff --git a/vendor/symfony/console/Tests/Output/OutputTest.php b/vendor/symfony/console/Tests/Output/OutputTest.php new file mode 100644 index 0000000000..45e6ddc7dc --- /dev/null +++ b/vendor/symfony/console/Tests/Output/OutputTest.php @@ -0,0 +1,175 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Output; + +use Symfony\Component\Console\Output\Output; +use Symfony\Component\Console\Formatter\OutputFormatterStyle; + +class OutputTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $output = new TestOutput(Output::VERBOSITY_QUIET, true); + $this->assertEquals(Output::VERBOSITY_QUIET, $output->getVerbosity(), '__construct() takes the verbosity as its first argument'); + $this->assertTrue($output->isDecorated(), '__construct() takes the decorated flag as its second argument'); + } + + public function testSetIsDecorated() + { + $output = new TestOutput(); + $output->setDecorated(true); + $this->assertTrue($output->isDecorated(), 'setDecorated() sets the decorated flag'); + } + + public function testSetGetVerbosity() + { + $output = new TestOutput(); + $output->setVerbosity(Output::VERBOSITY_QUIET); + $this->assertEquals(Output::VERBOSITY_QUIET, $output->getVerbosity(), '->setVerbosity() sets the verbosity'); + + $this->assertTrue($output->isQuiet()); + $this->assertFalse($output->isVerbose()); + $this->assertFalse($output->isVeryVerbose()); + $this->assertFalse($output->isDebug()); + + $output->setVerbosity(Output::VERBOSITY_NORMAL); + $this->assertFalse($output->isQuiet()); + $this->assertFalse($output->isVerbose()); + $this->assertFalse($output->isVeryVerbose()); + $this->assertFalse($output->isDebug()); + + $output->setVerbosity(Output::VERBOSITY_VERBOSE); + $this->assertFalse($output->isQuiet()); + $this->assertTrue($output->isVerbose()); + $this->assertFalse($output->isVeryVerbose()); + $this->assertFalse($output->isDebug()); + + $output->setVerbosity(Output::VERBOSITY_VERY_VERBOSE); + $this->assertFalse($output->isQuiet()); + $this->assertTrue($output->isVerbose()); + $this->assertTrue($output->isVeryVerbose()); + $this->assertFalse($output->isDebug()); + + $output->setVerbosity(Output::VERBOSITY_DEBUG); + $this->assertFalse($output->isQuiet()); + $this->assertTrue($output->isVerbose()); + $this->assertTrue($output->isVeryVerbose()); + $this->assertTrue($output->isDebug()); + } + + public function testWriteWithVerbosityQuiet() + { + $output = new TestOutput(Output::VERBOSITY_QUIET); + $output->writeln('foo'); + $this->assertEquals('', $output->output, '->writeln() outputs nothing if verbosity is set to VERBOSITY_QUIET'); + } + + public function testWriteAnArrayOfMessages() + { + $output = new TestOutput(); + $output->writeln(array('foo', 'bar')); + $this->assertEquals("foo\nbar\n", $output->output, '->writeln() can take an array of messages to output'); + } + + /** + * @dataProvider provideWriteArguments + */ + public function testWriteRawMessage($message, $type, $expectedOutput) + { + $output = new TestOutput(); + $output->writeln($message, $type); + $this->assertEquals($expectedOutput, $output->output); + } + + public function provideWriteArguments() + { + return array( + array('foo', Output::OUTPUT_RAW, "foo\n"), + array('foo', Output::OUTPUT_PLAIN, "foo\n"), + ); + } + + public function testWriteWithDecorationTurnedOff() + { + $output = new TestOutput(); + $output->setDecorated(false); + $output->writeln('foo'); + $this->assertEquals("foo\n", $output->output, '->writeln() strips decoration tags if decoration is set to false'); + } + + public function testWriteDecoratedMessage() + { + $fooStyle = new OutputFormatterStyle('yellow', 'red', array('blink')); + $output = new TestOutput(); + $output->getFormatter()->setStyle('FOO', $fooStyle); + $output->setDecorated(true); + $output->writeln('foo'); + $this->assertEquals("\033[33;41;5mfoo\033[39;49;25m\n", $output->output, '->writeln() decorates the output'); + } + + public function testWriteWithInvalidStyle() + { + $output = new TestOutput(); + + $output->clear(); + $output->write('foo'); + $this->assertEquals('foo', $output->output, '->write() do nothing when a style does not exist'); + + $output->clear(); + $output->writeln('foo'); + $this->assertEquals("foo\n", $output->output, '->writeln() do nothing when a style does not exist'); + } + + /** + * @dataProvider verbosityProvider + */ + public function testWriteWithVerbosityOption($verbosity, $expected, $msg) + { + $output = new TestOutput(); + + $output->setVerbosity($verbosity); + $output->clear(); + $output->write('1', false); + $output->write('2', false, Output::VERBOSITY_QUIET); + $output->write('3', false, Output::VERBOSITY_NORMAL); + $output->write('4', false, Output::VERBOSITY_VERBOSE); + $output->write('5', false, Output::VERBOSITY_VERY_VERBOSE); + $output->write('6', false, Output::VERBOSITY_DEBUG); + $this->assertEquals($expected, $output->output, $msg); + } + + public function verbosityProvider() + { + return array( + array(Output::VERBOSITY_QUIET, '2', '->write() in QUIET mode only outputs when an explicit QUIET verbosity is passed'), + array(Output::VERBOSITY_NORMAL, '123', '->write() in NORMAL mode outputs anything below an explicit VERBOSE verbosity'), + array(Output::VERBOSITY_VERBOSE, '1234', '->write() in VERBOSE mode outputs anything below an explicit VERY_VERBOSE verbosity'), + array(Output::VERBOSITY_VERY_VERBOSE, '12345', '->write() in VERY_VERBOSE mode outputs anything below an explicit DEBUG verbosity'), + array(Output::VERBOSITY_DEBUG, '123456', '->write() in DEBUG mode outputs everything'), + ); + } +} + +class TestOutput extends Output +{ + public $output = ''; + + public function clear() + { + $this->output = ''; + } + + protected function doWrite($message, $newline) + { + $this->output .= $message.($newline ? "\n" : ''); + } +} diff --git a/vendor/symfony/console/Tests/Output/StreamOutputTest.php b/vendor/symfony/console/Tests/Output/StreamOutputTest.php new file mode 100644 index 0000000000..2fd4f61214 --- /dev/null +++ b/vendor/symfony/console/Tests/Output/StreamOutputTest.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Output; + +use Symfony\Component\Console\Output\Output; +use Symfony\Component\Console\Output\StreamOutput; + +class StreamOutputTest extends \PHPUnit_Framework_TestCase +{ + protected $stream; + + protected function setUp() + { + $this->stream = fopen('php://memory', 'a', false); + } + + protected function tearDown() + { + $this->stream = null; + } + + public function testConstructor() + { + $output = new StreamOutput($this->stream, Output::VERBOSITY_QUIET, true); + $this->assertEquals(Output::VERBOSITY_QUIET, $output->getVerbosity(), '__construct() takes the verbosity as its first argument'); + $this->assertTrue($output->isDecorated(), '__construct() takes the decorated flag as its second argument'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The StreamOutput class needs a stream as its first argument. + */ + public function testStreamIsRequired() + { + new StreamOutput('foo'); + } + + public function testGetStream() + { + $output = new StreamOutput($this->stream); + $this->assertEquals($this->stream, $output->getStream(), '->getStream() returns the current stream'); + } + + public function testDoWrite() + { + $output = new StreamOutput($this->stream); + $output->writeln('foo'); + rewind($output->getStream()); + $this->assertEquals('foo'.PHP_EOL, stream_get_contents($output->getStream()), '->doWrite() writes to the stream'); + } +} diff --git a/vendor/symfony/console/Tests/Style/SymfonyStyleTest.php b/vendor/symfony/console/Tests/Style/SymfonyStyleTest.php new file mode 100644 index 0000000000..889a9c82f2 --- /dev/null +++ b/vendor/symfony/console/Tests/Style/SymfonyStyleTest.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Style; + +use PHPUnit_Framework_TestCase; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\Console\Tester\CommandTester; + +class SymfonyStyleTest extends PHPUnit_Framework_TestCase +{ + /** @var Command */ + protected $command; + /** @var CommandTester */ + protected $tester; + + protected function setUp() + { + $this->command = new Command('sfstyle'); + $this->tester = new CommandTester($this->command); + } + + protected function tearDown() + { + $this->command = null; + $this->tester = null; + } + + /** + * @dataProvider inputCommandToOutputFilesProvider + */ + public function testOutputs($inputCommandFilepath, $outputFilepath) + { + $code = require $inputCommandFilepath; + $this->command->setCode($code); + $this->tester->execute(array(), array('interactive' => false, 'decorated' => false)); + $this->assertStringEqualsFile($outputFilepath, $this->tester->getDisplay(true)); + } + + public function inputCommandToOutputFilesProvider() + { + $baseDir = __DIR__.'/../Fixtures/Style/SymfonyStyle'; + + return array_map(null, glob($baseDir.'/command/command_*.php'), glob($baseDir.'/output/output_*.txt')); + } + + public function testLongWordsBlockWrapping() + { + $word = 'Lopadotemachoselachogaleokranioleipsanodrimhypotrimmatosilphioparaomelitokatakechymenokichlepikossyphophattoperisteralektryonoptekephalliokigklopeleiolagoiosiraiobaphetraganopterygovgollhjvhvljfezefeqifzeiqgiqzhrsdgihqzridghqridghqirshdghdghieridgheirhsdgehrsdvhqrsidhqshdgihrsidvqhneriqsdvjzergetsrfhgrstsfhsetsfhesrhdgtesfhbzrtfbrztvetbsdfbrsdfbrn'; + $wordLength = strlen($word); + $maxLineLength = SymfonyStyle::MAX_LINE_LENGTH - 3; + + $this->command->setCode(function (InputInterface $input, OutputInterface $output) use ($word) { + $sfStyle = new SymfonyStyleWithForcedLineLength($input, $output); + $sfStyle->block($word, 'CUSTOM', 'fg=white;bg=blue', ' § ', false); + }); + + $this->tester->execute(array(), array('interactive' => false, 'decorated' => false)); + $expectedCount = (int) ceil($wordLength / ($maxLineLength)) + (int) ($wordLength > $maxLineLength - 5); + $this->assertSame($expectedCount, substr_count($this->tester->getDisplay(true), ' § ')); + } +} + +/** + * Use this class in tests to force the line length + * and ensure a consistent output for expectations. + */ +class SymfonyStyleWithForcedLineLength extends SymfonyStyle +{ + public function __construct(InputInterface $input, OutputInterface $output) + { + parent::__construct($input, $output); + + $ref = new \ReflectionProperty(get_parent_class($this), 'lineLength'); + $ref->setAccessible(true); + $ref->setValue($this, 120); + } +} diff --git a/vendor/symfony/console/Tests/Tester/ApplicationTesterTest.php b/vendor/symfony/console/Tests/Tester/ApplicationTesterTest.php new file mode 100644 index 0000000000..a8389dd186 --- /dev/null +++ b/vendor/symfony/console/Tests/Tester/ApplicationTesterTest.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Tester; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Output\Output; +use Symfony\Component\Console\Tester\ApplicationTester; + +class ApplicationTesterTest extends \PHPUnit_Framework_TestCase +{ + protected $application; + protected $tester; + + protected function setUp() + { + $this->application = new Application(); + $this->application->setAutoExit(false); + $this->application->register('foo') + ->addArgument('foo') + ->setCode(function ($input, $output) { $output->writeln('foo'); }) + ; + + $this->tester = new ApplicationTester($this->application); + $this->tester->run(array('command' => 'foo', 'foo' => 'bar'), array('interactive' => false, 'decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE)); + } + + protected function tearDown() + { + $this->application = null; + $this->tester = null; + } + + public function testRun() + { + $this->assertFalse($this->tester->getInput()->isInteractive(), '->execute() takes an interactive option'); + $this->assertFalse($this->tester->getOutput()->isDecorated(), '->execute() takes a decorated option'); + $this->assertEquals(Output::VERBOSITY_VERBOSE, $this->tester->getOutput()->getVerbosity(), '->execute() takes a verbosity option'); + } + + public function testGetInput() + { + $this->assertEquals('bar', $this->tester->getInput()->getArgument('foo'), '->getInput() returns the current input instance'); + } + + public function testGetOutput() + { + rewind($this->tester->getOutput()->getStream()); + $this->assertEquals('foo'.PHP_EOL, stream_get_contents($this->tester->getOutput()->getStream()), '->getOutput() returns the current output instance'); + } + + public function testGetDisplay() + { + $this->assertEquals('foo'.PHP_EOL, $this->tester->getDisplay(), '->getDisplay() returns the display of the last execution'); + } + + public function testGetStatusCode() + { + $this->assertSame(0, $this->tester->getStatusCode(), '->getStatusCode() returns the status code'); + } +} diff --git a/vendor/symfony/console/Tests/Tester/CommandTesterTest.php b/vendor/symfony/console/Tests/Tester/CommandTesterTest.php new file mode 100644 index 0000000000..b54c00e83d --- /dev/null +++ b/vendor/symfony/console/Tests/Tester/CommandTesterTest.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Tester; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Output\Output; +use Symfony\Component\Console\Tester\CommandTester; + +class CommandTesterTest extends \PHPUnit_Framework_TestCase +{ + protected $command; + protected $tester; + + protected function setUp() + { + $this->command = new Command('foo'); + $this->command->addArgument('command'); + $this->command->addArgument('foo'); + $this->command->setCode(function ($input, $output) { $output->writeln('foo'); }); + + $this->tester = new CommandTester($this->command); + $this->tester->execute(array('foo' => 'bar'), array('interactive' => false, 'decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE)); + } + + protected function tearDown() + { + $this->command = null; + $this->tester = null; + } + + public function testExecute() + { + $this->assertFalse($this->tester->getInput()->isInteractive(), '->execute() takes an interactive option'); + $this->assertFalse($this->tester->getOutput()->isDecorated(), '->execute() takes a decorated option'); + $this->assertEquals(Output::VERBOSITY_VERBOSE, $this->tester->getOutput()->getVerbosity(), '->execute() takes a verbosity option'); + } + + public function testGetInput() + { + $this->assertEquals('bar', $this->tester->getInput()->getArgument('foo'), '->getInput() returns the current input instance'); + } + + public function testGetOutput() + { + rewind($this->tester->getOutput()->getStream()); + $this->assertEquals('foo'.PHP_EOL, stream_get_contents($this->tester->getOutput()->getStream()), '->getOutput() returns the current output instance'); + } + + public function testGetDisplay() + { + $this->assertEquals('foo'.PHP_EOL, $this->tester->getDisplay(), '->getDisplay() returns the display of the last execution'); + } + + public function testGetStatusCode() + { + $this->assertSame(0, $this->tester->getStatusCode(), '->getStatusCode() returns the status code'); + } + + public function testCommandFromApplication() + { + $application = new Application(); + $application->setAutoExit(false); + + $command = new Command('foo'); + $command->setCode(function ($input, $output) { $output->writeln('foo'); }); + + $application->add($command); + + $tester = new CommandTester($application->find('foo')); + + // check that there is no need to pass the command name here + $this->assertEquals(0, $tester->execute(array())); + } +} diff --git a/vendor/symfony/console/composer.json b/vendor/symfony/console/composer.json new file mode 100644 index 0000000000..ab247045fe --- /dev/null +++ b/vendor/symfony/console/composer.json @@ -0,0 +1,44 @@ +{ + "name": "symfony/console", + "type": "library", + "description": "Symfony Console Component", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.9", + "symfony/polyfill-mbstring": "~1.0" + }, + "require-dev": { + "symfony/event-dispatcher": "~2.1|~3.0.0", + "symfony/process": "~2.1|~3.0.0", + "psr/log": "~1.0" + }, + "suggest": { + "symfony/event-dispatcher": "", + "symfony/process": "", + "psr/log": "For using the console logger" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\Console\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.8-dev" + } + } +} diff --git a/vendor/symfony/console/phpunit.xml.dist b/vendor/symfony/console/phpunit.xml.dist new file mode 100644 index 0000000000..8c09554f80 --- /dev/null +++ b/vendor/symfony/console/phpunit.xml.dist @@ -0,0 +1,39 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + + + + + + Symfony\Component\Console + + + + + diff --git a/vendor/symfony/event-dispatcher/.gitignore b/vendor/symfony/event-dispatcher/.gitignore new file mode 100644 index 0000000000..c49a5d8df5 --- /dev/null +++ b/vendor/symfony/event-dispatcher/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/vendor/symfony/event-dispatcher/CHANGELOG.md b/vendor/symfony/event-dispatcher/CHANGELOG.md new file mode 100644 index 0000000000..bb42ee19c0 --- /dev/null +++ b/vendor/symfony/event-dispatcher/CHANGELOG.md @@ -0,0 +1,23 @@ +CHANGELOG +========= + +2.5.0 +----- + + * added Debug\TraceableEventDispatcher (originally in HttpKernel) + * changed Debug\TraceableEventDispatcherInterface to extend EventDispatcherInterface + * added RegisterListenersPass (originally in HttpKernel) + +2.1.0 +----- + + * added TraceableEventDispatcherInterface + * added ContainerAwareEventDispatcher + * added a reference to the EventDispatcher on the Event + * added a reference to the Event name on the event + * added fluid interface to the dispatch() method which now returns the Event + object + * added GenericEvent event class + * added the possibility for subscribers to subscribe several times for the + same event + * added ImmutableEventDispatcher diff --git a/vendor/symfony/event-dispatcher/ContainerAwareEventDispatcher.php b/vendor/symfony/event-dispatcher/ContainerAwareEventDispatcher.php new file mode 100644 index 0000000000..b92defe691 --- /dev/null +++ b/vendor/symfony/event-dispatcher/ContainerAwareEventDispatcher.php @@ -0,0 +1,187 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Lazily loads listeners and subscribers from the dependency injection + * container. + * + * @author Fabien Potencier + * @author Bernhard Schussek + * @author Jordan Alliot + */ +class ContainerAwareEventDispatcher extends EventDispatcher +{ + /** + * The container from where services are loaded. + * + * @var ContainerInterface + */ + private $container; + + /** + * The service IDs of the event listeners and subscribers. + * + * @var array + */ + private $listenerIds = array(); + + /** + * The services registered as listeners. + * + * @var array + */ + private $listeners = array(); + + /** + * Constructor. + * + * @param ContainerInterface $container A ContainerInterface instance + */ + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + + /** + * Adds a service as event listener. + * + * @param string $eventName Event for which the listener is added + * @param array $callback The service ID of the listener service & the method + * name that has to be called + * @param int $priority The higher this value, the earlier an event listener + * will be triggered in the chain. + * Defaults to 0. + * + * @throws \InvalidArgumentException + */ + public function addListenerService($eventName, $callback, $priority = 0) + { + if (!is_array($callback) || 2 !== count($callback)) { + throw new \InvalidArgumentException('Expected an array("service", "method") argument'); + } + + $this->listenerIds[$eventName][] = array($callback[0], $callback[1], $priority); + } + + public function removeListener($eventName, $listener) + { + $this->lazyLoad($eventName); + + if (isset($this->listenerIds[$eventName])) { + foreach ($this->listenerIds[$eventName] as $i => $args) { + list($serviceId, $method, $priority) = $args; + $key = $serviceId.'.'.$method; + if (isset($this->listeners[$eventName][$key]) && $listener === array($this->listeners[$eventName][$key], $method)) { + unset($this->listeners[$eventName][$key]); + if (empty($this->listeners[$eventName])) { + unset($this->listeners[$eventName]); + } + unset($this->listenerIds[$eventName][$i]); + if (empty($this->listenerIds[$eventName])) { + unset($this->listenerIds[$eventName]); + } + } + } + } + + parent::removeListener($eventName, $listener); + } + + /** + * {@inheritdoc} + */ + public function hasListeners($eventName = null) + { + if (null === $eventName) { + return (bool) count($this->listenerIds) || (bool) count($this->listeners); + } + + if (isset($this->listenerIds[$eventName])) { + return true; + } + + return parent::hasListeners($eventName); + } + + /** + * {@inheritdoc} + */ + public function getListeners($eventName = null) + { + if (null === $eventName) { + foreach ($this->listenerIds as $serviceEventName => $args) { + $this->lazyLoad($serviceEventName); + } + } else { + $this->lazyLoad($eventName); + } + + return parent::getListeners($eventName); + } + + /** + * Adds a service as event subscriber. + * + * @param string $serviceId The service ID of the subscriber service + * @param string $class The service's class name (which must implement EventSubscriberInterface) + */ + public function addSubscriberService($serviceId, $class) + { + foreach ($class::getSubscribedEvents() as $eventName => $params) { + if (is_string($params)) { + $this->listenerIds[$eventName][] = array($serviceId, $params, 0); + } elseif (is_string($params[0])) { + $this->listenerIds[$eventName][] = array($serviceId, $params[0], isset($params[1]) ? $params[1] : 0); + } else { + foreach ($params as $listener) { + $this->listenerIds[$eventName][] = array($serviceId, $listener[0], isset($listener[1]) ? $listener[1] : 0); + } + } + } + } + + public function getContainer() + { + return $this->container; + } + + /** + * Lazily loads listeners for this event from the dependency injection + * container. + * + * @param string $eventName The name of the event to dispatch. The name of + * the event is the name of the method that is + * invoked on listeners. + */ + protected function lazyLoad($eventName) + { + if (isset($this->listenerIds[$eventName])) { + foreach ($this->listenerIds[$eventName] as $args) { + list($serviceId, $method, $priority) = $args; + $listener = $this->container->get($serviceId); + + $key = $serviceId.'.'.$method; + if (!isset($this->listeners[$eventName][$key])) { + $this->addListener($eventName, array($listener, $method), $priority); + } elseif ($listener !== $this->listeners[$eventName][$key]) { + parent::removeListener($eventName, array($this->listeners[$eventName][$key], $method)); + $this->addListener($eventName, array($listener, $method), $priority); + } + + $this->listeners[$eventName][$key] = $listener; + } + } + } +} diff --git a/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php b/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php new file mode 100644 index 0000000000..12e2b1c67b --- /dev/null +++ b/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php @@ -0,0 +1,339 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Debug; + +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\Stopwatch\Stopwatch; +use Psr\Log\LoggerInterface; + +/** + * Collects some data about event listeners. + * + * This event dispatcher delegates the dispatching to another one. + * + * @author Fabien Potencier + */ +class TraceableEventDispatcher implements TraceableEventDispatcherInterface +{ + protected $logger; + protected $stopwatch; + + private $called; + private $dispatcher; + private $wrappedListeners; + + /** + * Constructor. + * + * @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance + * @param Stopwatch $stopwatch A Stopwatch instance + * @param LoggerInterface $logger A LoggerInterface instance + */ + public function __construct(EventDispatcherInterface $dispatcher, Stopwatch $stopwatch, LoggerInterface $logger = null) + { + $this->dispatcher = $dispatcher; + $this->stopwatch = $stopwatch; + $this->logger = $logger; + $this->called = array(); + $this->wrappedListeners = array(); + } + + /** + * {@inheritdoc} + */ + public function addListener($eventName, $listener, $priority = 0) + { + $this->dispatcher->addListener($eventName, $listener, $priority); + } + + /** + * {@inheritdoc} + */ + public function addSubscriber(EventSubscriberInterface $subscriber) + { + $this->dispatcher->addSubscriber($subscriber); + } + + /** + * {@inheritdoc} + */ + public function removeListener($eventName, $listener) + { + if (isset($this->wrappedListeners[$eventName])) { + foreach ($this->wrappedListeners[$eventName] as $index => $wrappedListener) { + if ($wrappedListener->getWrappedListener() === $listener) { + $listener = $wrappedListener; + unset($this->wrappedListeners[$eventName][$index]); + break; + } + } + } + + return $this->dispatcher->removeListener($eventName, $listener); + } + + /** + * {@inheritdoc} + */ + public function removeSubscriber(EventSubscriberInterface $subscriber) + { + return $this->dispatcher->removeSubscriber($subscriber); + } + + /** + * {@inheritdoc} + */ + public function getListeners($eventName = null) + { + return $this->dispatcher->getListeners($eventName); + } + + /** + * {@inheritdoc} + */ + public function hasListeners($eventName = null) + { + return $this->dispatcher->hasListeners($eventName); + } + + /** + * {@inheritdoc} + */ + public function dispatch($eventName, Event $event = null) + { + if (null === $event) { + $event = new Event(); + } + + if (null !== $this->logger && $event->isPropagationStopped()) { + $this->logger->debug(sprintf('The "%s" event is already stopped. No listeners have been called.', $eventName)); + } + + $this->preProcess($eventName); + $this->preDispatch($eventName, $event); + + $e = $this->stopwatch->start($eventName, 'section'); + + $this->dispatcher->dispatch($eventName, $event); + + if ($e->isStarted()) { + $e->stop(); + } + + $this->postDispatch($eventName, $event); + $this->postProcess($eventName); + + return $event; + } + + /** + * {@inheritdoc} + */ + public function getCalledListeners() + { + $called = array(); + foreach ($this->called as $eventName => $listeners) { + foreach ($listeners as $listener) { + $info = $this->getListenerInfo($listener->getWrappedListener(), $eventName); + $called[$eventName.'.'.$info['pretty']] = $info; + } + } + + return $called; + } + + /** + * {@inheritdoc} + */ + public function getNotCalledListeners() + { + try { + $allListeners = $this->getListeners(); + } catch (\Exception $e) { + if (null !== $this->logger) { + $this->logger->info('An exception was thrown while getting the uncalled listeners.', array('exception' => $e)); + } + + // unable to retrieve the uncalled listeners + return array(); + } + + $notCalled = array(); + foreach ($allListeners as $eventName => $listeners) { + foreach ($listeners as $listener) { + $called = false; + if (isset($this->called[$eventName])) { + foreach ($this->called[$eventName] as $l) { + if ($l->getWrappedListener() === $listener) { + $called = true; + + break; + } + } + } + + if (!$called) { + $info = $this->getListenerInfo($listener, $eventName); + $notCalled[$eventName.'.'.$info['pretty']] = $info; + } + } + } + + return $notCalled; + } + + /** + * Proxies all method calls to the original event dispatcher. + * + * @param string $method The method name + * @param array $arguments The method arguments + * + * @return mixed + */ + public function __call($method, $arguments) + { + return call_user_func_array(array($this->dispatcher, $method), $arguments); + } + + /** + * Called before dispatching the event. + * + * @param string $eventName The event name + * @param Event $event The event + */ + protected function preDispatch($eventName, Event $event) + { + } + + /** + * Called after dispatching the event. + * + * @param string $eventName The event name + * @param Event $event The event + */ + protected function postDispatch($eventName, Event $event) + { + } + + private function preProcess($eventName) + { + foreach ($this->dispatcher->getListeners($eventName) as $listener) { + $this->dispatcher->removeListener($eventName, $listener); + $info = $this->getListenerInfo($listener, $eventName); + $name = isset($info['class']) ? $info['class'] : $info['type']; + $wrappedListener = new WrappedListener($listener, $name, $this->stopwatch, $this); + $this->wrappedListeners[$eventName][] = $wrappedListener; + $this->dispatcher->addListener($eventName, $wrappedListener); + } + } + + private function postProcess($eventName) + { + unset($this->wrappedListeners[$eventName]); + $skipped = false; + foreach ($this->dispatcher->getListeners($eventName) as $listener) { + if (!$listener instanceof WrappedListener) { // #12845: a new listener was added during dispatch. + continue; + } + // Unwrap listener + $this->dispatcher->removeListener($eventName, $listener); + $this->dispatcher->addListener($eventName, $listener->getWrappedListener()); + + $info = $this->getListenerInfo($listener->getWrappedListener(), $eventName); + if ($listener->wasCalled()) { + if (null !== $this->logger) { + $this->logger->debug(sprintf('Notified event "%s" to listener "%s".', $eventName, $info['pretty'])); + } + + if (!isset($this->called[$eventName])) { + $this->called[$eventName] = new \SplObjectStorage(); + } + + $this->called[$eventName]->attach($listener); + } + + if (null !== $this->logger && $skipped) { + $this->logger->debug(sprintf('Listener "%s" was not called for event "%s".', $info['pretty'], $eventName)); + } + + if ($listener->stoppedPropagation()) { + if (null !== $this->logger) { + $this->logger->debug(sprintf('Listener "%s" stopped propagation of the event "%s".', $info['pretty'], $eventName)); + } + + $skipped = true; + } + } + } + + /** + * Returns information about the listener. + * + * @param object $listener The listener + * @param string $eventName The event name + * + * @return array Information about the listener + */ + private function getListenerInfo($listener, $eventName) + { + $info = array( + 'event' => $eventName, + ); + if ($listener instanceof \Closure) { + $info += array( + 'type' => 'Closure', + 'pretty' => 'closure', + ); + } elseif (is_string($listener)) { + try { + $r = new \ReflectionFunction($listener); + $file = $r->getFileName(); + $line = $r->getStartLine(); + } catch (\ReflectionException $e) { + $file = null; + $line = null; + } + $info += array( + 'type' => 'Function', + 'function' => $listener, + 'file' => $file, + 'line' => $line, + 'pretty' => $listener, + ); + } elseif (is_array($listener) || (is_object($listener) && is_callable($listener))) { + if (!is_array($listener)) { + $listener = array($listener, '__invoke'); + } + $class = is_object($listener[0]) ? get_class($listener[0]) : $listener[0]; + try { + $r = new \ReflectionMethod($class, $listener[1]); + $file = $r->getFileName(); + $line = $r->getStartLine(); + } catch (\ReflectionException $e) { + $file = null; + $line = null; + } + $info += array( + 'type' => 'Method', + 'class' => $class, + 'method' => $listener[1], + 'file' => $file, + 'line' => $line, + 'pretty' => $class.'::'.$listener[1], + ); + } + + return $info; + } +} diff --git a/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php b/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php new file mode 100644 index 0000000000..5483e81506 --- /dev/null +++ b/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Debug; + +use Symfony\Component\EventDispatcher\EventDispatcherInterface; + +/** + * @author Fabien Potencier + */ +interface TraceableEventDispatcherInterface extends EventDispatcherInterface +{ + /** + * Gets the called listeners. + * + * @return array An array of called listeners + */ + public function getCalledListeners(); + + /** + * Gets the not called listeners. + * + * @return array An array of not called listeners + */ + public function getNotCalledListeners(); +} diff --git a/vendor/symfony/event-dispatcher/Debug/WrappedListener.php b/vendor/symfony/event-dispatcher/Debug/WrappedListener.php new file mode 100644 index 0000000000..e16627d6ad --- /dev/null +++ b/vendor/symfony/event-dispatcher/Debug/WrappedListener.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Debug; + +use Symfony\Component\Stopwatch\Stopwatch; +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; + +/** + * @author Fabien Potencier + */ +class WrappedListener +{ + private $listener; + private $name; + private $called; + private $stoppedPropagation; + private $stopwatch; + private $dispatcher; + + public function __construct($listener, $name, Stopwatch $stopwatch, EventDispatcherInterface $dispatcher = null) + { + $this->listener = $listener; + $this->name = $name; + $this->stopwatch = $stopwatch; + $this->dispatcher = $dispatcher; + $this->called = false; + $this->stoppedPropagation = false; + } + + public function getWrappedListener() + { + return $this->listener; + } + + public function wasCalled() + { + return $this->called; + } + + public function stoppedPropagation() + { + return $this->stoppedPropagation; + } + + public function __invoke(Event $event, $eventName, EventDispatcherInterface $dispatcher) + { + $this->called = true; + + $e = $this->stopwatch->start($this->name, 'event_listener'); + + call_user_func($this->listener, $event, $eventName, $this->dispatcher ?: $dispatcher); + + if ($e->isStarted()) { + $e->stop(); + } + + if ($event->isPropagationStopped()) { + $this->stoppedPropagation = true; + } + } +} diff --git a/vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php b/vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php new file mode 100644 index 0000000000..ebfe435f87 --- /dev/null +++ b/vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php @@ -0,0 +1,109 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\DependencyInjection; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; + +/** + * Compiler pass to register tagged services for an event dispatcher. + */ +class RegisterListenersPass implements CompilerPassInterface +{ + /** + * @var string + */ + protected $dispatcherService; + + /** + * @var string + */ + protected $listenerTag; + + /** + * @var string + */ + protected $subscriberTag; + + /** + * Constructor. + * + * @param string $dispatcherService Service name of the event dispatcher in processed container + * @param string $listenerTag Tag name used for listener + * @param string $subscriberTag Tag name used for subscribers + */ + public function __construct($dispatcherService = 'event_dispatcher', $listenerTag = 'kernel.event_listener', $subscriberTag = 'kernel.event_subscriber') + { + $this->dispatcherService = $dispatcherService; + $this->listenerTag = $listenerTag; + $this->subscriberTag = $subscriberTag; + } + + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition($this->dispatcherService) && !$container->hasAlias($this->dispatcherService)) { + return; + } + + $definition = $container->findDefinition($this->dispatcherService); + + foreach ($container->findTaggedServiceIds($this->listenerTag) as $id => $events) { + $def = $container->getDefinition($id); + if (!$def->isPublic()) { + throw new \InvalidArgumentException(sprintf('The service "%s" must be public as event listeners are lazy-loaded.', $id)); + } + + if ($def->isAbstract()) { + throw new \InvalidArgumentException(sprintf('The service "%s" must not be abstract as event listeners are lazy-loaded.', $id)); + } + + foreach ($events as $event) { + $priority = isset($event['priority']) ? $event['priority'] : 0; + + if (!isset($event['event'])) { + throw new \InvalidArgumentException(sprintf('Service "%s" must define the "event" attribute on "%s" tags.', $id, $this->listenerTag)); + } + + if (!isset($event['method'])) { + $event['method'] = 'on'.preg_replace_callback(array( + '/(?<=\b)[a-z]/i', + '/[^a-z0-9]/i', + ), function ($matches) { return strtoupper($matches[0]); }, $event['event']); + $event['method'] = preg_replace('/[^a-z0-9]/i', '', $event['method']); + } + + $definition->addMethodCall('addListenerService', array($event['event'], array($id, $event['method']), $priority)); + } + } + + foreach ($container->findTaggedServiceIds($this->subscriberTag) as $id => $attributes) { + $def = $container->getDefinition($id); + if (!$def->isPublic()) { + throw new \InvalidArgumentException(sprintf('The service "%s" must be public as event subscribers are lazy-loaded.', $id)); + } + + if ($def->isAbstract()) { + throw new \InvalidArgumentException(sprintf('The service "%s" must not be abstract as event subscribers are lazy-loaded.', $id)); + } + + // We must assume that the class value has been correctly filled, even if the service is created by a factory + $class = $container->getParameterBag()->resolveValue($def->getClass()); + + $interface = 'Symfony\Component\EventDispatcher\EventSubscriberInterface'; + if (!is_subclass_of($class, $interface)) { + throw new \InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, $interface)); + } + + $definition->addMethodCall('addSubscriberService', array($id, $class)); + } + } +} diff --git a/vendor/symfony/event-dispatcher/Event.php b/vendor/symfony/event-dispatcher/Event.php new file mode 100644 index 0000000000..4a563495e3 --- /dev/null +++ b/vendor/symfony/event-dispatcher/Event.php @@ -0,0 +1,120 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * Event is the base class for classes containing event data. + * + * This class contains no event data. It is used by events that do not pass + * state information to an event handler when an event is raised. + * + * You can call the method stopPropagation() to abort the execution of + * further listeners in your event listener. + * + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Bernhard Schussek + */ +class Event +{ + /** + * @var bool Whether no further event listeners should be triggered + */ + private $propagationStopped = false; + + /** + * @var EventDispatcher Dispatcher that dispatched this event + */ + private $dispatcher; + + /** + * @var string This event's name + */ + private $name; + + /** + * Returns whether further event listeners should be triggered. + * + * @see Event::stopPropagation() + * + * @return bool Whether propagation was already stopped for this event. + */ + public function isPropagationStopped() + { + return $this->propagationStopped; + } + + /** + * Stops the propagation of the event to further event listeners. + * + * If multiple event listeners are connected to the same event, no + * further event listener will be triggered once any trigger calls + * stopPropagation(). + */ + public function stopPropagation() + { + $this->propagationStopped = true; + } + + /** + * Stores the EventDispatcher that dispatches this Event. + * + * @param EventDispatcherInterface $dispatcher + * + * @deprecated since version 2.4, to be removed in 3.0. The event dispatcher is passed to the listener call. + */ + public function setDispatcher(EventDispatcherInterface $dispatcher) + { + $this->dispatcher = $dispatcher; + } + + /** + * Returns the EventDispatcher that dispatches this Event. + * + * @return EventDispatcherInterface + * + * @deprecated since version 2.4, to be removed in 3.0. The event dispatcher is passed to the listener call. + */ + public function getDispatcher() + { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.4 and will be removed in 3.0. The event dispatcher instance can be received in the listener call instead.', E_USER_DEPRECATED); + + return $this->dispatcher; + } + + /** + * Gets the event's name. + * + * @return string + * + * @deprecated since version 2.4, to be removed in 3.0. The event name is passed to the listener call. + */ + public function getName() + { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.4 and will be removed in 3.0. The event name can be received in the listener call instead.', E_USER_DEPRECATED); + + return $this->name; + } + + /** + * Sets the event's name property. + * + * @param string $name The event name. + * + * @deprecated since version 2.4, to be removed in 3.0. The event name is passed to the listener call. + */ + public function setName($name) + { + $this->name = $name; + } +} diff --git a/vendor/symfony/event-dispatcher/EventDispatcher.php b/vendor/symfony/event-dispatcher/EventDispatcher.php new file mode 100644 index 0000000000..f1b63f709d --- /dev/null +++ b/vendor/symfony/event-dispatcher/EventDispatcher.php @@ -0,0 +1,177 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * The EventDispatcherInterface is the central point of Symfony's event listener system. + * + * Listeners are registered on the manager and events are dispatched through the + * manager. + * + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Bernhard Schussek + * @author Fabien Potencier + * @author Jordi Boggiano + * @author Jordan Alliot + */ +class EventDispatcher implements EventDispatcherInterface +{ + private $listeners = array(); + private $sorted = array(); + + /** + * {@inheritdoc} + */ + public function dispatch($eventName, Event $event = null) + { + if (null === $event) { + $event = new Event(); + } + + $event->setDispatcher($this); + $event->setName($eventName); + + if ($listeners = $this->getListeners($eventName)) { + $this->doDispatch($listeners, $eventName, $event); + } + + return $event; + } + + /** + * {@inheritdoc} + */ + public function getListeners($eventName = null) + { + if (null !== $eventName) { + if (!isset($this->listeners[$eventName])) { + return array(); + } + + if (!isset($this->sorted[$eventName])) { + $this->sortListeners($eventName); + } + + return $this->sorted[$eventName]; + } + + foreach ($this->listeners as $eventName => $eventListeners) { + if (!isset($this->sorted[$eventName])) { + $this->sortListeners($eventName); + } + } + + return array_filter($this->sorted); + } + + /** + * {@inheritdoc} + */ + public function hasListeners($eventName = null) + { + return (bool) count($this->getListeners($eventName)); + } + + /** + * {@inheritdoc} + */ + public function addListener($eventName, $listener, $priority = 0) + { + $this->listeners[$eventName][$priority][] = $listener; + unset($this->sorted[$eventName]); + } + + /** + * {@inheritdoc} + */ + public function removeListener($eventName, $listener) + { + if (!isset($this->listeners[$eventName])) { + return; + } + + foreach ($this->listeners[$eventName] as $priority => $listeners) { + if (false !== ($key = array_search($listener, $listeners, true))) { + unset($this->listeners[$eventName][$priority][$key], $this->sorted[$eventName]); + } + } + } + + /** + * {@inheritdoc} + */ + public function addSubscriber(EventSubscriberInterface $subscriber) + { + foreach ($subscriber->getSubscribedEvents() as $eventName => $params) { + if (is_string($params)) { + $this->addListener($eventName, array($subscriber, $params)); + } elseif (is_string($params[0])) { + $this->addListener($eventName, array($subscriber, $params[0]), isset($params[1]) ? $params[1] : 0); + } else { + foreach ($params as $listener) { + $this->addListener($eventName, array($subscriber, $listener[0]), isset($listener[1]) ? $listener[1] : 0); + } + } + } + } + + /** + * {@inheritdoc} + */ + public function removeSubscriber(EventSubscriberInterface $subscriber) + { + foreach ($subscriber->getSubscribedEvents() as $eventName => $params) { + if (is_array($params) && is_array($params[0])) { + foreach ($params as $listener) { + $this->removeListener($eventName, array($subscriber, $listener[0])); + } + } else { + $this->removeListener($eventName, array($subscriber, is_string($params) ? $params : $params[0])); + } + } + } + + /** + * Triggers the listeners of an event. + * + * This method can be overridden to add functionality that is executed + * for each listener. + * + * @param callable[] $listeners The event listeners. + * @param string $eventName The name of the event to dispatch. + * @param Event $event The event object to pass to the event handlers/listeners. + */ + protected function doDispatch($listeners, $eventName, Event $event) + { + foreach ($listeners as $listener) { + if ($event->isPropagationStopped()) { + break; + } + call_user_func($listener, $event, $eventName, $this); + } + } + + /** + * Sorts the internal list of listeners for the given event by priority. + * + * @param string $eventName The name of the event. + */ + private function sortListeners($eventName) + { + $this->sorted[$eventName] = array(); + + krsort($this->listeners[$eventName]); + $this->sorted[$eventName] = call_user_func_array('array_merge', $this->listeners[$eventName]); + } +} diff --git a/vendor/symfony/event-dispatcher/EventDispatcherInterface.php b/vendor/symfony/event-dispatcher/EventDispatcherInterface.php new file mode 100644 index 0000000000..a9bdd2c886 --- /dev/null +++ b/vendor/symfony/event-dispatcher/EventDispatcherInterface.php @@ -0,0 +1,88 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * The EventDispatcherInterface is the central point of Symfony's event listener system. + * Listeners are registered on the manager and events are dispatched through the + * manager. + * + * @author Bernhard Schussek + */ +interface EventDispatcherInterface +{ + /** + * Dispatches an event to all registered listeners. + * + * @param string $eventName The name of the event to dispatch. The name of + * the event is the name of the method that is + * invoked on listeners. + * @param Event $event The event to pass to the event handlers/listeners. + * If not supplied, an empty Event instance is created. + * + * @return Event + */ + public function dispatch($eventName, Event $event = null); + + /** + * Adds an event listener that listens on the specified events. + * + * @param string $eventName The event to listen on + * @param callable $listener The listener + * @param int $priority The higher this value, the earlier an event + * listener will be triggered in the chain (defaults to 0) + */ + public function addListener($eventName, $listener, $priority = 0); + + /** + * Adds an event subscriber. + * + * The subscriber is asked for all the events he is + * interested in and added as a listener for these events. + * + * @param EventSubscriberInterface $subscriber The subscriber. + */ + public function addSubscriber(EventSubscriberInterface $subscriber); + + /** + * Removes an event listener from the specified events. + * + * @param string $eventName The event to remove a listener from + * @param callable $listener The listener to remove + */ + public function removeListener($eventName, $listener); + + /** + * Removes an event subscriber. + * + * @param EventSubscriberInterface $subscriber The subscriber + */ + public function removeSubscriber(EventSubscriberInterface $subscriber); + + /** + * Gets the listeners of a specific event or all listeners sorted by descending priority. + * + * @param string $eventName The name of the event + * + * @return array The event listeners for the specified event, or all event listeners by event name + */ + public function getListeners($eventName = null); + + /** + * Checks whether an event has any registered listeners. + * + * @param string $eventName The name of the event + * + * @return bool true if the specified event has any listeners, false otherwise + */ + public function hasListeners($eventName = null); +} diff --git a/vendor/symfony/event-dispatcher/EventSubscriberInterface.php b/vendor/symfony/event-dispatcher/EventSubscriberInterface.php new file mode 100644 index 0000000000..8af778919b --- /dev/null +++ b/vendor/symfony/event-dispatcher/EventSubscriberInterface.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * An EventSubscriber knows himself what events he is interested in. + * If an EventSubscriber is added to an EventDispatcherInterface, the manager invokes + * {@link getSubscribedEvents} and registers the subscriber as a listener for all + * returned events. + * + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Bernhard Schussek + */ +interface EventSubscriberInterface +{ + /** + * Returns an array of event names this subscriber wants to listen to. + * + * The array keys are event names and the value can be: + * + * * The method name to call (priority defaults to 0) + * * An array composed of the method name to call and the priority + * * An array of arrays composed of the method names to call and respective + * priorities, or 0 if unset + * + * For instance: + * + * * array('eventName' => 'methodName') + * * array('eventName' => array('methodName', $priority)) + * * array('eventName' => array(array('methodName1', $priority), array('methodName2'))) + * + * @return array The event names to listen to + */ + public static function getSubscribedEvents(); +} diff --git a/vendor/symfony/event-dispatcher/GenericEvent.php b/vendor/symfony/event-dispatcher/GenericEvent.php new file mode 100644 index 0000000000..03cbcfe334 --- /dev/null +++ b/vendor/symfony/event-dispatcher/GenericEvent.php @@ -0,0 +1,186 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * Event encapsulation class. + * + * Encapsulates events thus decoupling the observer from the subject they encapsulate. + * + * @author Drak + */ +class GenericEvent extends Event implements \ArrayAccess, \IteratorAggregate +{ + /** + * Event subject. + * + * @var mixed usually object or callable + */ + protected $subject; + + /** + * Array of arguments. + * + * @var array + */ + protected $arguments; + + /** + * Encapsulate an event with $subject and $args. + * + * @param mixed $subject The subject of the event, usually an object. + * @param array $arguments Arguments to store in the event. + */ + public function __construct($subject = null, array $arguments = array()) + { + $this->subject = $subject; + $this->arguments = $arguments; + } + + /** + * Getter for subject property. + * + * @return mixed $subject The observer subject. + */ + public function getSubject() + { + return $this->subject; + } + + /** + * Get argument by key. + * + * @param string $key Key. + * + * @return mixed Contents of array key. + * + * @throws \InvalidArgumentException If key is not found. + */ + public function getArgument($key) + { + if ($this->hasArgument($key)) { + return $this->arguments[$key]; + } + + throw new \InvalidArgumentException(sprintf('Argument "%s" not found.', $key)); + } + + /** + * Add argument to event. + * + * @param string $key Argument name. + * @param mixed $value Value. + * + * @return GenericEvent + */ + public function setArgument($key, $value) + { + $this->arguments[$key] = $value; + + return $this; + } + + /** + * Getter for all arguments. + * + * @return array + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * Set args property. + * + * @param array $args Arguments. + * + * @return GenericEvent + */ + public function setArguments(array $args = array()) + { + $this->arguments = $args; + + return $this; + } + + /** + * Has argument. + * + * @param string $key Key of arguments array. + * + * @return bool + */ + public function hasArgument($key) + { + return array_key_exists($key, $this->arguments); + } + + /** + * ArrayAccess for argument getter. + * + * @param string $key Array key. + * + * @return mixed + * + * @throws \InvalidArgumentException If key does not exist in $this->args. + */ + public function offsetGet($key) + { + return $this->getArgument($key); + } + + /** + * ArrayAccess for argument setter. + * + * @param string $key Array key to set. + * @param mixed $value Value. + */ + public function offsetSet($key, $value) + { + $this->setArgument($key, $value); + } + + /** + * ArrayAccess for unset argument. + * + * @param string $key Array key. + */ + public function offsetUnset($key) + { + if ($this->hasArgument($key)) { + unset($this->arguments[$key]); + } + } + + /** + * ArrayAccess has argument. + * + * @param string $key Array key. + * + * @return bool + */ + public function offsetExists($key) + { + return $this->hasArgument($key); + } + + /** + * IteratorAggregate for iterating over the object like an array. + * + * @return \ArrayIterator + */ + public function getIterator() + { + return new \ArrayIterator($this->arguments); + } +} diff --git a/vendor/symfony/event-dispatcher/ImmutableEventDispatcher.php b/vendor/symfony/event-dispatcher/ImmutableEventDispatcher.php new file mode 100644 index 0000000000..7ef9ece757 --- /dev/null +++ b/vendor/symfony/event-dispatcher/ImmutableEventDispatcher.php @@ -0,0 +1,93 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * A read-only proxy for an event dispatcher. + * + * @author Bernhard Schussek + */ +class ImmutableEventDispatcher implements EventDispatcherInterface +{ + /** + * The proxied dispatcher. + * + * @var EventDispatcherInterface + */ + private $dispatcher; + + /** + * Creates an unmodifiable proxy for an event dispatcher. + * + * @param EventDispatcherInterface $dispatcher The proxied event dispatcher. + */ + public function __construct(EventDispatcherInterface $dispatcher) + { + $this->dispatcher = $dispatcher; + } + + /** + * {@inheritdoc} + */ + public function dispatch($eventName, Event $event = null) + { + return $this->dispatcher->dispatch($eventName, $event); + } + + /** + * {@inheritdoc} + */ + public function addListener($eventName, $listener, $priority = 0) + { + throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); + } + + /** + * {@inheritdoc} + */ + public function addSubscriber(EventSubscriberInterface $subscriber) + { + throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); + } + + /** + * {@inheritdoc} + */ + public function removeListener($eventName, $listener) + { + throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); + } + + /** + * {@inheritdoc} + */ + public function removeSubscriber(EventSubscriberInterface $subscriber) + { + throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); + } + + /** + * {@inheritdoc} + */ + public function getListeners($eventName = null) + { + return $this->dispatcher->getListeners($eventName); + } + + /** + * {@inheritdoc} + */ + public function hasListeners($eventName = null) + { + return $this->dispatcher->hasListeners($eventName); + } +} diff --git a/vendor/symfony/event-dispatcher/LICENSE b/vendor/symfony/event-dispatcher/LICENSE new file mode 100644 index 0000000000..12a74531e4 --- /dev/null +++ b/vendor/symfony/event-dispatcher/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2016 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/event-dispatcher/README.md b/vendor/symfony/event-dispatcher/README.md new file mode 100644 index 0000000000..185c3fecf8 --- /dev/null +++ b/vendor/symfony/event-dispatcher/README.md @@ -0,0 +1,15 @@ +EventDispatcher Component +========================= + +The EventDispatcher component provides tools that allow your application +components to communicate with each other by dispatching events and listening to +them. + +Resources +--------- + + * [Documentation](https://symfony.com/doc/current/components/event_dispatcher/index.html) + * [Contributing](https://symfony.com/doc/current/contributing/index.html) + * [Report issues](https://github.com/symfony/symfony/issues) and + [send Pull Requests](https://github.com/symfony/symfony/pulls) + in the [main Symfony repository](https://github.com/symfony/symfony) diff --git a/vendor/symfony/event-dispatcher/Tests/AbstractEventDispatcherTest.php b/vendor/symfony/event-dispatcher/Tests/AbstractEventDispatcherTest.php new file mode 100644 index 0000000000..2e4c3fd97f --- /dev/null +++ b/vendor/symfony/event-dispatcher/Tests/AbstractEventDispatcherTest.php @@ -0,0 +1,382 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests; + +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +abstract class AbstractEventDispatcherTest extends \PHPUnit_Framework_TestCase +{ + /* Some pseudo events */ + const preFoo = 'pre.foo'; + const postFoo = 'post.foo'; + const preBar = 'pre.bar'; + const postBar = 'post.bar'; + + /** + * @var EventDispatcher + */ + private $dispatcher; + + private $listener; + + protected function setUp() + { + $this->dispatcher = $this->createEventDispatcher(); + $this->listener = new TestEventListener(); + } + + protected function tearDown() + { + $this->dispatcher = null; + $this->listener = null; + } + + abstract protected function createEventDispatcher(); + + public function testInitialState() + { + $this->assertEquals(array(), $this->dispatcher->getListeners()); + $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); + $this->assertFalse($this->dispatcher->hasListeners(self::postFoo)); + } + + public function testAddListener() + { + $this->dispatcher->addListener('pre.foo', array($this->listener, 'preFoo')); + $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo')); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); + $this->assertCount(1, $this->dispatcher->getListeners(self::preFoo)); + $this->assertCount(1, $this->dispatcher->getListeners(self::postFoo)); + $this->assertCount(2, $this->dispatcher->getListeners()); + } + + public function testGetListenersSortsByPriority() + { + $listener1 = new TestEventListener(); + $listener2 = new TestEventListener(); + $listener3 = new TestEventListener(); + $listener1->name = '1'; + $listener2->name = '2'; + $listener3->name = '3'; + + $this->dispatcher->addListener('pre.foo', array($listener1, 'preFoo'), -10); + $this->dispatcher->addListener('pre.foo', array($listener2, 'preFoo'), 10); + $this->dispatcher->addListener('pre.foo', array($listener3, 'preFoo')); + + $expected = array( + array($listener2, 'preFoo'), + array($listener3, 'preFoo'), + array($listener1, 'preFoo'), + ); + + $this->assertSame($expected, $this->dispatcher->getListeners('pre.foo')); + } + + public function testGetAllListenersSortsByPriority() + { + $listener1 = new TestEventListener(); + $listener2 = new TestEventListener(); + $listener3 = new TestEventListener(); + $listener4 = new TestEventListener(); + $listener5 = new TestEventListener(); + $listener6 = new TestEventListener(); + + $this->dispatcher->addListener('pre.foo', $listener1, -10); + $this->dispatcher->addListener('pre.foo', $listener2); + $this->dispatcher->addListener('pre.foo', $listener3, 10); + $this->dispatcher->addListener('post.foo', $listener4, -10); + $this->dispatcher->addListener('post.foo', $listener5); + $this->dispatcher->addListener('post.foo', $listener6, 10); + + $expected = array( + 'pre.foo' => array($listener3, $listener2, $listener1), + 'post.foo' => array($listener6, $listener5, $listener4), + ); + + $this->assertSame($expected, $this->dispatcher->getListeners()); + } + + public function testDispatch() + { + $this->dispatcher->addListener('pre.foo', array($this->listener, 'preFoo')); + $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo')); + $this->dispatcher->dispatch(self::preFoo); + $this->assertTrue($this->listener->preFooInvoked); + $this->assertFalse($this->listener->postFooInvoked); + $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch('noevent')); + $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch(self::preFoo)); + $event = new Event(); + $return = $this->dispatcher->dispatch(self::preFoo, $event); + $this->assertSame($event, $return); + } + + /** + * @group legacy + */ + public function testLegacyDispatch() + { + $event = new Event(); + $return = $this->dispatcher->dispatch(self::preFoo, $event); + $this->assertEquals('pre.foo', $event->getName()); + } + + public function testDispatchForClosure() + { + $invoked = 0; + $listener = function () use (&$invoked) { + ++$invoked; + }; + $this->dispatcher->addListener('pre.foo', $listener); + $this->dispatcher->addListener('post.foo', $listener); + $this->dispatcher->dispatch(self::preFoo); + $this->assertEquals(1, $invoked); + } + + public function testStopEventPropagation() + { + $otherListener = new TestEventListener(); + + // postFoo() stops the propagation, so only one listener should + // be executed + // Manually set priority to enforce $this->listener to be called first + $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo'), 10); + $this->dispatcher->addListener('post.foo', array($otherListener, 'preFoo')); + $this->dispatcher->dispatch(self::postFoo); + $this->assertTrue($this->listener->postFooInvoked); + $this->assertFalse($otherListener->postFooInvoked); + } + + public function testDispatchByPriority() + { + $invoked = array(); + $listener1 = function () use (&$invoked) { + $invoked[] = '1'; + }; + $listener2 = function () use (&$invoked) { + $invoked[] = '2'; + }; + $listener3 = function () use (&$invoked) { + $invoked[] = '3'; + }; + $this->dispatcher->addListener('pre.foo', $listener1, -10); + $this->dispatcher->addListener('pre.foo', $listener2); + $this->dispatcher->addListener('pre.foo', $listener3, 10); + $this->dispatcher->dispatch(self::preFoo); + $this->assertEquals(array('3', '2', '1'), $invoked); + } + + public function testRemoveListener() + { + $this->dispatcher->addListener('pre.bar', $this->listener); + $this->assertTrue($this->dispatcher->hasListeners(self::preBar)); + $this->dispatcher->removeListener('pre.bar', $this->listener); + $this->assertFalse($this->dispatcher->hasListeners(self::preBar)); + $this->dispatcher->removeListener('notExists', $this->listener); + } + + public function testAddSubscriber() + { + $eventSubscriber = new TestEventSubscriber(); + $this->dispatcher->addSubscriber($eventSubscriber); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); + } + + public function testAddSubscriberWithPriorities() + { + $eventSubscriber = new TestEventSubscriber(); + $this->dispatcher->addSubscriber($eventSubscriber); + + $eventSubscriber = new TestEventSubscriberWithPriorities(); + $this->dispatcher->addSubscriber($eventSubscriber); + + $listeners = $this->dispatcher->getListeners('pre.foo'); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertCount(2, $listeners); + $this->assertInstanceOf('Symfony\Component\EventDispatcher\Tests\TestEventSubscriberWithPriorities', $listeners[0][0]); + } + + public function testAddSubscriberWithMultipleListeners() + { + $eventSubscriber = new TestEventSubscriberWithMultipleListeners(); + $this->dispatcher->addSubscriber($eventSubscriber); + + $listeners = $this->dispatcher->getListeners('pre.foo'); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertCount(2, $listeners); + $this->assertEquals('preFoo2', $listeners[0][1]); + } + + public function testRemoveSubscriber() + { + $eventSubscriber = new TestEventSubscriber(); + $this->dispatcher->addSubscriber($eventSubscriber); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); + $this->dispatcher->removeSubscriber($eventSubscriber); + $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); + $this->assertFalse($this->dispatcher->hasListeners(self::postFoo)); + } + + public function testRemoveSubscriberWithPriorities() + { + $eventSubscriber = new TestEventSubscriberWithPriorities(); + $this->dispatcher->addSubscriber($eventSubscriber); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->dispatcher->removeSubscriber($eventSubscriber); + $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); + } + + public function testRemoveSubscriberWithMultipleListeners() + { + $eventSubscriber = new TestEventSubscriberWithMultipleListeners(); + $this->dispatcher->addSubscriber($eventSubscriber); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertCount(2, $this->dispatcher->getListeners(self::preFoo)); + $this->dispatcher->removeSubscriber($eventSubscriber); + $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); + } + + /** + * @group legacy + */ + public function testLegacyEventReceivesTheDispatcherInstance() + { + $dispatcher = null; + $this->dispatcher->addListener('test', function ($event) use (&$dispatcher) { + $dispatcher = $event->getDispatcher(); + }); + $this->dispatcher->dispatch('test'); + $this->assertSame($this->dispatcher, $dispatcher); + } + + public function testEventReceivesTheDispatcherInstanceAsArgument() + { + $listener = new TestWithDispatcher(); + $this->dispatcher->addListener('test', array($listener, 'foo')); + $this->assertNull($listener->name); + $this->assertNull($listener->dispatcher); + $this->dispatcher->dispatch('test'); + $this->assertEquals('test', $listener->name); + $this->assertSame($this->dispatcher, $listener->dispatcher); + } + + /** + * @see https://bugs.php.net/bug.php?id=62976 + * + * This bug affects: + * - The PHP 5.3 branch for versions < 5.3.18 + * - The PHP 5.4 branch for versions < 5.4.8 + * - The PHP 5.5 branch is not affected + */ + public function testWorkaroundForPhpBug62976() + { + $dispatcher = $this->createEventDispatcher(); + $dispatcher->addListener('bug.62976', new CallableClass()); + $dispatcher->removeListener('bug.62976', function () {}); + $this->assertTrue($dispatcher->hasListeners('bug.62976')); + } + + public function testHasListenersWhenAddedCallbackListenerIsRemoved() + { + $listener = function () {}; + $this->dispatcher->addListener('foo', $listener); + $this->dispatcher->removeListener('foo', $listener); + $this->assertFalse($this->dispatcher->hasListeners()); + } + + public function testGetListenersWhenAddedCallbackListenerIsRemoved() + { + $listener = function () {}; + $this->dispatcher->addListener('foo', $listener); + $this->dispatcher->removeListener('foo', $listener); + $this->assertSame(array(), $this->dispatcher->getListeners()); + } + + public function testHasListenersWithoutEventsReturnsFalseAfterHasListenersWithEventHasBeenCalled() + { + $this->assertFalse($this->dispatcher->hasListeners('foo')); + $this->assertFalse($this->dispatcher->hasListeners()); + } +} + +class CallableClass +{ + public function __invoke() + { + } +} + +class TestEventListener +{ + public $preFooInvoked = false; + public $postFooInvoked = false; + + /* Listener methods */ + + public function preFoo(Event $e) + { + $this->preFooInvoked = true; + } + + public function postFoo(Event $e) + { + $this->postFooInvoked = true; + + $e->stopPropagation(); + } +} + +class TestWithDispatcher +{ + public $name; + public $dispatcher; + + public function foo(Event $e, $name, $dispatcher) + { + $this->name = $name; + $this->dispatcher = $dispatcher; + } +} + +class TestEventSubscriber implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array('pre.foo' => 'preFoo', 'post.foo' => 'postFoo'); + } +} + +class TestEventSubscriberWithPriorities implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array( + 'pre.foo' => array('preFoo', 10), + 'post.foo' => array('postFoo'), + ); + } +} + +class TestEventSubscriberWithMultipleListeners implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array('pre.foo' => array( + array('preFoo1'), + array('preFoo2', 10), + )); + } +} diff --git a/vendor/symfony/event-dispatcher/Tests/ContainerAwareEventDispatcherTest.php b/vendor/symfony/event-dispatcher/Tests/ContainerAwareEventDispatcherTest.php new file mode 100644 index 0000000000..0f3f5ba766 --- /dev/null +++ b/vendor/symfony/event-dispatcher/Tests/ContainerAwareEventDispatcherTest.php @@ -0,0 +1,273 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests; + +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\DependencyInjection\Scope; +use Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher; +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +class ContainerAwareEventDispatcherTest extends AbstractEventDispatcherTest +{ + protected function createEventDispatcher() + { + $container = new Container(); + + return new ContainerAwareEventDispatcher($container); + } + + public function testAddAListenerService() + { + $event = new Event(); + + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $service + ->expects($this->once()) + ->method('onEvent') + ->with($event) + ; + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $dispatcher->dispatch('onEvent', $event); + } + + public function testAddASubscriberService() + { + $event = new Event(); + + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\SubscriberService'); + + $service + ->expects($this->once()) + ->method('onEvent') + ->with($event) + ; + + $service + ->expects($this->once()) + ->method('onEventWithPriority') + ->with($event) + ; + + $service + ->expects($this->once()) + ->method('onEventNested') + ->with($event) + ; + + $container = new Container(); + $container->set('service.subscriber', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addSubscriberService('service.subscriber', 'Symfony\Component\EventDispatcher\Tests\SubscriberService'); + + $dispatcher->dispatch('onEvent', $event); + $dispatcher->dispatch('onEventWithPriority', $event); + $dispatcher->dispatch('onEventNested', $event); + } + + public function testPreventDuplicateListenerService() + { + $event = new Event(); + + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $service + ->expects($this->once()) + ->method('onEvent') + ->with($event) + ; + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'), 5); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'), 10); + + $dispatcher->dispatch('onEvent', $event); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testTriggerAListenerServiceOutOfScope() + { + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $scope = new Scope('scope'); + $container = new Container(); + $container->addScope($scope); + $container->enterScope('scope'); + + $container->set('service.listener', $service, 'scope'); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $container->leaveScope('scope'); + $dispatcher->dispatch('onEvent'); + } + + public function testReEnteringAScope() + { + $event = new Event(); + + $service1 = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $service1 + ->expects($this->exactly(2)) + ->method('onEvent') + ->with($event) + ; + + $scope = new Scope('scope'); + $container = new Container(); + $container->addScope($scope); + $container->enterScope('scope'); + + $container->set('service.listener', $service1, 'scope'); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + $dispatcher->dispatch('onEvent', $event); + + $service2 = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $service2 + ->expects($this->once()) + ->method('onEvent') + ->with($event) + ; + + $container->enterScope('scope'); + $container->set('service.listener', $service2, 'scope'); + + $dispatcher->dispatch('onEvent', $event); + + $container->leaveScope('scope'); + + $dispatcher->dispatch('onEvent'); + } + + public function testHasListenersOnLazyLoad() + { + $event = new Event(); + + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $event->setDispatcher($dispatcher); + $event->setName('onEvent'); + + $service + ->expects($this->once()) + ->method('onEvent') + ->with($event) + ; + + $this->assertTrue($dispatcher->hasListeners()); + + if ($dispatcher->hasListeners('onEvent')) { + $dispatcher->dispatch('onEvent'); + } + } + + public function testGetListenersOnLazyLoad() + { + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $listeners = $dispatcher->getListeners(); + + $this->assertTrue(isset($listeners['onEvent'])); + + $this->assertCount(1, $dispatcher->getListeners('onEvent')); + } + + public function testRemoveAfterDispatch() + { + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $dispatcher->dispatch('onEvent', new Event()); + $dispatcher->removeListener('onEvent', array($container->get('service.listener'), 'onEvent')); + $this->assertFalse($dispatcher->hasListeners('onEvent')); + } + + public function testRemoveBeforeDispatch() + { + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $dispatcher->removeListener('onEvent', array($container->get('service.listener'), 'onEvent')); + $this->assertFalse($dispatcher->hasListeners('onEvent')); + } +} + +class Service +{ + public function onEvent(Event $e) + { + } +} + +class SubscriberService implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array( + 'onEvent' => 'onEvent', + 'onEventWithPriority' => array('onEventWithPriority', 10), + 'onEventNested' => array(array('onEventNested')), + ); + } + + public function onEvent(Event $e) + { + } + + public function onEventWithPriority(Event $e) + { + } + + public function onEventNested(Event $e) + { + } +} diff --git a/vendor/symfony/event-dispatcher/Tests/Debug/TraceableEventDispatcherTest.php b/vendor/symfony/event-dispatcher/Tests/Debug/TraceableEventDispatcherTest.php new file mode 100644 index 0000000000..4aa6226e49 --- /dev/null +++ b/vendor/symfony/event-dispatcher/Tests/Debug/TraceableEventDispatcherTest.php @@ -0,0 +1,199 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests\Debug; + +use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\Stopwatch\Stopwatch; + +class TraceableEventDispatcherTest extends \PHPUnit_Framework_TestCase +{ + public function testAddRemoveListener() + { + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); + + $tdispatcher->addListener('foo', $listener = function () {; }); + $listeners = $dispatcher->getListeners('foo'); + $this->assertCount(1, $listeners); + $this->assertSame($listener, $listeners[0]); + + $tdispatcher->removeListener('foo', $listener); + $this->assertCount(0, $dispatcher->getListeners('foo')); + } + + public function testGetListeners() + { + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); + + $tdispatcher->addListener('foo', $listener = function () {; }); + $this->assertSame($dispatcher->getListeners('foo'), $tdispatcher->getListeners('foo')); + } + + public function testHasListeners() + { + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); + + $this->assertFalse($dispatcher->hasListeners('foo')); + $this->assertFalse($tdispatcher->hasListeners('foo')); + + $tdispatcher->addListener('foo', $listener = function () {; }); + $this->assertTrue($dispatcher->hasListeners('foo')); + $this->assertTrue($tdispatcher->hasListeners('foo')); + } + + public function testAddRemoveSubscriber() + { + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); + + $subscriber = new EventSubscriber(); + + $tdispatcher->addSubscriber($subscriber); + $listeners = $dispatcher->getListeners('foo'); + $this->assertCount(1, $listeners); + $this->assertSame(array($subscriber, 'call'), $listeners[0]); + + $tdispatcher->removeSubscriber($subscriber); + $this->assertCount(0, $dispatcher->getListeners('foo')); + } + + public function testGetCalledListeners() + { + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); + $tdispatcher->addListener('foo', $listener = function () {; }); + + $this->assertEquals(array(), $tdispatcher->getCalledListeners()); + $this->assertEquals(array('foo.closure' => array('event' => 'foo', 'type' => 'Closure', 'pretty' => 'closure')), $tdispatcher->getNotCalledListeners()); + + $tdispatcher->dispatch('foo'); + + $this->assertEquals(array('foo.closure' => array('event' => 'foo', 'type' => 'Closure', 'pretty' => 'closure')), $tdispatcher->getCalledListeners()); + $this->assertEquals(array(), $tdispatcher->getNotCalledListeners()); + } + + public function testGetCalledListenersNested() + { + $tdispatcher = null; + $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); + $dispatcher->addListener('foo', function (Event $event, $eventName, $dispatcher) use (&$tdispatcher) { + $tdispatcher = $dispatcher; + $dispatcher->dispatch('bar'); + }); + $dispatcher->addListener('bar', function (Event $event) {}); + $dispatcher->dispatch('foo'); + $this->assertSame($dispatcher, $tdispatcher); + $this->assertCount(2, $dispatcher->getCalledListeners()); + } + + public function testLogger() + { + $logger = $this->getMock('Psr\Log\LoggerInterface'); + + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch(), $logger); + $tdispatcher->addListener('foo', $listener1 = function () {; }); + $tdispatcher->addListener('foo', $listener2 = function () {; }); + + $logger->expects($this->at(0))->method('debug')->with('Notified event "foo" to listener "closure".'); + $logger->expects($this->at(1))->method('debug')->with('Notified event "foo" to listener "closure".'); + + $tdispatcher->dispatch('foo'); + } + + public function testLoggerWithStoppedEvent() + { + $logger = $this->getMock('Psr\Log\LoggerInterface'); + + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch(), $logger); + $tdispatcher->addListener('foo', $listener1 = function (Event $event) { $event->stopPropagation(); }); + $tdispatcher->addListener('foo', $listener2 = function () {; }); + + $logger->expects($this->at(0))->method('debug')->with('Notified event "foo" to listener "closure".'); + $logger->expects($this->at(1))->method('debug')->with('Listener "closure" stopped propagation of the event "foo".'); + $logger->expects($this->at(2))->method('debug')->with('Listener "closure" was not called for event "foo".'); + + $tdispatcher->dispatch('foo'); + } + + public function testDispatchCallListeners() + { + $called = array(); + + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); + $tdispatcher->addListener('foo', $listener1 = function () use (&$called) { $called[] = 'foo1'; }); + $tdispatcher->addListener('foo', $listener2 = function () use (&$called) { $called[] = 'foo2'; }); + + $tdispatcher->dispatch('foo'); + + $this->assertEquals(array('foo1', 'foo2'), $called); + } + + public function testDispatchNested() + { + $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); + $loop = 1; + $dispatcher->addListener('foo', $listener1 = function () use ($dispatcher, &$loop) { + ++$loop; + if (2 == $loop) { + $dispatcher->dispatch('foo'); + } + }); + + $dispatcher->dispatch('foo'); + } + + public function testDispatchReusedEventNested() + { + $nestedCall = false; + $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); + $dispatcher->addListener('foo', function (Event $e) use ($dispatcher) { + $dispatcher->dispatch('bar', $e); + }); + $dispatcher->addListener('bar', function (Event $e) use (&$nestedCall) { + $nestedCall = true; + }); + + $this->assertFalse($nestedCall); + $dispatcher->dispatch('foo'); + $this->assertTrue($nestedCall); + } + + public function testListenerCanRemoveItselfWhenExecuted() + { + $eventDispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); + $listener1 = function ($event, $eventName, EventDispatcherInterface $dispatcher) use (&$listener1) { + $dispatcher->removeListener('foo', $listener1); + }; + $eventDispatcher->addListener('foo', $listener1); + $eventDispatcher->addListener('foo', function () {}); + $eventDispatcher->dispatch('foo'); + + $this->assertCount(1, $eventDispatcher->getListeners('foo'), 'expected listener1 to be removed'); + } +} + +class EventSubscriber implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array('foo' => 'call'); + } +} diff --git a/vendor/symfony/event-dispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php b/vendor/symfony/event-dispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php new file mode 100644 index 0000000000..0fdd6372bd --- /dev/null +++ b/vendor/symfony/event-dispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php @@ -0,0 +1,200 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests\DependencyInjection; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass; + +class RegisterListenersPassTest extends \PHPUnit_Framework_TestCase +{ + /** + * Tests that event subscribers not implementing EventSubscriberInterface + * trigger an exception. + * + * @expectedException \InvalidArgumentException + */ + public function testEventSubscriberWithoutInterface() + { + // one service, not implementing any interface + $services = array( + 'my_event_subscriber' => array(0 => array()), + ); + + $definition = $this->getMock('Symfony\Component\DependencyInjection\Definition'); + $definition->expects($this->atLeastOnce()) + ->method('isPublic') + ->will($this->returnValue(true)); + $definition->expects($this->atLeastOnce()) + ->method('getClass') + ->will($this->returnValue('stdClass')); + + $builder = $this->getMock( + 'Symfony\Component\DependencyInjection\ContainerBuilder', + array('hasDefinition', 'findTaggedServiceIds', 'getDefinition') + ); + $builder->expects($this->any()) + ->method('hasDefinition') + ->will($this->returnValue(true)); + + // We don't test kernel.event_listener here + $builder->expects($this->atLeastOnce()) + ->method('findTaggedServiceIds') + ->will($this->onConsecutiveCalls(array(), $services)); + + $builder->expects($this->atLeastOnce()) + ->method('getDefinition') + ->will($this->returnValue($definition)); + + $registerListenersPass = new RegisterListenersPass(); + $registerListenersPass->process($builder); + } + + public function testValidEventSubscriber() + { + $services = array( + 'my_event_subscriber' => array(0 => array()), + ); + + $definition = $this->getMock('Symfony\Component\DependencyInjection\Definition'); + $definition->expects($this->atLeastOnce()) + ->method('isPublic') + ->will($this->returnValue(true)); + $definition->expects($this->atLeastOnce()) + ->method('getClass') + ->will($this->returnValue('Symfony\Component\EventDispatcher\Tests\DependencyInjection\SubscriberService')); + + $builder = $this->getMock( + 'Symfony\Component\DependencyInjection\ContainerBuilder', + array('hasDefinition', 'findTaggedServiceIds', 'getDefinition', 'findDefinition') + ); + $builder->expects($this->any()) + ->method('hasDefinition') + ->will($this->returnValue(true)); + + // We don't test kernel.event_listener here + $builder->expects($this->atLeastOnce()) + ->method('findTaggedServiceIds') + ->will($this->onConsecutiveCalls(array(), $services)); + + $builder->expects($this->atLeastOnce()) + ->method('getDefinition') + ->will($this->returnValue($definition)); + + $builder->expects($this->atLeastOnce()) + ->method('findDefinition') + ->will($this->returnValue($definition)); + + $registerListenersPass = new RegisterListenersPass(); + $registerListenersPass->process($builder); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The service "foo" must be public as event listeners are lazy-loaded. + */ + public function testPrivateEventListener() + { + $container = new ContainerBuilder(); + $container->register('foo', 'stdClass')->setPublic(false)->addTag('kernel.event_listener', array()); + $container->register('event_dispatcher', 'stdClass'); + + $registerListenersPass = new RegisterListenersPass(); + $registerListenersPass->process($container); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The service "foo" must be public as event subscribers are lazy-loaded. + */ + public function testPrivateEventSubscriber() + { + $container = new ContainerBuilder(); + $container->register('foo', 'stdClass')->setPublic(false)->addTag('kernel.event_subscriber', array()); + $container->register('event_dispatcher', 'stdClass'); + + $registerListenersPass = new RegisterListenersPass(); + $registerListenersPass->process($container); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The service "foo" must not be abstract as event listeners are lazy-loaded. + */ + public function testAbstractEventListener() + { + $container = new ContainerBuilder(); + $container->register('foo', 'stdClass')->setAbstract(true)->addTag('kernel.event_listener', array()); + $container->register('event_dispatcher', 'stdClass'); + + $registerListenersPass = new RegisterListenersPass(); + $registerListenersPass->process($container); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The service "foo" must not be abstract as event subscribers are lazy-loaded. + */ + public function testAbstractEventSubscriber() + { + $container = new ContainerBuilder(); + $container->register('foo', 'stdClass')->setAbstract(true)->addTag('kernel.event_subscriber', array()); + $container->register('event_dispatcher', 'stdClass'); + + $registerListenersPass = new RegisterListenersPass(); + $registerListenersPass->process($container); + } + + public function testEventSubscriberResolvableClassName() + { + $container = new ContainerBuilder(); + + $container->setParameter('subscriber.class', 'Symfony\Component\EventDispatcher\Tests\DependencyInjection\SubscriberService'); + $container->register('foo', '%subscriber.class%')->addTag('kernel.event_subscriber', array()); + $container->register('event_dispatcher', 'stdClass'); + + $registerListenersPass = new RegisterListenersPass(); + $registerListenersPass->process($container); + + $definition = $container->getDefinition('event_dispatcher'); + $expected_calls = array( + array( + 'addSubscriberService', + array( + 'foo', + 'Symfony\Component\EventDispatcher\Tests\DependencyInjection\SubscriberService', + ), + ), + ); + $this->assertSame($expected_calls, $definition->getMethodCalls()); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage You have requested a non-existent parameter "subscriber.class" + */ + public function testEventSubscriberUnresolvableClassName() + { + $container = new ContainerBuilder(); + $container->register('foo', '%subscriber.class%')->addTag('kernel.event_subscriber', array()); + $container->register('event_dispatcher', 'stdClass'); + + $registerListenersPass = new RegisterListenersPass(); + $registerListenersPass->process($container); + } +} + +class SubscriberService implements \Symfony\Component\EventDispatcher\EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + } +} diff --git a/vendor/symfony/event-dispatcher/Tests/EventDispatcherTest.php b/vendor/symfony/event-dispatcher/Tests/EventDispatcherTest.php new file mode 100644 index 0000000000..5faa5c8be8 --- /dev/null +++ b/vendor/symfony/event-dispatcher/Tests/EventDispatcherTest.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests; + +use Symfony\Component\EventDispatcher\EventDispatcher; + +class EventDispatcherTest extends AbstractEventDispatcherTest +{ + protected function createEventDispatcher() + { + return new EventDispatcher(); + } +} diff --git a/vendor/symfony/event-dispatcher/Tests/EventTest.php b/vendor/symfony/event-dispatcher/Tests/EventTest.php new file mode 100644 index 0000000000..9a822670cd --- /dev/null +++ b/vendor/symfony/event-dispatcher/Tests/EventTest.php @@ -0,0 +1,96 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests; + +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\EventDispatcher; + +/** + * Test class for Event. + */ +class EventTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Symfony\Component\EventDispatcher\Event + */ + protected $event; + + /** + * @var \Symfony\Component\EventDispatcher\EventDispatcher + */ + protected $dispatcher; + + /** + * Sets up the fixture, for example, opens a network connection. + * This method is called before a test is executed. + */ + protected function setUp() + { + $this->event = new Event(); + $this->dispatcher = new EventDispatcher(); + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + $this->event = null; + $this->dispatcher = null; + } + + public function testIsPropagationStopped() + { + $this->assertFalse($this->event->isPropagationStopped()); + } + + public function testStopPropagationAndIsPropagationStopped() + { + $this->event->stopPropagation(); + $this->assertTrue($this->event->isPropagationStopped()); + } + + /** + * @group legacy + */ + public function testLegacySetDispatcher() + { + $this->event->setDispatcher($this->dispatcher); + $this->assertSame($this->dispatcher, $this->event->getDispatcher()); + } + + /** + * @group legacy + */ + public function testLegacyGetDispatcher() + { + $this->assertNull($this->event->getDispatcher()); + } + + /** + * @group legacy + */ + public function testLegacyGetName() + { + $this->assertNull($this->event->getName()); + } + + /** + * @group legacy + */ + public function testLegacySetName() + { + $this->event->setName('foo'); + $this->assertEquals('foo', $this->event->getName()); + } +} diff --git a/vendor/symfony/event-dispatcher/Tests/GenericEventTest.php b/vendor/symfony/event-dispatcher/Tests/GenericEventTest.php new file mode 100644 index 0000000000..aebd82dabd --- /dev/null +++ b/vendor/symfony/event-dispatcher/Tests/GenericEventTest.php @@ -0,0 +1,139 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests; + +use Symfony\Component\EventDispatcher\GenericEvent; + +/** + * Test class for Event. + */ +class GenericEventTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var GenericEvent + */ + private $event; + + private $subject; + + /** + * Prepares the environment before running a test. + */ + protected function setUp() + { + parent::setUp(); + + $this->subject = new \stdClass(); + $this->event = new GenericEvent($this->subject, array('name' => 'Event')); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->subject = null; + $this->event = null; + + parent::tearDown(); + } + + public function testConstruct() + { + $this->assertEquals($this->event, new GenericEvent($this->subject, array('name' => 'Event'))); + } + + /** + * Tests Event->getArgs(). + */ + public function testGetArguments() + { + // test getting all + $this->assertSame(array('name' => 'Event'), $this->event->getArguments()); + } + + public function testSetArguments() + { + $result = $this->event->setArguments(array('foo' => 'bar')); + $this->assertAttributeSame(array('foo' => 'bar'), 'arguments', $this->event); + $this->assertSame($this->event, $result); + } + + public function testSetArgument() + { + $result = $this->event->setArgument('foo2', 'bar2'); + $this->assertAttributeSame(array('name' => 'Event', 'foo2' => 'bar2'), 'arguments', $this->event); + $this->assertEquals($this->event, $result); + } + + public function testGetArgument() + { + // test getting key + $this->assertEquals('Event', $this->event->getArgument('name')); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testGetArgException() + { + $this->event->getArgument('nameNotExist'); + } + + public function testOffsetGet() + { + // test getting key + $this->assertEquals('Event', $this->event['name']); + + // test getting invalid arg + $this->setExpectedException('InvalidArgumentException'); + $this->assertFalse($this->event['nameNotExist']); + } + + public function testOffsetSet() + { + $this->event['foo2'] = 'bar2'; + $this->assertAttributeSame(array('name' => 'Event', 'foo2' => 'bar2'), 'arguments', $this->event); + } + + public function testOffsetUnset() + { + unset($this->event['name']); + $this->assertAttributeSame(array(), 'arguments', $this->event); + } + + public function testOffsetIsset() + { + $this->assertTrue(isset($this->event['name'])); + $this->assertFalse(isset($this->event['nameNotExist'])); + } + + public function testHasArgument() + { + $this->assertTrue($this->event->hasArgument('name')); + $this->assertFalse($this->event->hasArgument('nameNotExist')); + } + + public function testGetSubject() + { + $this->assertSame($this->subject, $this->event->getSubject()); + } + + public function testHasIterator() + { + $data = array(); + foreach ($this->event as $key => $value) { + $data[$key] = $value; + } + $this->assertEquals(array('name' => 'Event'), $data); + } +} diff --git a/vendor/symfony/event-dispatcher/Tests/ImmutableEventDispatcherTest.php b/vendor/symfony/event-dispatcher/Tests/ImmutableEventDispatcherTest.php new file mode 100644 index 0000000000..80a7e43be6 --- /dev/null +++ b/vendor/symfony/event-dispatcher/Tests/ImmutableEventDispatcherTest.php @@ -0,0 +1,105 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests; + +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\ImmutableEventDispatcher; + +/** + * @author Bernhard Schussek + */ +class ImmutableEventDispatcherTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $innerDispatcher; + + /** + * @var ImmutableEventDispatcher + */ + private $dispatcher; + + protected function setUp() + { + $this->innerDispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $this->dispatcher = new ImmutableEventDispatcher($this->innerDispatcher); + } + + public function testDispatchDelegates() + { + $event = new Event(); + + $this->innerDispatcher->expects($this->once()) + ->method('dispatch') + ->with('event', $event) + ->will($this->returnValue('result')); + + $this->assertSame('result', $this->dispatcher->dispatch('event', $event)); + } + + public function testGetListenersDelegates() + { + $this->innerDispatcher->expects($this->once()) + ->method('getListeners') + ->with('event') + ->will($this->returnValue('result')); + + $this->assertSame('result', $this->dispatcher->getListeners('event')); + } + + public function testHasListenersDelegates() + { + $this->innerDispatcher->expects($this->once()) + ->method('hasListeners') + ->with('event') + ->will($this->returnValue('result')); + + $this->assertSame('result', $this->dispatcher->hasListeners('event')); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testAddListenerDisallowed() + { + $this->dispatcher->addListener('event', function () { return 'foo'; }); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testAddSubscriberDisallowed() + { + $subscriber = $this->getMock('Symfony\Component\EventDispatcher\EventSubscriberInterface'); + + $this->dispatcher->addSubscriber($subscriber); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testRemoveListenerDisallowed() + { + $this->dispatcher->removeListener('event', function () { return 'foo'; }); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testRemoveSubscriberDisallowed() + { + $subscriber = $this->getMock('Symfony\Component\EventDispatcher\EventSubscriberInterface'); + + $this->dispatcher->removeSubscriber($subscriber); + } +} diff --git a/vendor/symfony/event-dispatcher/composer.json b/vendor/symfony/event-dispatcher/composer.json new file mode 100644 index 0000000000..3a20c35f38 --- /dev/null +++ b/vendor/symfony/event-dispatcher/composer.json @@ -0,0 +1,44 @@ +{ + "name": "symfony/event-dispatcher", + "type": "library", + "description": "Symfony EventDispatcher Component", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.9" + }, + "require-dev": { + "symfony/dependency-injection": "~2.6", + "symfony/expression-language": "~2.6", + "symfony/config": "~2.0,>=2.0.5", + "symfony/stopwatch": "~2.3", + "psr/log": "~1.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\EventDispatcher\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + } +} diff --git a/vendor/symfony/event-dispatcher/phpunit.xml.dist b/vendor/symfony/event-dispatcher/phpunit.xml.dist new file mode 100644 index 0000000000..ae0586e0b3 --- /dev/null +++ b/vendor/symfony/event-dispatcher/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/polyfill-mbstring/LICENSE b/vendor/symfony/polyfill-mbstring/LICENSE new file mode 100644 index 0000000000..39fa189d2b --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014-2016 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/polyfill-mbstring/Mbstring.php b/vendor/symfony/polyfill-mbstring/Mbstring.php new file mode 100644 index 0000000000..97e8c9b46c --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/Mbstring.php @@ -0,0 +1,664 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Mbstring; + +/** + * Partial mbstring implementation in PHP, iconv based, UTF-8 centric. + * + * Implemented: + * - mb_chr - Returns a specific character from its Unicode code point + * - mb_convert_encoding - Convert character encoding + * - mb_convert_variables - Convert character code in variable(s) + * - mb_decode_mimeheader - Decode string in MIME header field + * - mb_encode_mimeheader - Encode string for MIME header XXX NATIVE IMPLEMENTATION IS REALLY BUGGED + * - mb_convert_case - Perform case folding on a string + * - mb_get_info - Get internal settings of mbstring + * - mb_http_input - Detect HTTP input character encoding + * - mb_http_output - Set/Get HTTP output character encoding + * - mb_internal_encoding - Set/Get internal character encoding + * - mb_list_encodings - Returns an array of all supported encodings + * - mb_ord - Returns the Unicode code point of a character + * - mb_output_handler - Callback function converts character encoding in output buffer + * - mb_scrub - Replaces ill-formed byte sequences with substitute characters + * - mb_strlen - Get string length + * - mb_strpos - Find position of first occurrence of string in a string + * - mb_strrpos - Find position of last occurrence of a string in a string + * - mb_strtolower - Make a string lowercase + * - mb_strtoupper - Make a string uppercase + * - mb_substitute_character - Set/Get substitution character + * - mb_substr - Get part of string + * - mb_stripos - Finds position of first occurrence of a string within another, case insensitive + * - mb_stristr - Finds first occurrence of a string within another, case insensitive + * - mb_strrchr - Finds the last occurrence of a character in a string within another + * - mb_strrichr - Finds the last occurrence of a character in a string within another, case insensitive + * - mb_strripos - Finds position of last occurrence of a string within another, case insensitive + * - mb_strstr - Finds first occurrence of a string within anothers + * - mb_strwidth - Return width of string + * - mb_substr_count - Count the number of substring occurrences + * + * Not implemented: + * - mb_convert_kana - Convert "kana" one from another ("zen-kaku", "han-kaku" and more) + * - mb_decode_numericentity - Decode HTML numeric string reference to character + * - mb_encode_numericentity - Encode character to HTML numeric string reference + * - mb_ereg_* - Regular expression with multibyte support + * - mb_parse_str - Parse GET/POST/COOKIE data and set global variable + * - mb_preferred_mime_name - Get MIME charset string + * - mb_regex_encoding - Returns current encoding for multibyte regex as string + * - mb_regex_set_options - Set/Get the default options for mbregex functions + * - mb_send_mail - Send encoded mail + * - mb_split - Split multibyte string using regular expression + * - mb_strcut - Get part of string + * - mb_strimwidth - Get truncated string with specified width + * + * @author Nicolas Grekas + * + * @internal + */ +final class Mbstring +{ + const MB_CASE_FOLD = PHP_INT_MAX; + + private static $encodingList = array('ASCII', 'UTF-8'); + private static $language = 'neutral'; + private static $internalEncoding = 'UTF-8'; + private static $caseFold = array( + array('µ','ſ',"\xCD\x85",'ς',"\xCF\x90","\xCF\x91","\xCF\x95","\xCF\x96","\xCF\xB0","\xCF\xB1","\xCF\xB5","\xE1\xBA\x9B","\xE1\xBE\xBE"), + array('μ','s','ι', 'σ','β', 'θ', 'φ', 'π', 'κ', 'ρ', 'ε', "\xE1\xB9\xA1",'ι'), + ); + + public static function mb_convert_encoding($s, $toEncoding, $fromEncoding = null) + { + if (is_array($fromEncoding) || false !== strpos($fromEncoding, ',')) { + $fromEncoding = self::mb_detect_encoding($s, $fromEncoding); + } else { + $fromEncoding = self::getEncoding($fromEncoding); + } + + $toEncoding = self::getEncoding($toEncoding); + + if ('BASE64' === $fromEncoding) { + $s = base64_decode($s); + $fromEncoding = $toEncoding; + } + + if ('BASE64' === $toEncoding) { + return base64_encode($s); + } + + if ('HTML-ENTITIES' === $toEncoding || 'HTML' === $toEncoding) { + if ('HTML-ENTITIES' === $fromEncoding || 'HTML' === $fromEncoding) { + $fromEncoding = 'Windows-1252'; + } + if ('UTF-8' !== $fromEncoding) { + $s = iconv($fromEncoding, 'UTF-8//IGNORE', $s); + } + + return preg_replace_callback('/[\x80-\xFF]+/', array(__CLASS__, 'html_encoding_callback'), $s); + } + + if ('HTML-ENTITIES' === $fromEncoding) { + $s = html_entity_decode($s, ENT_COMPAT, 'UTF-8'); + $fromEncoding = 'UTF-8'; + } + + return iconv($fromEncoding, $toEncoding.'//IGNORE', $s); + } + + public static function mb_convert_variables($toEncoding, $fromEncoding, &$a = null, &$b = null, &$c = null, &$d = null, &$e = null, &$f = null) + { + $vars = array(&$a, &$b, &$c, &$d, &$e, &$f); + + $ok = true; + array_walk_recursive($vars, function (&$v) use (&$ok, $toEncoding, $fromEncoding) { + if (false === $v = Mbstring::mb_convert_encoding($v, $toEncoding, $fromEncoding)) { + $ok = false; + } + }); + + return $ok ? $fromEncoding : false; + } + + public static function mb_decode_mimeheader($s) + { + return iconv_mime_decode($s, 2, self::$internalEncoding); + } + + public static function mb_encode_mimeheader($s, $charset = null, $transferEncoding = null, $linefeed = null, $indent = null) + { + trigger_error('mb_encode_mimeheader() is bugged. Please use iconv_mime_encode() instead', E_USER_WARNING); + } + + public static function mb_convert_case($s, $mode, $encoding = null) + { + if ('' === $s .= '') { + return ''; + } + + $encoding = self::getEncoding($encoding); + + if ('UTF-8' === $encoding) { + $encoding = null; + if (!preg_match('//u', $s)) { + $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s); + } + } else { + $s = iconv($encoding, 'UTF-8//IGNORE', $s); + } + + if (MB_CASE_TITLE == $mode) { + $s = preg_replace_callback('/\b\p{Ll}/u', array(__CLASS__, 'title_case_upper'), $s); + $s = preg_replace_callback('/\B[\p{Lu}\p{Lt}]+/u', array(__CLASS__, 'title_case_lower'), $s); + } else { + if (MB_CASE_UPPER == $mode) { + static $upper = null; + if (null === $upper) { + $upper = self::getData('upperCase'); + } + $map = $upper; + } else { + if (self::MB_CASE_FOLD === $mode) { + $s = str_replace(self::$caseFold[0], self::$caseFold[1], $s); + } + + static $lower = null; + if (null === $lower) { + $lower = self::getData('lowerCase'); + } + $map = $lower; + } + + static $ulenMask = array("\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4); + + $i = 0; + $len = strlen($s); + + while ($i < $len) { + $ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"]; + $uchr = substr($s, $i, $ulen); + $i += $ulen; + + if (isset($map[$uchr])) { + $uchr = $map[$uchr]; + $nlen = strlen($uchr); + + if ($nlen == $ulen) { + $nlen = $i; + do { + $s[--$nlen] = $uchr[--$ulen]; + } while ($ulen); + } else { + $s = substr_replace($s, $uchr, $i - $ulen, $ulen); + $len += $nlen - $ulen; + $i += $nlen - $ulen; + } + } + } + } + + if (null === $encoding) { + return $s; + } + + return iconv('UTF-8', $encoding.'//IGNORE', $s); + } + + public static function mb_internal_encoding($encoding = null) + { + if (null === $encoding) { + return self::$internalEncoding; + } + + $encoding = self::getEncoding($encoding); + + if ('UTF-8' === $encoding || false !== @iconv($encoding, $encoding, ' ')) { + self::$internalEncoding = $encoding; + + return true; + } + + return false; + } + + public static function mb_language($lang = null) + { + if (null === $lang) { + return self::$language; + } + + switch ($lang = strtolower($lang)) { + case 'uni': + case 'neutral': + self::$language = $lang; + + return true; + } + + return false; + } + + public static function mb_list_encodings() + { + return array('UTF-8'); + } + + public static function mb_encoding_aliases($encoding) + { + switch (strtoupper($encoding)) { + case 'UTF8': + case 'UTF-8': + return array('utf8'); + } + + return false; + } + + public static function mb_check_encoding($var = null, $encoding = null) + { + if (null === $encoding) { + if (null === $var) { + return false; + } + $encoding = self::$internalEncoding; + } + + return self::mb_detect_encoding($var, array($encoding)) || false !== @iconv($encoding, $encoding, $var); + } + + public static function mb_detect_encoding($str, $encodingList = null, $strict = false) + { + if (null === $encodingList) { + $encodingList = self::$encodingList; + } else { + if (!is_array($encodingList)) { + $encodingList = array_map('trim', explode(',', $encodingList)); + } + $encodingList = array_map('strtoupper', $encodingList); + } + + foreach ($encodingList as $enc) { + switch ($enc) { + case 'ASCII': + if (!preg_match('/[\x80-\xFF]/', $str)) { + return $enc; + } + break; + + case 'UTF8': + case 'UTF-8': + if (preg_match('//u', $str)) { + return 'UTF-8'; + } + break; + + default: + if (0 === strncmp($enc, 'ISO-8859-', 9)) { + return $enc; + } + } + } + + return false; + } + + public static function mb_detect_order($encodingList = null) + { + if (null === $encodingList) { + return self::$encodingList; + } + + if (!is_array($encodingList)) { + $encodingList = array_map('trim', explode(',', $encodingList)); + } + $encodingList = array_map('strtoupper', $encodingList); + + foreach ($encodingList as $enc) { + switch ($enc) { + default: + if (strncmp($enc, 'ISO-8859-', 9)) { + return false; + } + case 'ASCII': + case 'UTF8': + case 'UTF-8': + } + } + + self::$encodingList = $encodingList; + + return true; + } + + public static function mb_strlen($s, $encoding = null) + { + $encoding = self::getEncoding($encoding); + if ('CP850' === $encoding || 'ASCII' === $encoding) { + return strlen($s); + } + + return @iconv_strlen($s, $encoding); + } + + public static function mb_strpos($haystack, $needle, $offset = 0, $encoding = null) + { + $encoding = self::getEncoding($encoding); + if ('CP850' === $encoding || 'ASCII' === $encoding) { + return strpos($haystack, $needle, $offset); + } + + if ('' === $needle .= '') { + trigger_error(__METHOD__.': Empty delimiter', E_USER_WARNING); + + return false; + } + + return iconv_strpos($haystack, $needle, $offset, $encoding); + } + + public static function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null) + { + $encoding = self::getEncoding($encoding); + if ('CP850' === $encoding || 'ASCII' === $encoding) { + return strrpos($haystack, $needle, $offset); + } + + if ($offset != (int) $offset) { + $offset = 0; + } elseif ($offset = (int) $offset) { + if ($offset < 0) { + $haystack = self::mb_substr($haystack, 0, $offset, $encoding); + $offset = 0; + } else { + $haystack = self::mb_substr($haystack, $offset, 2147483647, $encoding); + } + } + + $pos = iconv_strrpos($haystack, $needle, $encoding); + + return false !== $pos ? $offset + $pos : false; + } + + public static function mb_strtolower($s, $encoding = null) + { + return self::mb_convert_case($s, MB_CASE_LOWER, $encoding); + } + + public static function mb_strtoupper($s, $encoding = null) + { + return self::mb_convert_case($s, MB_CASE_UPPER, $encoding); + } + + public static function mb_substitute_character($c = null) + { + if (0 === strcasecmp($c, 'none')) { + return true; + } + + return null !== $c ? false : 'none'; + } + + public static function mb_substr($s, $start, $length = null, $encoding = null) + { + $encoding = self::getEncoding($encoding); + if ('CP850' === $encoding || 'ASCII' === $encoding) { + return substr($s, $start, null === $length ? 2147483647 : $length); + } + + if ($start < 0) { + $start = iconv_strlen($s, $encoding) + $start; + if ($start < 0) { + $start = 0; + } + } + + if (null === $length) { + $length = 2147483647; + } elseif ($length < 0) { + $length = iconv_strlen($s, $encoding) + $length - $start; + if ($length < 0) { + return ''; + } + } + + return iconv_substr($s, $start, $length, $encoding).''; + } + + public static function mb_stripos($haystack, $needle, $offset = 0, $encoding = null) + { + $haystack = self::mb_convert_case($haystack, self::MB_CASE_FOLD, $encoding); + $needle = self::mb_convert_case($needle, self::MB_CASE_FOLD, $encoding); + + return self::mb_strpos($haystack, $needle, $offset, $encoding); + } + + public static function mb_stristr($haystack, $needle, $part = false, $encoding = null) + { + $pos = self::mb_stripos($haystack, $needle, 0, $encoding); + + return self::getSubpart($pos, $part, $haystack, $encoding); + } + + public static function mb_strrchr($haystack, $needle, $part = false, $encoding = null) + { + $encoding = self::getEncoding($encoding); + if ('CP850' === $encoding || 'ASCII' === $encoding) { + return strrchr($haystack, $needle, $part); + } + $needle = self::mb_substr($needle, 0, 1, $encoding); + $pos = iconv_strrpos($haystack, $needle, $encoding); + + return self::getSubpart($pos, $part, $haystack, $encoding); + } + + public static function mb_strrichr($haystack, $needle, $part = false, $encoding = null) + { + $needle = self::mb_substr($needle, 0, 1, $encoding); + $pos = self::mb_strripos($haystack, $needle, $encoding); + + return self::getSubpart($pos, $part, $haystack, $encoding); + } + + public static function mb_strripos($haystack, $needle, $offset = 0, $encoding = null) + { + $haystack = self::mb_convert_case($haystack, self::MB_CASE_FOLD, $encoding); + $needle = self::mb_convert_case($needle, self::MB_CASE_FOLD, $encoding); + + return self::mb_strrpos($haystack, $needle, $offset, $encoding); + } + + public static function mb_strstr($haystack, $needle, $part = false, $encoding = null) + { + $pos = strpos($haystack, $needle); + if (false === $pos) { + return false; + } + if ($part) { + return substr($haystack, 0, $pos); + } + + return substr($haystack, $pos); + } + + public static function mb_get_info($type = 'all') + { + $info = array( + 'internal_encoding' => self::$internalEncoding, + 'http_output' => 'pass', + 'http_output_conv_mimetypes' => '^(text/|application/xhtml\+xml)', + 'func_overload' => 0, + 'func_overload_list' => 'no overload', + 'mail_charset' => 'UTF-8', + 'mail_header_encoding' => 'BASE64', + 'mail_body_encoding' => 'BASE64', + 'illegal_chars' => 0, + 'encoding_translation' => 'Off', + 'language' => self::$language, + 'detect_order' => self::$encodingList, + 'substitute_character' => 'none', + 'strict_detection' => 'Off', + ); + + if ('all' === $type) { + return $info; + } + if (isset($info[$type])) { + return $info[$type]; + } + + return false; + } + + public static function mb_http_input($type = '') + { + return false; + } + + public static function mb_http_output($encoding = null) + { + return null !== $encoding ? 'pass' === $encoding : 'pass'; + } + + public static function mb_strwidth($s, $encoding = null) + { + $encoding = self::getEncoding($encoding); + + if ('UTF-8' !== $encoding) { + $s = iconv($encoding, 'UTF-8//IGNORE', $s); + } + + $s = preg_replace('/[\x{1100}-\x{115F}\x{2329}\x{232A}\x{2E80}-\x{303E}\x{3040}-\x{A4CF}\x{AC00}-\x{D7A3}\x{F900}-\x{FAFF}\x{FE10}-\x{FE19}\x{FE30}-\x{FE6F}\x{FF00}-\x{FF60}\x{FFE0}-\x{FFE6}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}]/u', '', $s, -1, $wide); + + return ($wide << 1) + iconv_strlen($s, 'UTF-8'); + } + + public static function mb_substr_count($haystack, $needle, $encoding = null) + { + return substr_count($haystack, $needle); + } + + public static function mb_output_handler($contents, $status) + { + return $contents; + } + + public static function mb_chr($code, $encoding = null) + { + if (0x80 > $code %= 0x200000) { + $s = chr($code); + } elseif (0x800 > $code) { + $s = chr(0xC0 | $code >> 6).chr(0x80 | $code & 0x3F); + } elseif (0x10000 > $code) { + $s = chr(0xE0 | $code >> 12).chr(0x80 | $code >> 6 & 0x3F).chr(0x80 | $code & 0x3F); + } else { + $s = chr(0xF0 | $code >> 18).chr(0x80 | $code >> 12 & 0x3F).chr(0x80 | $code >> 6 & 0x3F).chr(0x80 | $code & 0x3F); + } + + if ('UTF-8' !== $encoding = self::getEncoding($encoding)) { + $s = mb_convert_encoding($s, $encoding, 'UTF-8'); + } + + return $s; + } + + public static function mb_ord($s, $encoding = null) + { + if ('UTF-8' !== $encoding = self::getEncoding($encoding)) { + $s = mb_convert_encoding($s, 'UTF-8', $encoding); + } + + $code = ($s = unpack('C*', substr($s, 0, 4))) ? $s[1] : 0; + if (0xF0 <= $code) { + return (($code - 0xF0) << 18) + (($s[2] - 0x80) << 12) + (($s[3] - 0x80) << 6) + $s[4] - 0x80; + } + if (0xE0 <= $code) { + return (($code - 0xE0) << 12) + (($s[2] - 0x80) << 6) + $s[3] - 0x80; + } + if (0xC0 <= $code) { + return (($code - 0xC0) << 6) + $s[2] - 0x80; + } + + return $code; + } + + private static function getSubpart($pos, $part, $haystack, $encoding) + { + if (false === $pos) { + return false; + } + if ($part) { + return self::mb_substr($haystack, 0, $pos, $encoding); + } + + return self::mb_substr($haystack, $pos, null, $encoding); + } + + private static function html_encoding_callback($m) + { + $i = 1; + $entities = ''; + $m = unpack('C*', htmlentities($m[0], ENT_COMPAT, 'UTF-8')); + + while (isset($m[$i])) { + if (0x80 > $m[$i]) { + $entities .= chr($m[$i++]); + continue; + } + if (0xF0 <= $m[$i]) { + $c = (($m[$i++] - 0xF0) << 18) + (($m[$i++] - 0x80) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80; + } elseif (0xE0 <= $m[$i]) { + $c = (($m[$i++] - 0xE0) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80; + } else { + $c = (($m[$i++] - 0xC0) << 6) + $m[$i++] - 0x80; + } + + $entities .= '&#'.$c.';'; + } + + return $entities; + } + + private static function title_case_lower($s) + { + return self::mb_convert_case($s[0], MB_CASE_LOWER, 'UTF-8'); + } + + private static function title_case_upper($s) + { + return self::mb_convert_case($s[0], MB_CASE_UPPER, 'UTF-8'); + } + + private static function getData($file) + { + if (file_exists($file = __DIR__.'/Resources/unidata/'.$file.'.php')) { + return require $file; + } + + return false; + } + + private static function getEncoding($encoding) + { + if (null === $encoding) { + return self::$internalEncoding; + } + + $encoding = strtoupper($encoding); + + if ('8BIT' === $encoding || 'BINARY' === $encoding) { + return 'CP850'; + } + if ('UTF8' === $encoding) { + return 'UTF-8'; + } + + return $encoding; + } +} diff --git a/vendor/symfony/polyfill-mbstring/README.md b/vendor/symfony/polyfill-mbstring/README.md new file mode 100644 index 0000000000..342e8286db --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/README.md @@ -0,0 +1,13 @@ +Symfony Polyfill / Mbstring +=========================== + +This component provides a partial, native PHP implementation for the +[Mbstring](http://php.net/mbstring) extension. + +More information can be found in the +[main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md). + +License +======= + +This library is released under the [MIT license](LICENSE). diff --git a/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php b/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php new file mode 100644 index 0000000000..3ca16416a8 --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php @@ -0,0 +1,1101 @@ + 'a', + 'B' => 'b', + 'C' => 'c', + 'D' => 'd', + 'E' => 'e', + 'F' => 'f', + 'G' => 'g', + 'H' => 'h', + 'I' => 'i', + 'J' => 'j', + 'K' => 'k', + 'L' => 'l', + 'M' => 'm', + 'N' => 'n', + 'O' => 'o', + 'P' => 'p', + 'Q' => 'q', + 'R' => 'r', + 'S' => 's', + 'T' => 't', + 'U' => 'u', + 'V' => 'v', + 'W' => 'w', + 'X' => 'x', + 'Y' => 'y', + 'Z' => 'z', + 'À' => 'à', + 'Á' => 'á', + 'Â' => 'â', + 'Ã' => 'ã', + 'Ä' => 'ä', + 'Å' => 'å', + 'Æ' => 'æ', + 'Ç' => 'ç', + 'È' => 'è', + 'É' => 'é', + 'Ê' => 'ê', + 'Ë' => 'ë', + 'Ì' => 'ì', + 'Í' => 'í', + 'Î' => 'î', + 'Ï' => 'ï', + 'Ð' => 'ð', + 'Ñ' => 'ñ', + 'Ò' => 'ò', + 'Ó' => 'ó', + 'Ô' => 'ô', + 'Õ' => 'õ', + 'Ö' => 'ö', + 'Ø' => 'ø', + 'Ù' => 'ù', + 'Ú' => 'ú', + 'Û' => 'û', + 'Ü' => 'ü', + 'Ý' => 'ý', + 'Þ' => 'þ', + 'Ā' => 'ā', + 'Ă' => 'ă', + 'Ą' => 'ą', + 'Ć' => 'ć', + 'Ĉ' => 'ĉ', + 'Ċ' => 'ċ', + 'Č' => 'č', + 'Ď' => 'ď', + 'Đ' => 'đ', + 'Ē' => 'ē', + 'Ĕ' => 'ĕ', + 'Ė' => 'ė', + 'Ę' => 'ę', + 'Ě' => 'ě', + 'Ĝ' => 'ĝ', + 'Ğ' => 'ğ', + 'Ġ' => 'ġ', + 'Ģ' => 'ģ', + 'Ĥ' => 'ĥ', + 'Ħ' => 'ħ', + 'Ĩ' => 'ĩ', + 'Ī' => 'ī', + 'Ĭ' => 'ĭ', + 'Į' => 'į', + 'İ' => 'i', + 'IJ' => 'ij', + 'Ĵ' => 'ĵ', + 'Ķ' => 'ķ', + 'Ĺ' => 'ĺ', + 'Ļ' => 'ļ', + 'Ľ' => 'ľ', + 'Ŀ' => 'ŀ', + 'Ł' => 'ł', + 'Ń' => 'ń', + 'Ņ' => 'ņ', + 'Ň' => 'ň', + 'Ŋ' => 'ŋ', + 'Ō' => 'ō', + 'Ŏ' => 'ŏ', + 'Ő' => 'ő', + 'Œ' => 'œ', + 'Ŕ' => 'ŕ', + 'Ŗ' => 'ŗ', + 'Ř' => 'ř', + 'Ś' => 'ś', + 'Ŝ' => 'ŝ', + 'Ş' => 'ş', + 'Š' => 'š', + 'Ţ' => 'ţ', + 'Ť' => 'ť', + 'Ŧ' => 'ŧ', + 'Ũ' => 'ũ', + 'Ū' => 'ū', + 'Ŭ' => 'ŭ', + 'Ů' => 'ů', + 'Ű' => 'ű', + 'Ų' => 'ų', + 'Ŵ' => 'ŵ', + 'Ŷ' => 'ŷ', + 'Ÿ' => 'ÿ', + 'Ź' => 'ź', + 'Ż' => 'ż', + 'Ž' => 'ž', + 'Ɓ' => 'ɓ', + 'Ƃ' => 'ƃ', + 'Ƅ' => 'ƅ', + 'Ɔ' => 'ɔ', + 'Ƈ' => 'ƈ', + 'Ɖ' => 'ɖ', + 'Ɗ' => 'ɗ', + 'Ƌ' => 'ƌ', + 'Ǝ' => 'ǝ', + 'Ə' => 'ə', + 'Ɛ' => 'ɛ', + 'Ƒ' => 'ƒ', + 'Ɠ' => 'ɠ', + 'Ɣ' => 'ɣ', + 'Ɩ' => 'ɩ', + 'Ɨ' => 'ɨ', + 'Ƙ' => 'ƙ', + 'Ɯ' => 'ɯ', + 'Ɲ' => 'ɲ', + 'Ɵ' => 'ɵ', + 'Ơ' => 'ơ', + 'Ƣ' => 'ƣ', + 'Ƥ' => 'ƥ', + 'Ʀ' => 'ʀ', + 'Ƨ' => 'ƨ', + 'Ʃ' => 'ʃ', + 'Ƭ' => 'ƭ', + 'Ʈ' => 'ʈ', + 'Ư' => 'ư', + 'Ʊ' => 'ʊ', + 'Ʋ' => 'ʋ', + 'Ƴ' => 'ƴ', + 'Ƶ' => 'ƶ', + 'Ʒ' => 'ʒ', + 'Ƹ' => 'ƹ', + 'Ƽ' => 'ƽ', + 'DŽ' => 'dž', + 'Dž' => 'dž', + 'LJ' => 'lj', + 'Lj' => 'lj', + 'NJ' => 'nj', + 'Nj' => 'nj', + 'Ǎ' => 'ǎ', + 'Ǐ' => 'ǐ', + 'Ǒ' => 'ǒ', + 'Ǔ' => 'ǔ', + 'Ǖ' => 'ǖ', + 'Ǘ' => 'ǘ', + 'Ǚ' => 'ǚ', + 'Ǜ' => 'ǜ', + 'Ǟ' => 'ǟ', + 'Ǡ' => 'ǡ', + 'Ǣ' => 'ǣ', + 'Ǥ' => 'ǥ', + 'Ǧ' => 'ǧ', + 'Ǩ' => 'ǩ', + 'Ǫ' => 'ǫ', + 'Ǭ' => 'ǭ', + 'Ǯ' => 'ǯ', + 'DZ' => 'dz', + 'Dz' => 'dz', + 'Ǵ' => 'ǵ', + 'Ƕ' => 'ƕ', + 'Ƿ' => 'ƿ', + 'Ǹ' => 'ǹ', + 'Ǻ' => 'ǻ', + 'Ǽ' => 'ǽ', + 'Ǿ' => 'ǿ', + 'Ȁ' => 'ȁ', + 'Ȃ' => 'ȃ', + 'Ȅ' => 'ȅ', + 'Ȇ' => 'ȇ', + 'Ȉ' => 'ȉ', + 'Ȋ' => 'ȋ', + 'Ȍ' => 'ȍ', + 'Ȏ' => 'ȏ', + 'Ȑ' => 'ȑ', + 'Ȓ' => 'ȓ', + 'Ȕ' => 'ȕ', + 'Ȗ' => 'ȗ', + 'Ș' => 'ș', + 'Ț' => 'ț', + 'Ȝ' => 'ȝ', + 'Ȟ' => 'ȟ', + 'Ƞ' => 'ƞ', + 'Ȣ' => 'ȣ', + 'Ȥ' => 'ȥ', + 'Ȧ' => 'ȧ', + 'Ȩ' => 'ȩ', + 'Ȫ' => 'ȫ', + 'Ȭ' => 'ȭ', + 'Ȯ' => 'ȯ', + 'Ȱ' => 'ȱ', + 'Ȳ' => 'ȳ', + 'Ⱥ' => 'ⱥ', + 'Ȼ' => 'ȼ', + 'Ƚ' => 'ƚ', + 'Ⱦ' => 'ⱦ', + 'Ɂ' => 'ɂ', + 'Ƀ' => 'ƀ', + 'Ʉ' => 'ʉ', + 'Ʌ' => 'ʌ', + 'Ɇ' => 'ɇ', + 'Ɉ' => 'ɉ', + 'Ɋ' => 'ɋ', + 'Ɍ' => 'ɍ', + 'Ɏ' => 'ɏ', + 'Ͱ' => 'ͱ', + 'Ͳ' => 'ͳ', + 'Ͷ' => 'ͷ', + 'Ϳ' => 'ϳ', + 'Ά' => 'ά', + 'Έ' => 'έ', + 'Ή' => 'ή', + 'Ί' => 'ί', + 'Ό' => 'ό', + 'Ύ' => 'ύ', + 'Ώ' => 'ώ', + 'Α' => 'α', + 'Β' => 'β', + 'Γ' => 'γ', + 'Δ' => 'δ', + 'Ε' => 'ε', + 'Ζ' => 'ζ', + 'Η' => 'η', + 'Θ' => 'θ', + 'Ι' => 'ι', + 'Κ' => 'κ', + 'Λ' => 'λ', + 'Μ' => 'μ', + 'Ν' => 'ν', + 'Ξ' => 'ξ', + 'Ο' => 'ο', + 'Π' => 'π', + 'Ρ' => 'ρ', + 'Σ' => 'σ', + 'Τ' => 'τ', + 'Υ' => 'υ', + 'Φ' => 'φ', + 'Χ' => 'χ', + 'Ψ' => 'ψ', + 'Ω' => 'ω', + 'Ϊ' => 'ϊ', + 'Ϋ' => 'ϋ', + 'Ϗ' => 'ϗ', + 'Ϙ' => 'ϙ', + 'Ϛ' => 'ϛ', + 'Ϝ' => 'ϝ', + 'Ϟ' => 'ϟ', + 'Ϡ' => 'ϡ', + 'Ϣ' => 'ϣ', + 'Ϥ' => 'ϥ', + 'Ϧ' => 'ϧ', + 'Ϩ' => 'ϩ', + 'Ϫ' => 'ϫ', + 'Ϭ' => 'ϭ', + 'Ϯ' => 'ϯ', + 'ϴ' => 'θ', + 'Ϸ' => 'ϸ', + 'Ϲ' => 'ϲ', + 'Ϻ' => 'ϻ', + 'Ͻ' => 'ͻ', + 'Ͼ' => 'ͼ', + 'Ͽ' => 'ͽ', + 'Ѐ' => 'ѐ', + 'Ё' => 'ё', + 'Ђ' => 'ђ', + 'Ѓ' => 'ѓ', + 'Є' => 'є', + 'Ѕ' => 'ѕ', + 'І' => 'і', + 'Ї' => 'ї', + 'Ј' => 'ј', + 'Љ' => 'љ', + 'Њ' => 'њ', + 'Ћ' => 'ћ', + 'Ќ' => 'ќ', + 'Ѝ' => 'ѝ', + 'Ў' => 'ў', + 'Џ' => 'џ', + 'А' => 'а', + 'Б' => 'б', + 'В' => 'в', + 'Г' => 'г', + 'Д' => 'д', + 'Е' => 'е', + 'Ж' => 'ж', + 'З' => 'з', + 'И' => 'и', + 'Й' => 'й', + 'К' => 'к', + 'Л' => 'л', + 'М' => 'м', + 'Н' => 'н', + 'О' => 'о', + 'П' => 'п', + 'Р' => 'р', + 'С' => 'с', + 'Т' => 'т', + 'У' => 'у', + 'Ф' => 'ф', + 'Х' => 'х', + 'Ц' => 'ц', + 'Ч' => 'ч', + 'Ш' => 'ш', + 'Щ' => 'щ', + 'Ъ' => 'ъ', + 'Ы' => 'ы', + 'Ь' => 'ь', + 'Э' => 'э', + 'Ю' => 'ю', + 'Я' => 'я', + 'Ѡ' => 'ѡ', + 'Ѣ' => 'ѣ', + 'Ѥ' => 'ѥ', + 'Ѧ' => 'ѧ', + 'Ѩ' => 'ѩ', + 'Ѫ' => 'ѫ', + 'Ѭ' => 'ѭ', + 'Ѯ' => 'ѯ', + 'Ѱ' => 'ѱ', + 'Ѳ' => 'ѳ', + 'Ѵ' => 'ѵ', + 'Ѷ' => 'ѷ', + 'Ѹ' => 'ѹ', + 'Ѻ' => 'ѻ', + 'Ѽ' => 'ѽ', + 'Ѿ' => 'ѿ', + 'Ҁ' => 'ҁ', + 'Ҋ' => 'ҋ', + 'Ҍ' => 'ҍ', + 'Ҏ' => 'ҏ', + 'Ґ' => 'ґ', + 'Ғ' => 'ғ', + 'Ҕ' => 'ҕ', + 'Җ' => 'җ', + 'Ҙ' => 'ҙ', + 'Қ' => 'қ', + 'Ҝ' => 'ҝ', + 'Ҟ' => 'ҟ', + 'Ҡ' => 'ҡ', + 'Ң' => 'ң', + 'Ҥ' => 'ҥ', + 'Ҧ' => 'ҧ', + 'Ҩ' => 'ҩ', + 'Ҫ' => 'ҫ', + 'Ҭ' => 'ҭ', + 'Ү' => 'ү', + 'Ұ' => 'ұ', + 'Ҳ' => 'ҳ', + 'Ҵ' => 'ҵ', + 'Ҷ' => 'ҷ', + 'Ҹ' => 'ҹ', + 'Һ' => 'һ', + 'Ҽ' => 'ҽ', + 'Ҿ' => 'ҿ', + 'Ӏ' => 'ӏ', + 'Ӂ' => 'ӂ', + 'Ӄ' => 'ӄ', + 'Ӆ' => 'ӆ', + 'Ӈ' => 'ӈ', + 'Ӊ' => 'ӊ', + 'Ӌ' => 'ӌ', + 'Ӎ' => 'ӎ', + 'Ӑ' => 'ӑ', + 'Ӓ' => 'ӓ', + 'Ӕ' => 'ӕ', + 'Ӗ' => 'ӗ', + 'Ә' => 'ә', + 'Ӛ' => 'ӛ', + 'Ӝ' => 'ӝ', + 'Ӟ' => 'ӟ', + 'Ӡ' => 'ӡ', + 'Ӣ' => 'ӣ', + 'Ӥ' => 'ӥ', + 'Ӧ' => 'ӧ', + 'Ө' => 'ө', + 'Ӫ' => 'ӫ', + 'Ӭ' => 'ӭ', + 'Ӯ' => 'ӯ', + 'Ӱ' => 'ӱ', + 'Ӳ' => 'ӳ', + 'Ӵ' => 'ӵ', + 'Ӷ' => 'ӷ', + 'Ӹ' => 'ӹ', + 'Ӻ' => 'ӻ', + 'Ӽ' => 'ӽ', + 'Ӿ' => 'ӿ', + 'Ԁ' => 'ԁ', + 'Ԃ' => 'ԃ', + 'Ԅ' => 'ԅ', + 'Ԇ' => 'ԇ', + 'Ԉ' => 'ԉ', + 'Ԋ' => 'ԋ', + 'Ԍ' => 'ԍ', + 'Ԏ' => 'ԏ', + 'Ԑ' => 'ԑ', + 'Ԓ' => 'ԓ', + 'Ԕ' => 'ԕ', + 'Ԗ' => 'ԗ', + 'Ԙ' => 'ԙ', + 'Ԛ' => 'ԛ', + 'Ԝ' => 'ԝ', + 'Ԟ' => 'ԟ', + 'Ԡ' => 'ԡ', + 'Ԣ' => 'ԣ', + 'Ԥ' => 'ԥ', + 'Ԧ' => 'ԧ', + 'Ԩ' => 'ԩ', + 'Ԫ' => 'ԫ', + 'Ԭ' => 'ԭ', + 'Ԯ' => 'ԯ', + 'Ա' => 'ա', + 'Բ' => 'բ', + 'Գ' => 'գ', + 'Դ' => 'դ', + 'Ե' => 'ե', + 'Զ' => 'զ', + 'Է' => 'է', + 'Ը' => 'ը', + 'Թ' => 'թ', + 'Ժ' => 'ժ', + 'Ի' => 'ի', + 'Լ' => 'լ', + 'Խ' => 'խ', + 'Ծ' => 'ծ', + 'Կ' => 'կ', + 'Հ' => 'հ', + 'Ձ' => 'ձ', + 'Ղ' => 'ղ', + 'Ճ' => 'ճ', + 'Մ' => 'մ', + 'Յ' => 'յ', + 'Ն' => 'ն', + 'Շ' => 'շ', + 'Ո' => 'ո', + 'Չ' => 'չ', + 'Պ' => 'պ', + 'Ջ' => 'ջ', + 'Ռ' => 'ռ', + 'Ս' => 'ս', + 'Վ' => 'վ', + 'Տ' => 'տ', + 'Ր' => 'ր', + 'Ց' => 'ց', + 'Ւ' => 'ւ', + 'Փ' => 'փ', + 'Ք' => 'ք', + 'Օ' => 'օ', + 'Ֆ' => 'ֆ', + 'Ⴀ' => 'ⴀ', + 'Ⴁ' => 'ⴁ', + 'Ⴂ' => 'ⴂ', + 'Ⴃ' => 'ⴃ', + 'Ⴄ' => 'ⴄ', + 'Ⴅ' => 'ⴅ', + 'Ⴆ' => 'ⴆ', + 'Ⴇ' => 'ⴇ', + 'Ⴈ' => 'ⴈ', + 'Ⴉ' => 'ⴉ', + 'Ⴊ' => 'ⴊ', + 'Ⴋ' => 'ⴋ', + 'Ⴌ' => 'ⴌ', + 'Ⴍ' => 'ⴍ', + 'Ⴎ' => 'ⴎ', + 'Ⴏ' => 'ⴏ', + 'Ⴐ' => 'ⴐ', + 'Ⴑ' => 'ⴑ', + 'Ⴒ' => 'ⴒ', + 'Ⴓ' => 'ⴓ', + 'Ⴔ' => 'ⴔ', + 'Ⴕ' => 'ⴕ', + 'Ⴖ' => 'ⴖ', + 'Ⴗ' => 'ⴗ', + 'Ⴘ' => 'ⴘ', + 'Ⴙ' => 'ⴙ', + 'Ⴚ' => 'ⴚ', + 'Ⴛ' => 'ⴛ', + 'Ⴜ' => 'ⴜ', + 'Ⴝ' => 'ⴝ', + 'Ⴞ' => 'ⴞ', + 'Ⴟ' => 'ⴟ', + 'Ⴠ' => 'ⴠ', + 'Ⴡ' => 'ⴡ', + 'Ⴢ' => 'ⴢ', + 'Ⴣ' => 'ⴣ', + 'Ⴤ' => 'ⴤ', + 'Ⴥ' => 'ⴥ', + 'Ⴧ' => 'ⴧ', + 'Ⴭ' => 'ⴭ', + 'Ḁ' => 'ḁ', + 'Ḃ' => 'ḃ', + 'Ḅ' => 'ḅ', + 'Ḇ' => 'ḇ', + 'Ḉ' => 'ḉ', + 'Ḋ' => 'ḋ', + 'Ḍ' => 'ḍ', + 'Ḏ' => 'ḏ', + 'Ḑ' => 'ḑ', + 'Ḓ' => 'ḓ', + 'Ḕ' => 'ḕ', + 'Ḗ' => 'ḗ', + 'Ḙ' => 'ḙ', + 'Ḛ' => 'ḛ', + 'Ḝ' => 'ḝ', + 'Ḟ' => 'ḟ', + 'Ḡ' => 'ḡ', + 'Ḣ' => 'ḣ', + 'Ḥ' => 'ḥ', + 'Ḧ' => 'ḧ', + 'Ḩ' => 'ḩ', + 'Ḫ' => 'ḫ', + 'Ḭ' => 'ḭ', + 'Ḯ' => 'ḯ', + 'Ḱ' => 'ḱ', + 'Ḳ' => 'ḳ', + 'Ḵ' => 'ḵ', + 'Ḷ' => 'ḷ', + 'Ḹ' => 'ḹ', + 'Ḻ' => 'ḻ', + 'Ḽ' => 'ḽ', + 'Ḿ' => 'ḿ', + 'Ṁ' => 'ṁ', + 'Ṃ' => 'ṃ', + 'Ṅ' => 'ṅ', + 'Ṇ' => 'ṇ', + 'Ṉ' => 'ṉ', + 'Ṋ' => 'ṋ', + 'Ṍ' => 'ṍ', + 'Ṏ' => 'ṏ', + 'Ṑ' => 'ṑ', + 'Ṓ' => 'ṓ', + 'Ṕ' => 'ṕ', + 'Ṗ' => 'ṗ', + 'Ṙ' => 'ṙ', + 'Ṛ' => 'ṛ', + 'Ṝ' => 'ṝ', + 'Ṟ' => 'ṟ', + 'Ṡ' => 'ṡ', + 'Ṣ' => 'ṣ', + 'Ṥ' => 'ṥ', + 'Ṧ' => 'ṧ', + 'Ṩ' => 'ṩ', + 'Ṫ' => 'ṫ', + 'Ṭ' => 'ṭ', + 'Ṯ' => 'ṯ', + 'Ṱ' => 'ṱ', + 'Ṳ' => 'ṳ', + 'Ṵ' => 'ṵ', + 'Ṷ' => 'ṷ', + 'Ṹ' => 'ṹ', + 'Ṻ' => 'ṻ', + 'Ṽ' => 'ṽ', + 'Ṿ' => 'ṿ', + 'Ẁ' => 'ẁ', + 'Ẃ' => 'ẃ', + 'Ẅ' => 'ẅ', + 'Ẇ' => 'ẇ', + 'Ẉ' => 'ẉ', + 'Ẋ' => 'ẋ', + 'Ẍ' => 'ẍ', + 'Ẏ' => 'ẏ', + 'Ẑ' => 'ẑ', + 'Ẓ' => 'ẓ', + 'Ẕ' => 'ẕ', + 'ẞ' => 'ß', + 'Ạ' => 'ạ', + 'Ả' => 'ả', + 'Ấ' => 'ấ', + 'Ầ' => 'ầ', + 'Ẩ' => 'ẩ', + 'Ẫ' => 'ẫ', + 'Ậ' => 'ậ', + 'Ắ' => 'ắ', + 'Ằ' => 'ằ', + 'Ẳ' => 'ẳ', + 'Ẵ' => 'ẵ', + 'Ặ' => 'ặ', + 'Ẹ' => 'ẹ', + 'Ẻ' => 'ẻ', + 'Ẽ' => 'ẽ', + 'Ế' => 'ế', + 'Ề' => 'ề', + 'Ể' => 'ể', + 'Ễ' => 'ễ', + 'Ệ' => 'ệ', + 'Ỉ' => 'ỉ', + 'Ị' => 'ị', + 'Ọ' => 'ọ', + 'Ỏ' => 'ỏ', + 'Ố' => 'ố', + 'Ồ' => 'ồ', + 'Ổ' => 'ổ', + 'Ỗ' => 'ỗ', + 'Ộ' => 'ộ', + 'Ớ' => 'ớ', + 'Ờ' => 'ờ', + 'Ở' => 'ở', + 'Ỡ' => 'ỡ', + 'Ợ' => 'ợ', + 'Ụ' => 'ụ', + 'Ủ' => 'ủ', + 'Ứ' => 'ứ', + 'Ừ' => 'ừ', + 'Ử' => 'ử', + 'Ữ' => 'ữ', + 'Ự' => 'ự', + 'Ỳ' => 'ỳ', + 'Ỵ' => 'ỵ', + 'Ỷ' => 'ỷ', + 'Ỹ' => 'ỹ', + 'Ỻ' => 'ỻ', + 'Ỽ' => 'ỽ', + 'Ỿ' => 'ỿ', + 'Ἀ' => 'ἀ', + 'Ἁ' => 'ἁ', + 'Ἂ' => 'ἂ', + 'Ἃ' => 'ἃ', + 'Ἄ' => 'ἄ', + 'Ἅ' => 'ἅ', + 'Ἆ' => 'ἆ', + 'Ἇ' => 'ἇ', + 'Ἐ' => 'ἐ', + 'Ἑ' => 'ἑ', + 'Ἒ' => 'ἒ', + 'Ἓ' => 'ἓ', + 'Ἔ' => 'ἔ', + 'Ἕ' => 'ἕ', + 'Ἠ' => 'ἠ', + 'Ἡ' => 'ἡ', + 'Ἢ' => 'ἢ', + 'Ἣ' => 'ἣ', + 'Ἤ' => 'ἤ', + 'Ἥ' => 'ἥ', + 'Ἦ' => 'ἦ', + 'Ἧ' => 'ἧ', + 'Ἰ' => 'ἰ', + 'Ἱ' => 'ἱ', + 'Ἲ' => 'ἲ', + 'Ἳ' => 'ἳ', + 'Ἴ' => 'ἴ', + 'Ἵ' => 'ἵ', + 'Ἶ' => 'ἶ', + 'Ἷ' => 'ἷ', + 'Ὀ' => 'ὀ', + 'Ὁ' => 'ὁ', + 'Ὂ' => 'ὂ', + 'Ὃ' => 'ὃ', + 'Ὄ' => 'ὄ', + 'Ὅ' => 'ὅ', + 'Ὑ' => 'ὑ', + 'Ὓ' => 'ὓ', + 'Ὕ' => 'ὕ', + 'Ὗ' => 'ὗ', + 'Ὠ' => 'ὠ', + 'Ὡ' => 'ὡ', + 'Ὢ' => 'ὢ', + 'Ὣ' => 'ὣ', + 'Ὤ' => 'ὤ', + 'Ὥ' => 'ὥ', + 'Ὦ' => 'ὦ', + 'Ὧ' => 'ὧ', + 'ᾈ' => 'ᾀ', + 'ᾉ' => 'ᾁ', + 'ᾊ' => 'ᾂ', + 'ᾋ' => 'ᾃ', + 'ᾌ' => 'ᾄ', + 'ᾍ' => 'ᾅ', + 'ᾎ' => 'ᾆ', + 'ᾏ' => 'ᾇ', + 'ᾘ' => 'ᾐ', + 'ᾙ' => 'ᾑ', + 'ᾚ' => 'ᾒ', + 'ᾛ' => 'ᾓ', + 'ᾜ' => 'ᾔ', + 'ᾝ' => 'ᾕ', + 'ᾞ' => 'ᾖ', + 'ᾟ' => 'ᾗ', + 'ᾨ' => 'ᾠ', + 'ᾩ' => 'ᾡ', + 'ᾪ' => 'ᾢ', + 'ᾫ' => 'ᾣ', + 'ᾬ' => 'ᾤ', + 'ᾭ' => 'ᾥ', + 'ᾮ' => 'ᾦ', + 'ᾯ' => 'ᾧ', + 'Ᾰ' => 'ᾰ', + 'Ᾱ' => 'ᾱ', + 'Ὰ' => 'ὰ', + 'Ά' => 'ά', + 'ᾼ' => 'ᾳ', + 'Ὲ' => 'ὲ', + 'Έ' => 'έ', + 'Ὴ' => 'ὴ', + 'Ή' => 'ή', + 'ῌ' => 'ῃ', + 'Ῐ' => 'ῐ', + 'Ῑ' => 'ῑ', + 'Ὶ' => 'ὶ', + 'Ί' => 'ί', + 'Ῠ' => 'ῠ', + 'Ῡ' => 'ῡ', + 'Ὺ' => 'ὺ', + 'Ύ' => 'ύ', + 'Ῥ' => 'ῥ', + 'Ὸ' => 'ὸ', + 'Ό' => 'ό', + 'Ὼ' => 'ὼ', + 'Ώ' => 'ώ', + 'ῼ' => 'ῳ', + 'Ω' => 'ω', + 'K' => 'k', + 'Å' => 'å', + 'Ⅎ' => 'ⅎ', + 'Ⅰ' => 'ⅰ', + 'Ⅱ' => 'ⅱ', + 'Ⅲ' => 'ⅲ', + 'Ⅳ' => 'ⅳ', + 'Ⅴ' => 'ⅴ', + 'Ⅵ' => 'ⅵ', + 'Ⅶ' => 'ⅶ', + 'Ⅷ' => 'ⅷ', + 'Ⅸ' => 'ⅸ', + 'Ⅹ' => 'ⅹ', + 'Ⅺ' => 'ⅺ', + 'Ⅻ' => 'ⅻ', + 'Ⅼ' => 'ⅼ', + 'Ⅽ' => 'ⅽ', + 'Ⅾ' => 'ⅾ', + 'Ⅿ' => 'ⅿ', + 'Ↄ' => 'ↄ', + 'Ⓐ' => 'ⓐ', + 'Ⓑ' => 'ⓑ', + 'Ⓒ' => 'ⓒ', + 'Ⓓ' => 'ⓓ', + 'Ⓔ' => 'ⓔ', + 'Ⓕ' => 'ⓕ', + 'Ⓖ' => 'ⓖ', + 'Ⓗ' => 'ⓗ', + 'Ⓘ' => 'ⓘ', + 'Ⓙ' => 'ⓙ', + 'Ⓚ' => 'ⓚ', + 'Ⓛ' => 'ⓛ', + 'Ⓜ' => 'ⓜ', + 'Ⓝ' => 'ⓝ', + 'Ⓞ' => 'ⓞ', + 'Ⓟ' => 'ⓟ', + 'Ⓠ' => 'ⓠ', + 'Ⓡ' => 'ⓡ', + 'Ⓢ' => 'ⓢ', + 'Ⓣ' => 'ⓣ', + 'Ⓤ' => 'ⓤ', + 'Ⓥ' => 'ⓥ', + 'Ⓦ' => 'ⓦ', + 'Ⓧ' => 'ⓧ', + 'Ⓨ' => 'ⓨ', + 'Ⓩ' => 'ⓩ', + 'Ⰰ' => 'ⰰ', + 'Ⰱ' => 'ⰱ', + 'Ⰲ' => 'ⰲ', + 'Ⰳ' => 'ⰳ', + 'Ⰴ' => 'ⰴ', + 'Ⰵ' => 'ⰵ', + 'Ⰶ' => 'ⰶ', + 'Ⰷ' => 'ⰷ', + 'Ⰸ' => 'ⰸ', + 'Ⰹ' => 'ⰹ', + 'Ⰺ' => 'ⰺ', + 'Ⰻ' => 'ⰻ', + 'Ⰼ' => 'ⰼ', + 'Ⰽ' => 'ⰽ', + 'Ⰾ' => 'ⰾ', + 'Ⰿ' => 'ⰿ', + 'Ⱀ' => 'ⱀ', + 'Ⱁ' => 'ⱁ', + 'Ⱂ' => 'ⱂ', + 'Ⱃ' => 'ⱃ', + 'Ⱄ' => 'ⱄ', + 'Ⱅ' => 'ⱅ', + 'Ⱆ' => 'ⱆ', + 'Ⱇ' => 'ⱇ', + 'Ⱈ' => 'ⱈ', + 'Ⱉ' => 'ⱉ', + 'Ⱊ' => 'ⱊ', + 'Ⱋ' => 'ⱋ', + 'Ⱌ' => 'ⱌ', + 'Ⱍ' => 'ⱍ', + 'Ⱎ' => 'ⱎ', + 'Ⱏ' => 'ⱏ', + 'Ⱐ' => 'ⱐ', + 'Ⱑ' => 'ⱑ', + 'Ⱒ' => 'ⱒ', + 'Ⱓ' => 'ⱓ', + 'Ⱔ' => 'ⱔ', + 'Ⱕ' => 'ⱕ', + 'Ⱖ' => 'ⱖ', + 'Ⱗ' => 'ⱗ', + 'Ⱘ' => 'ⱘ', + 'Ⱙ' => 'ⱙ', + 'Ⱚ' => 'ⱚ', + 'Ⱛ' => 'ⱛ', + 'Ⱜ' => 'ⱜ', + 'Ⱝ' => 'ⱝ', + 'Ⱞ' => 'ⱞ', + 'Ⱡ' => 'ⱡ', + 'Ɫ' => 'ɫ', + 'Ᵽ' => 'ᵽ', + 'Ɽ' => 'ɽ', + 'Ⱨ' => 'ⱨ', + 'Ⱪ' => 'ⱪ', + 'Ⱬ' => 'ⱬ', + 'Ɑ' => 'ɑ', + 'Ɱ' => 'ɱ', + 'Ɐ' => 'ɐ', + 'Ɒ' => 'ɒ', + 'Ⱳ' => 'ⱳ', + 'Ⱶ' => 'ⱶ', + 'Ȿ' => 'ȿ', + 'Ɀ' => 'ɀ', + 'Ⲁ' => 'ⲁ', + 'Ⲃ' => 'ⲃ', + 'Ⲅ' => 'ⲅ', + 'Ⲇ' => 'ⲇ', + 'Ⲉ' => 'ⲉ', + 'Ⲋ' => 'ⲋ', + 'Ⲍ' => 'ⲍ', + 'Ⲏ' => 'ⲏ', + 'Ⲑ' => 'ⲑ', + 'Ⲓ' => 'ⲓ', + 'Ⲕ' => 'ⲕ', + 'Ⲗ' => 'ⲗ', + 'Ⲙ' => 'ⲙ', + 'Ⲛ' => 'ⲛ', + 'Ⲝ' => 'ⲝ', + 'Ⲟ' => 'ⲟ', + 'Ⲡ' => 'ⲡ', + 'Ⲣ' => 'ⲣ', + 'Ⲥ' => 'ⲥ', + 'Ⲧ' => 'ⲧ', + 'Ⲩ' => 'ⲩ', + 'Ⲫ' => 'ⲫ', + 'Ⲭ' => 'ⲭ', + 'Ⲯ' => 'ⲯ', + 'Ⲱ' => 'ⲱ', + 'Ⲳ' => 'ⲳ', + 'Ⲵ' => 'ⲵ', + 'Ⲷ' => 'ⲷ', + 'Ⲹ' => 'ⲹ', + 'Ⲻ' => 'ⲻ', + 'Ⲽ' => 'ⲽ', + 'Ⲿ' => 'ⲿ', + 'Ⳁ' => 'ⳁ', + 'Ⳃ' => 'ⳃ', + 'Ⳅ' => 'ⳅ', + 'Ⳇ' => 'ⳇ', + 'Ⳉ' => 'ⳉ', + 'Ⳋ' => 'ⳋ', + 'Ⳍ' => 'ⳍ', + 'Ⳏ' => 'ⳏ', + 'Ⳑ' => 'ⳑ', + 'Ⳓ' => 'ⳓ', + 'Ⳕ' => 'ⳕ', + 'Ⳗ' => 'ⳗ', + 'Ⳙ' => 'ⳙ', + 'Ⳛ' => 'ⳛ', + 'Ⳝ' => 'ⳝ', + 'Ⳟ' => 'ⳟ', + 'Ⳡ' => 'ⳡ', + 'Ⳣ' => 'ⳣ', + 'Ⳬ' => 'ⳬ', + 'Ⳮ' => 'ⳮ', + 'Ⳳ' => 'ⳳ', + 'Ꙁ' => 'ꙁ', + 'Ꙃ' => 'ꙃ', + 'Ꙅ' => 'ꙅ', + 'Ꙇ' => 'ꙇ', + 'Ꙉ' => 'ꙉ', + 'Ꙋ' => 'ꙋ', + 'Ꙍ' => 'ꙍ', + 'Ꙏ' => 'ꙏ', + 'Ꙑ' => 'ꙑ', + 'Ꙓ' => 'ꙓ', + 'Ꙕ' => 'ꙕ', + 'Ꙗ' => 'ꙗ', + 'Ꙙ' => 'ꙙ', + 'Ꙛ' => 'ꙛ', + 'Ꙝ' => 'ꙝ', + 'Ꙟ' => 'ꙟ', + 'Ꙡ' => 'ꙡ', + 'Ꙣ' => 'ꙣ', + 'Ꙥ' => 'ꙥ', + 'Ꙧ' => 'ꙧ', + 'Ꙩ' => 'ꙩ', + 'Ꙫ' => 'ꙫ', + 'Ꙭ' => 'ꙭ', + 'Ꚁ' => 'ꚁ', + 'Ꚃ' => 'ꚃ', + 'Ꚅ' => 'ꚅ', + 'Ꚇ' => 'ꚇ', + 'Ꚉ' => 'ꚉ', + 'Ꚋ' => 'ꚋ', + 'Ꚍ' => 'ꚍ', + 'Ꚏ' => 'ꚏ', + 'Ꚑ' => 'ꚑ', + 'Ꚓ' => 'ꚓ', + 'Ꚕ' => 'ꚕ', + 'Ꚗ' => 'ꚗ', + 'Ꚙ' => 'ꚙ', + 'Ꚛ' => 'ꚛ', + 'Ꜣ' => 'ꜣ', + 'Ꜥ' => 'ꜥ', + 'Ꜧ' => 'ꜧ', + 'Ꜩ' => 'ꜩ', + 'Ꜫ' => 'ꜫ', + 'Ꜭ' => 'ꜭ', + 'Ꜯ' => 'ꜯ', + 'Ꜳ' => 'ꜳ', + 'Ꜵ' => 'ꜵ', + 'Ꜷ' => 'ꜷ', + 'Ꜹ' => 'ꜹ', + 'Ꜻ' => 'ꜻ', + 'Ꜽ' => 'ꜽ', + 'Ꜿ' => 'ꜿ', + 'Ꝁ' => 'ꝁ', + 'Ꝃ' => 'ꝃ', + 'Ꝅ' => 'ꝅ', + 'Ꝇ' => 'ꝇ', + 'Ꝉ' => 'ꝉ', + 'Ꝋ' => 'ꝋ', + 'Ꝍ' => 'ꝍ', + 'Ꝏ' => 'ꝏ', + 'Ꝑ' => 'ꝑ', + 'Ꝓ' => 'ꝓ', + 'Ꝕ' => 'ꝕ', + 'Ꝗ' => 'ꝗ', + 'Ꝙ' => 'ꝙ', + 'Ꝛ' => 'ꝛ', + 'Ꝝ' => 'ꝝ', + 'Ꝟ' => 'ꝟ', + 'Ꝡ' => 'ꝡ', + 'Ꝣ' => 'ꝣ', + 'Ꝥ' => 'ꝥ', + 'Ꝧ' => 'ꝧ', + 'Ꝩ' => 'ꝩ', + 'Ꝫ' => 'ꝫ', + 'Ꝭ' => 'ꝭ', + 'Ꝯ' => 'ꝯ', + 'Ꝺ' => 'ꝺ', + 'Ꝼ' => 'ꝼ', + 'Ᵹ' => 'ᵹ', + 'Ꝿ' => 'ꝿ', + 'Ꞁ' => 'ꞁ', + 'Ꞃ' => 'ꞃ', + 'Ꞅ' => 'ꞅ', + 'Ꞇ' => 'ꞇ', + 'Ꞌ' => 'ꞌ', + 'Ɥ' => 'ɥ', + 'Ꞑ' => 'ꞑ', + 'Ꞓ' => 'ꞓ', + 'Ꞗ' => 'ꞗ', + 'Ꞙ' => 'ꞙ', + 'Ꞛ' => 'ꞛ', + 'Ꞝ' => 'ꞝ', + 'Ꞟ' => 'ꞟ', + 'Ꞡ' => 'ꞡ', + 'Ꞣ' => 'ꞣ', + 'Ꞥ' => 'ꞥ', + 'Ꞧ' => 'ꞧ', + 'Ꞩ' => 'ꞩ', + 'Ɦ' => 'ɦ', + 'Ɜ' => 'ɜ', + 'Ɡ' => 'ɡ', + 'Ɬ' => 'ɬ', + 'Ʞ' => 'ʞ', + 'Ʇ' => 'ʇ', + 'A' => 'a', + 'B' => 'b', + 'C' => 'c', + 'D' => 'd', + 'E' => 'e', + 'F' => 'f', + 'G' => 'g', + 'H' => 'h', + 'I' => 'i', + 'J' => 'j', + 'K' => 'k', + 'L' => 'l', + 'M' => 'm', + 'N' => 'n', + 'O' => 'o', + 'P' => 'p', + 'Q' => 'q', + 'R' => 'r', + 'S' => 's', + 'T' => 't', + 'U' => 'u', + 'V' => 'v', + 'W' => 'w', + 'X' => 'x', + 'Y' => 'y', + 'Z' => 'z', + '𐐀' => '𐐨', + '𐐁' => '𐐩', + '𐐂' => '𐐪', + '𐐃' => '𐐫', + '𐐄' => '𐐬', + '𐐅' => '𐐭', + '𐐆' => '𐐮', + '𐐇' => '𐐯', + '𐐈' => '𐐰', + '𐐉' => '𐐱', + '𐐊' => '𐐲', + '𐐋' => '𐐳', + '𐐌' => '𐐴', + '𐐍' => '𐐵', + '𐐎' => '𐐶', + '𐐏' => '𐐷', + '𐐐' => '𐐸', + '𐐑' => '𐐹', + '𐐒' => '𐐺', + '𐐓' => '𐐻', + '𐐔' => '𐐼', + '𐐕' => '𐐽', + '𐐖' => '𐐾', + '𐐗' => '𐐿', + '𐐘' => '𐑀', + '𐐙' => '𐑁', + '𐐚' => '𐑂', + '𐐛' => '𐑃', + '𐐜' => '𐑄', + '𐐝' => '𐑅', + '𐐞' => '𐑆', + '𐐟' => '𐑇', + '𐐠' => '𐑈', + '𐐡' => '𐑉', + '𐐢' => '𐑊', + '𐐣' => '𐑋', + '𐐤' => '𐑌', + '𐐥' => '𐑍', + '𐐦' => '𐑎', + '𐐧' => '𐑏', + '𑢠' => '𑣀', + '𑢡' => '𑣁', + '𑢢' => '𑣂', + '𑢣' => '𑣃', + '𑢤' => '𑣄', + '𑢥' => '𑣅', + '𑢦' => '𑣆', + '𑢧' => '𑣇', + '𑢨' => '𑣈', + '𑢩' => '𑣉', + '𑢪' => '𑣊', + '𑢫' => '𑣋', + '𑢬' => '𑣌', + '𑢭' => '𑣍', + '𑢮' => '𑣎', + '𑢯' => '𑣏', + '𑢰' => '𑣐', + '𑢱' => '𑣑', + '𑢲' => '𑣒', + '𑢳' => '𑣓', + '𑢴' => '𑣔', + '𑢵' => '𑣕', + '𑢶' => '𑣖', + '𑢷' => '𑣗', + '𑢸' => '𑣘', + '𑢹' => '𑣙', + '𑢺' => '𑣚', + '𑢻' => '𑣛', + '𑢼' => '𑣜', + '𑢽' => '𑣝', + '𑢾' => '𑣞', + '𑢿' => '𑣟', +); + +$result =& $data; +unset($data); + +return $result; diff --git a/vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.php b/vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.php new file mode 100644 index 0000000000..ec9422121c --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.php @@ -0,0 +1,1109 @@ + 'A', + 'b' => 'B', + 'c' => 'C', + 'd' => 'D', + 'e' => 'E', + 'f' => 'F', + 'g' => 'G', + 'h' => 'H', + 'i' => 'I', + 'j' => 'J', + 'k' => 'K', + 'l' => 'L', + 'm' => 'M', + 'n' => 'N', + 'o' => 'O', + 'p' => 'P', + 'q' => 'Q', + 'r' => 'R', + 's' => 'S', + 't' => 'T', + 'u' => 'U', + 'v' => 'V', + 'w' => 'W', + 'x' => 'X', + 'y' => 'Y', + 'z' => 'Z', + 'µ' => 'Μ', + 'à' => 'À', + 'á' => 'Á', + 'â' => 'Â', + 'ã' => 'Ã', + 'ä' => 'Ä', + 'å' => 'Å', + 'æ' => 'Æ', + 'ç' => 'Ç', + 'è' => 'È', + 'é' => 'É', + 'ê' => 'Ê', + 'ë' => 'Ë', + 'ì' => 'Ì', + 'í' => 'Í', + 'î' => 'Î', + 'ï' => 'Ï', + 'ð' => 'Ð', + 'ñ' => 'Ñ', + 'ò' => 'Ò', + 'ó' => 'Ó', + 'ô' => 'Ô', + 'õ' => 'Õ', + 'ö' => 'Ö', + 'ø' => 'Ø', + 'ù' => 'Ù', + 'ú' => 'Ú', + 'û' => 'Û', + 'ü' => 'Ü', + 'ý' => 'Ý', + 'þ' => 'Þ', + 'ÿ' => 'Ÿ', + 'ā' => 'Ā', + 'ă' => 'Ă', + 'ą' => 'Ą', + 'ć' => 'Ć', + 'ĉ' => 'Ĉ', + 'ċ' => 'Ċ', + 'č' => 'Č', + 'ď' => 'Ď', + 'đ' => 'Đ', + 'ē' => 'Ē', + 'ĕ' => 'Ĕ', + 'ė' => 'Ė', + 'ę' => 'Ę', + 'ě' => 'Ě', + 'ĝ' => 'Ĝ', + 'ğ' => 'Ğ', + 'ġ' => 'Ġ', + 'ģ' => 'Ģ', + 'ĥ' => 'Ĥ', + 'ħ' => 'Ħ', + 'ĩ' => 'Ĩ', + 'ī' => 'Ī', + 'ĭ' => 'Ĭ', + 'į' => 'Į', + 'ı' => 'I', + 'ij' => 'IJ', + 'ĵ' => 'Ĵ', + 'ķ' => 'Ķ', + 'ĺ' => 'Ĺ', + 'ļ' => 'Ļ', + 'ľ' => 'Ľ', + 'ŀ' => 'Ŀ', + 'ł' => 'Ł', + 'ń' => 'Ń', + 'ņ' => 'Ņ', + 'ň' => 'Ň', + 'ŋ' => 'Ŋ', + 'ō' => 'Ō', + 'ŏ' => 'Ŏ', + 'ő' => 'Ő', + 'œ' => 'Œ', + 'ŕ' => 'Ŕ', + 'ŗ' => 'Ŗ', + 'ř' => 'Ř', + 'ś' => 'Ś', + 'ŝ' => 'Ŝ', + 'ş' => 'Ş', + 'š' => 'Š', + 'ţ' => 'Ţ', + 'ť' => 'Ť', + 'ŧ' => 'Ŧ', + 'ũ' => 'Ũ', + 'ū' => 'Ū', + 'ŭ' => 'Ŭ', + 'ů' => 'Ů', + 'ű' => 'Ű', + 'ų' => 'Ų', + 'ŵ' => 'Ŵ', + 'ŷ' => 'Ŷ', + 'ź' => 'Ź', + 'ż' => 'Ż', + 'ž' => 'Ž', + 'ſ' => 'S', + 'ƀ' => 'Ƀ', + 'ƃ' => 'Ƃ', + 'ƅ' => 'Ƅ', + 'ƈ' => 'Ƈ', + 'ƌ' => 'Ƌ', + 'ƒ' => 'Ƒ', + 'ƕ' => 'Ƕ', + 'ƙ' => 'Ƙ', + 'ƚ' => 'Ƚ', + 'ƞ' => 'Ƞ', + 'ơ' => 'Ơ', + 'ƣ' => 'Ƣ', + 'ƥ' => 'Ƥ', + 'ƨ' => 'Ƨ', + 'ƭ' => 'Ƭ', + 'ư' => 'Ư', + 'ƴ' => 'Ƴ', + 'ƶ' => 'Ƶ', + 'ƹ' => 'Ƹ', + 'ƽ' => 'Ƽ', + 'ƿ' => 'Ƿ', + 'Dž' => 'DŽ', + 'dž' => 'DŽ', + 'Lj' => 'LJ', + 'lj' => 'LJ', + 'Nj' => 'NJ', + 'nj' => 'NJ', + 'ǎ' => 'Ǎ', + 'ǐ' => 'Ǐ', + 'ǒ' => 'Ǒ', + 'ǔ' => 'Ǔ', + 'ǖ' => 'Ǖ', + 'ǘ' => 'Ǘ', + 'ǚ' => 'Ǚ', + 'ǜ' => 'Ǜ', + 'ǝ' => 'Ǝ', + 'ǟ' => 'Ǟ', + 'ǡ' => 'Ǡ', + 'ǣ' => 'Ǣ', + 'ǥ' => 'Ǥ', + 'ǧ' => 'Ǧ', + 'ǩ' => 'Ǩ', + 'ǫ' => 'Ǫ', + 'ǭ' => 'Ǭ', + 'ǯ' => 'Ǯ', + 'Dz' => 'DZ', + 'dz' => 'DZ', + 'ǵ' => 'Ǵ', + 'ǹ' => 'Ǹ', + 'ǻ' => 'Ǻ', + 'ǽ' => 'Ǽ', + 'ǿ' => 'Ǿ', + 'ȁ' => 'Ȁ', + 'ȃ' => 'Ȃ', + 'ȅ' => 'Ȅ', + 'ȇ' => 'Ȇ', + 'ȉ' => 'Ȉ', + 'ȋ' => 'Ȋ', + 'ȍ' => 'Ȍ', + 'ȏ' => 'Ȏ', + 'ȑ' => 'Ȑ', + 'ȓ' => 'Ȓ', + 'ȕ' => 'Ȕ', + 'ȗ' => 'Ȗ', + 'ș' => 'Ș', + 'ț' => 'Ț', + 'ȝ' => 'Ȝ', + 'ȟ' => 'Ȟ', + 'ȣ' => 'Ȣ', + 'ȥ' => 'Ȥ', + 'ȧ' => 'Ȧ', + 'ȩ' => 'Ȩ', + 'ȫ' => 'Ȫ', + 'ȭ' => 'Ȭ', + 'ȯ' => 'Ȯ', + 'ȱ' => 'Ȱ', + 'ȳ' => 'Ȳ', + 'ȼ' => 'Ȼ', + 'ȿ' => 'Ȿ', + 'ɀ' => 'Ɀ', + 'ɂ' => 'Ɂ', + 'ɇ' => 'Ɇ', + 'ɉ' => 'Ɉ', + 'ɋ' => 'Ɋ', + 'ɍ' => 'Ɍ', + 'ɏ' => 'Ɏ', + 'ɐ' => 'Ɐ', + 'ɑ' => 'Ɑ', + 'ɒ' => 'Ɒ', + 'ɓ' => 'Ɓ', + 'ɔ' => 'Ɔ', + 'ɖ' => 'Ɖ', + 'ɗ' => 'Ɗ', + 'ə' => 'Ə', + 'ɛ' => 'Ɛ', + 'ɜ' => 'Ɜ', + 'ɠ' => 'Ɠ', + 'ɡ' => 'Ɡ', + 'ɣ' => 'Ɣ', + 'ɥ' => 'Ɥ', + 'ɦ' => 'Ɦ', + 'ɨ' => 'Ɨ', + 'ɩ' => 'Ɩ', + 'ɫ' => 'Ɫ', + 'ɬ' => 'Ɬ', + 'ɯ' => 'Ɯ', + 'ɱ' => 'Ɱ', + 'ɲ' => 'Ɲ', + 'ɵ' => 'Ɵ', + 'ɽ' => 'Ɽ', + 'ʀ' => 'Ʀ', + 'ʃ' => 'Ʃ', + 'ʇ' => 'Ʇ', + 'ʈ' => 'Ʈ', + 'ʉ' => 'Ʉ', + 'ʊ' => 'Ʊ', + 'ʋ' => 'Ʋ', + 'ʌ' => 'Ʌ', + 'ʒ' => 'Ʒ', + 'ʞ' => 'Ʞ', + 'ͅ' => 'Ι', + 'ͱ' => 'Ͱ', + 'ͳ' => 'Ͳ', + 'ͷ' => 'Ͷ', + 'ͻ' => 'Ͻ', + 'ͼ' => 'Ͼ', + 'ͽ' => 'Ͽ', + 'ά' => 'Ά', + 'έ' => 'Έ', + 'ή' => 'Ή', + 'ί' => 'Ί', + 'α' => 'Α', + 'β' => 'Β', + 'γ' => 'Γ', + 'δ' => 'Δ', + 'ε' => 'Ε', + 'ζ' => 'Ζ', + 'η' => 'Η', + 'θ' => 'Θ', + 'ι' => 'Ι', + 'κ' => 'Κ', + 'λ' => 'Λ', + 'μ' => 'Μ', + 'ν' => 'Ν', + 'ξ' => 'Ξ', + 'ο' => 'Ο', + 'π' => 'Π', + 'ρ' => 'Ρ', + 'ς' => 'Σ', + 'σ' => 'Σ', + 'τ' => 'Τ', + 'υ' => 'Υ', + 'φ' => 'Φ', + 'χ' => 'Χ', + 'ψ' => 'Ψ', + 'ω' => 'Ω', + 'ϊ' => 'Ϊ', + 'ϋ' => 'Ϋ', + 'ό' => 'Ό', + 'ύ' => 'Ύ', + 'ώ' => 'Ώ', + 'ϐ' => 'Β', + 'ϑ' => 'Θ', + 'ϕ' => 'Φ', + 'ϖ' => 'Π', + 'ϗ' => 'Ϗ', + 'ϙ' => 'Ϙ', + 'ϛ' => 'Ϛ', + 'ϝ' => 'Ϝ', + 'ϟ' => 'Ϟ', + 'ϡ' => 'Ϡ', + 'ϣ' => 'Ϣ', + 'ϥ' => 'Ϥ', + 'ϧ' => 'Ϧ', + 'ϩ' => 'Ϩ', + 'ϫ' => 'Ϫ', + 'ϭ' => 'Ϭ', + 'ϯ' => 'Ϯ', + 'ϰ' => 'Κ', + 'ϱ' => 'Ρ', + 'ϲ' => 'Ϲ', + 'ϳ' => 'Ϳ', + 'ϵ' => 'Ε', + 'ϸ' => 'Ϸ', + 'ϻ' => 'Ϻ', + 'а' => 'А', + 'б' => 'Б', + 'в' => 'В', + 'г' => 'Г', + 'д' => 'Д', + 'е' => 'Е', + 'ж' => 'Ж', + 'з' => 'З', + 'и' => 'И', + 'й' => 'Й', + 'к' => 'К', + 'л' => 'Л', + 'м' => 'М', + 'н' => 'Н', + 'о' => 'О', + 'п' => 'П', + 'р' => 'Р', + 'с' => 'С', + 'т' => 'Т', + 'у' => 'У', + 'ф' => 'Ф', + 'х' => 'Х', + 'ц' => 'Ц', + 'ч' => 'Ч', + 'ш' => 'Ш', + 'щ' => 'Щ', + 'ъ' => 'Ъ', + 'ы' => 'Ы', + 'ь' => 'Ь', + 'э' => 'Э', + 'ю' => 'Ю', + 'я' => 'Я', + 'ѐ' => 'Ѐ', + 'ё' => 'Ё', + 'ђ' => 'Ђ', + 'ѓ' => 'Ѓ', + 'є' => 'Є', + 'ѕ' => 'Ѕ', + 'і' => 'І', + 'ї' => 'Ї', + 'ј' => 'Ј', + 'љ' => 'Љ', + 'њ' => 'Њ', + 'ћ' => 'Ћ', + 'ќ' => 'Ќ', + 'ѝ' => 'Ѝ', + 'ў' => 'Ў', + 'џ' => 'Џ', + 'ѡ' => 'Ѡ', + 'ѣ' => 'Ѣ', + 'ѥ' => 'Ѥ', + 'ѧ' => 'Ѧ', + 'ѩ' => 'Ѩ', + 'ѫ' => 'Ѫ', + 'ѭ' => 'Ѭ', + 'ѯ' => 'Ѯ', + 'ѱ' => 'Ѱ', + 'ѳ' => 'Ѳ', + 'ѵ' => 'Ѵ', + 'ѷ' => 'Ѷ', + 'ѹ' => 'Ѹ', + 'ѻ' => 'Ѻ', + 'ѽ' => 'Ѽ', + 'ѿ' => 'Ѿ', + 'ҁ' => 'Ҁ', + 'ҋ' => 'Ҋ', + 'ҍ' => 'Ҍ', + 'ҏ' => 'Ҏ', + 'ґ' => 'Ґ', + 'ғ' => 'Ғ', + 'ҕ' => 'Ҕ', + 'җ' => 'Җ', + 'ҙ' => 'Ҙ', + 'қ' => 'Қ', + 'ҝ' => 'Ҝ', + 'ҟ' => 'Ҟ', + 'ҡ' => 'Ҡ', + 'ң' => 'Ң', + 'ҥ' => 'Ҥ', + 'ҧ' => 'Ҧ', + 'ҩ' => 'Ҩ', + 'ҫ' => 'Ҫ', + 'ҭ' => 'Ҭ', + 'ү' => 'Ү', + 'ұ' => 'Ұ', + 'ҳ' => 'Ҳ', + 'ҵ' => 'Ҵ', + 'ҷ' => 'Ҷ', + 'ҹ' => 'Ҹ', + 'һ' => 'Һ', + 'ҽ' => 'Ҽ', + 'ҿ' => 'Ҿ', + 'ӂ' => 'Ӂ', + 'ӄ' => 'Ӄ', + 'ӆ' => 'Ӆ', + 'ӈ' => 'Ӈ', + 'ӊ' => 'Ӊ', + 'ӌ' => 'Ӌ', + 'ӎ' => 'Ӎ', + 'ӏ' => 'Ӏ', + 'ӑ' => 'Ӑ', + 'ӓ' => 'Ӓ', + 'ӕ' => 'Ӕ', + 'ӗ' => 'Ӗ', + 'ә' => 'Ә', + 'ӛ' => 'Ӛ', + 'ӝ' => 'Ӝ', + 'ӟ' => 'Ӟ', + 'ӡ' => 'Ӡ', + 'ӣ' => 'Ӣ', + 'ӥ' => 'Ӥ', + 'ӧ' => 'Ӧ', + 'ө' => 'Ө', + 'ӫ' => 'Ӫ', + 'ӭ' => 'Ӭ', + 'ӯ' => 'Ӯ', + 'ӱ' => 'Ӱ', + 'ӳ' => 'Ӳ', + 'ӵ' => 'Ӵ', + 'ӷ' => 'Ӷ', + 'ӹ' => 'Ӹ', + 'ӻ' => 'Ӻ', + 'ӽ' => 'Ӽ', + 'ӿ' => 'Ӿ', + 'ԁ' => 'Ԁ', + 'ԃ' => 'Ԃ', + 'ԅ' => 'Ԅ', + 'ԇ' => 'Ԇ', + 'ԉ' => 'Ԉ', + 'ԋ' => 'Ԋ', + 'ԍ' => 'Ԍ', + 'ԏ' => 'Ԏ', + 'ԑ' => 'Ԑ', + 'ԓ' => 'Ԓ', + 'ԕ' => 'Ԕ', + 'ԗ' => 'Ԗ', + 'ԙ' => 'Ԙ', + 'ԛ' => 'Ԛ', + 'ԝ' => 'Ԝ', + 'ԟ' => 'Ԟ', + 'ԡ' => 'Ԡ', + 'ԣ' => 'Ԣ', + 'ԥ' => 'Ԥ', + 'ԧ' => 'Ԧ', + 'ԩ' => 'Ԩ', + 'ԫ' => 'Ԫ', + 'ԭ' => 'Ԭ', + 'ԯ' => 'Ԯ', + 'ա' => 'Ա', + 'բ' => 'Բ', + 'գ' => 'Գ', + 'դ' => 'Դ', + 'ե' => 'Ե', + 'զ' => 'Զ', + 'է' => 'Է', + 'ը' => 'Ը', + 'թ' => 'Թ', + 'ժ' => 'Ժ', + 'ի' => 'Ի', + 'լ' => 'Լ', + 'խ' => 'Խ', + 'ծ' => 'Ծ', + 'կ' => 'Կ', + 'հ' => 'Հ', + 'ձ' => 'Ձ', + 'ղ' => 'Ղ', + 'ճ' => 'Ճ', + 'մ' => 'Մ', + 'յ' => 'Յ', + 'ն' => 'Ն', + 'շ' => 'Շ', + 'ո' => 'Ո', + 'չ' => 'Չ', + 'պ' => 'Պ', + 'ջ' => 'Ջ', + 'ռ' => 'Ռ', + 'ս' => 'Ս', + 'վ' => 'Վ', + 'տ' => 'Տ', + 'ր' => 'Ր', + 'ց' => 'Ց', + 'ւ' => 'Ւ', + 'փ' => 'Փ', + 'ք' => 'Ք', + 'օ' => 'Օ', + 'ֆ' => 'Ֆ', + 'ᵹ' => 'Ᵹ', + 'ᵽ' => 'Ᵽ', + 'ḁ' => 'Ḁ', + 'ḃ' => 'Ḃ', + 'ḅ' => 'Ḅ', + 'ḇ' => 'Ḇ', + 'ḉ' => 'Ḉ', + 'ḋ' => 'Ḋ', + 'ḍ' => 'Ḍ', + 'ḏ' => 'Ḏ', + 'ḑ' => 'Ḑ', + 'ḓ' => 'Ḓ', + 'ḕ' => 'Ḕ', + 'ḗ' => 'Ḗ', + 'ḙ' => 'Ḙ', + 'ḛ' => 'Ḛ', + 'ḝ' => 'Ḝ', + 'ḟ' => 'Ḟ', + 'ḡ' => 'Ḡ', + 'ḣ' => 'Ḣ', + 'ḥ' => 'Ḥ', + 'ḧ' => 'Ḧ', + 'ḩ' => 'Ḩ', + 'ḫ' => 'Ḫ', + 'ḭ' => 'Ḭ', + 'ḯ' => 'Ḯ', + 'ḱ' => 'Ḱ', + 'ḳ' => 'Ḳ', + 'ḵ' => 'Ḵ', + 'ḷ' => 'Ḷ', + 'ḹ' => 'Ḹ', + 'ḻ' => 'Ḻ', + 'ḽ' => 'Ḽ', + 'ḿ' => 'Ḿ', + 'ṁ' => 'Ṁ', + 'ṃ' => 'Ṃ', + 'ṅ' => 'Ṅ', + 'ṇ' => 'Ṇ', + 'ṉ' => 'Ṉ', + 'ṋ' => 'Ṋ', + 'ṍ' => 'Ṍ', + 'ṏ' => 'Ṏ', + 'ṑ' => 'Ṑ', + 'ṓ' => 'Ṓ', + 'ṕ' => 'Ṕ', + 'ṗ' => 'Ṗ', + 'ṙ' => 'Ṙ', + 'ṛ' => 'Ṛ', + 'ṝ' => 'Ṝ', + 'ṟ' => 'Ṟ', + 'ṡ' => 'Ṡ', + 'ṣ' => 'Ṣ', + 'ṥ' => 'Ṥ', + 'ṧ' => 'Ṧ', + 'ṩ' => 'Ṩ', + 'ṫ' => 'Ṫ', + 'ṭ' => 'Ṭ', + 'ṯ' => 'Ṯ', + 'ṱ' => 'Ṱ', + 'ṳ' => 'Ṳ', + 'ṵ' => 'Ṵ', + 'ṷ' => 'Ṷ', + 'ṹ' => 'Ṹ', + 'ṻ' => 'Ṻ', + 'ṽ' => 'Ṽ', + 'ṿ' => 'Ṿ', + 'ẁ' => 'Ẁ', + 'ẃ' => 'Ẃ', + 'ẅ' => 'Ẅ', + 'ẇ' => 'Ẇ', + 'ẉ' => 'Ẉ', + 'ẋ' => 'Ẋ', + 'ẍ' => 'Ẍ', + 'ẏ' => 'Ẏ', + 'ẑ' => 'Ẑ', + 'ẓ' => 'Ẓ', + 'ẕ' => 'Ẕ', + 'ẛ' => 'Ṡ', + 'ạ' => 'Ạ', + 'ả' => 'Ả', + 'ấ' => 'Ấ', + 'ầ' => 'Ầ', + 'ẩ' => 'Ẩ', + 'ẫ' => 'Ẫ', + 'ậ' => 'Ậ', + 'ắ' => 'Ắ', + 'ằ' => 'Ằ', + 'ẳ' => 'Ẳ', + 'ẵ' => 'Ẵ', + 'ặ' => 'Ặ', + 'ẹ' => 'Ẹ', + 'ẻ' => 'Ẻ', + 'ẽ' => 'Ẽ', + 'ế' => 'Ế', + 'ề' => 'Ề', + 'ể' => 'Ể', + 'ễ' => 'Ễ', + 'ệ' => 'Ệ', + 'ỉ' => 'Ỉ', + 'ị' => 'Ị', + 'ọ' => 'Ọ', + 'ỏ' => 'Ỏ', + 'ố' => 'Ố', + 'ồ' => 'Ồ', + 'ổ' => 'Ổ', + 'ỗ' => 'Ỗ', + 'ộ' => 'Ộ', + 'ớ' => 'Ớ', + 'ờ' => 'Ờ', + 'ở' => 'Ở', + 'ỡ' => 'Ỡ', + 'ợ' => 'Ợ', + 'ụ' => 'Ụ', + 'ủ' => 'Ủ', + 'ứ' => 'Ứ', + 'ừ' => 'Ừ', + 'ử' => 'Ử', + 'ữ' => 'Ữ', + 'ự' => 'Ự', + 'ỳ' => 'Ỳ', + 'ỵ' => 'Ỵ', + 'ỷ' => 'Ỷ', + 'ỹ' => 'Ỹ', + 'ỻ' => 'Ỻ', + 'ỽ' => 'Ỽ', + 'ỿ' => 'Ỿ', + 'ἀ' => 'Ἀ', + 'ἁ' => 'Ἁ', + 'ἂ' => 'Ἂ', + 'ἃ' => 'Ἃ', + 'ἄ' => 'Ἄ', + 'ἅ' => 'Ἅ', + 'ἆ' => 'Ἆ', + 'ἇ' => 'Ἇ', + 'ἐ' => 'Ἐ', + 'ἑ' => 'Ἑ', + 'ἒ' => 'Ἒ', + 'ἓ' => 'Ἓ', + 'ἔ' => 'Ἔ', + 'ἕ' => 'Ἕ', + 'ἠ' => 'Ἠ', + 'ἡ' => 'Ἡ', + 'ἢ' => 'Ἢ', + 'ἣ' => 'Ἣ', + 'ἤ' => 'Ἤ', + 'ἥ' => 'Ἥ', + 'ἦ' => 'Ἦ', + 'ἧ' => 'Ἧ', + 'ἰ' => 'Ἰ', + 'ἱ' => 'Ἱ', + 'ἲ' => 'Ἲ', + 'ἳ' => 'Ἳ', + 'ἴ' => 'Ἴ', + 'ἵ' => 'Ἵ', + 'ἶ' => 'Ἶ', + 'ἷ' => 'Ἷ', + 'ὀ' => 'Ὀ', + 'ὁ' => 'Ὁ', + 'ὂ' => 'Ὂ', + 'ὃ' => 'Ὃ', + 'ὄ' => 'Ὄ', + 'ὅ' => 'Ὅ', + 'ὑ' => 'Ὑ', + 'ὓ' => 'Ὓ', + 'ὕ' => 'Ὕ', + 'ὗ' => 'Ὗ', + 'ὠ' => 'Ὠ', + 'ὡ' => 'Ὡ', + 'ὢ' => 'Ὢ', + 'ὣ' => 'Ὣ', + 'ὤ' => 'Ὤ', + 'ὥ' => 'Ὥ', + 'ὦ' => 'Ὦ', + 'ὧ' => 'Ὧ', + 'ὰ' => 'Ὰ', + 'ά' => 'Ά', + 'ὲ' => 'Ὲ', + 'έ' => 'Έ', + 'ὴ' => 'Ὴ', + 'ή' => 'Ή', + 'ὶ' => 'Ὶ', + 'ί' => 'Ί', + 'ὸ' => 'Ὸ', + 'ό' => 'Ό', + 'ὺ' => 'Ὺ', + 'ύ' => 'Ύ', + 'ὼ' => 'Ὼ', + 'ώ' => 'Ώ', + 'ᾀ' => 'ᾈ', + 'ᾁ' => 'ᾉ', + 'ᾂ' => 'ᾊ', + 'ᾃ' => 'ᾋ', + 'ᾄ' => 'ᾌ', + 'ᾅ' => 'ᾍ', + 'ᾆ' => 'ᾎ', + 'ᾇ' => 'ᾏ', + 'ᾐ' => 'ᾘ', + 'ᾑ' => 'ᾙ', + 'ᾒ' => 'ᾚ', + 'ᾓ' => 'ᾛ', + 'ᾔ' => 'ᾜ', + 'ᾕ' => 'ᾝ', + 'ᾖ' => 'ᾞ', + 'ᾗ' => 'ᾟ', + 'ᾠ' => 'ᾨ', + 'ᾡ' => 'ᾩ', + 'ᾢ' => 'ᾪ', + 'ᾣ' => 'ᾫ', + 'ᾤ' => 'ᾬ', + 'ᾥ' => 'ᾭ', + 'ᾦ' => 'ᾮ', + 'ᾧ' => 'ᾯ', + 'ᾰ' => 'Ᾰ', + 'ᾱ' => 'Ᾱ', + 'ᾳ' => 'ᾼ', + 'ι' => 'Ι', + 'ῃ' => 'ῌ', + 'ῐ' => 'Ῐ', + 'ῑ' => 'Ῑ', + 'ῠ' => 'Ῠ', + 'ῡ' => 'Ῡ', + 'ῥ' => 'Ῥ', + 'ῳ' => 'ῼ', + 'ⅎ' => 'Ⅎ', + 'ⅰ' => 'Ⅰ', + 'ⅱ' => 'Ⅱ', + 'ⅲ' => 'Ⅲ', + 'ⅳ' => 'Ⅳ', + 'ⅴ' => 'Ⅴ', + 'ⅵ' => 'Ⅵ', + 'ⅶ' => 'Ⅶ', + 'ⅷ' => 'Ⅷ', + 'ⅸ' => 'Ⅸ', + 'ⅹ' => 'Ⅹ', + 'ⅺ' => 'Ⅺ', + 'ⅻ' => 'Ⅻ', + 'ⅼ' => 'Ⅼ', + 'ⅽ' => 'Ⅽ', + 'ⅾ' => 'Ⅾ', + 'ⅿ' => 'Ⅿ', + 'ↄ' => 'Ↄ', + 'ⓐ' => 'Ⓐ', + 'ⓑ' => 'Ⓑ', + 'ⓒ' => 'Ⓒ', + 'ⓓ' => 'Ⓓ', + 'ⓔ' => 'Ⓔ', + 'ⓕ' => 'Ⓕ', + 'ⓖ' => 'Ⓖ', + 'ⓗ' => 'Ⓗ', + 'ⓘ' => 'Ⓘ', + 'ⓙ' => 'Ⓙ', + 'ⓚ' => 'Ⓚ', + 'ⓛ' => 'Ⓛ', + 'ⓜ' => 'Ⓜ', + 'ⓝ' => 'Ⓝ', + 'ⓞ' => 'Ⓞ', + 'ⓟ' => 'Ⓟ', + 'ⓠ' => 'Ⓠ', + 'ⓡ' => 'Ⓡ', + 'ⓢ' => 'Ⓢ', + 'ⓣ' => 'Ⓣ', + 'ⓤ' => 'Ⓤ', + 'ⓥ' => 'Ⓥ', + 'ⓦ' => 'Ⓦ', + 'ⓧ' => 'Ⓧ', + 'ⓨ' => 'Ⓨ', + 'ⓩ' => 'Ⓩ', + 'ⰰ' => 'Ⰰ', + 'ⰱ' => 'Ⰱ', + 'ⰲ' => 'Ⰲ', + 'ⰳ' => 'Ⰳ', + 'ⰴ' => 'Ⰴ', + 'ⰵ' => 'Ⰵ', + 'ⰶ' => 'Ⰶ', + 'ⰷ' => 'Ⰷ', + 'ⰸ' => 'Ⰸ', + 'ⰹ' => 'Ⰹ', + 'ⰺ' => 'Ⰺ', + 'ⰻ' => 'Ⰻ', + 'ⰼ' => 'Ⰼ', + 'ⰽ' => 'Ⰽ', + 'ⰾ' => 'Ⰾ', + 'ⰿ' => 'Ⰿ', + 'ⱀ' => 'Ⱀ', + 'ⱁ' => 'Ⱁ', + 'ⱂ' => 'Ⱂ', + 'ⱃ' => 'Ⱃ', + 'ⱄ' => 'Ⱄ', + 'ⱅ' => 'Ⱅ', + 'ⱆ' => 'Ⱆ', + 'ⱇ' => 'Ⱇ', + 'ⱈ' => 'Ⱈ', + 'ⱉ' => 'Ⱉ', + 'ⱊ' => 'Ⱊ', + 'ⱋ' => 'Ⱋ', + 'ⱌ' => 'Ⱌ', + 'ⱍ' => 'Ⱍ', + 'ⱎ' => 'Ⱎ', + 'ⱏ' => 'Ⱏ', + 'ⱐ' => 'Ⱐ', + 'ⱑ' => 'Ⱑ', + 'ⱒ' => 'Ⱒ', + 'ⱓ' => 'Ⱓ', + 'ⱔ' => 'Ⱔ', + 'ⱕ' => 'Ⱕ', + 'ⱖ' => 'Ⱖ', + 'ⱗ' => 'Ⱗ', + 'ⱘ' => 'Ⱘ', + 'ⱙ' => 'Ⱙ', + 'ⱚ' => 'Ⱚ', + 'ⱛ' => 'Ⱛ', + 'ⱜ' => 'Ⱜ', + 'ⱝ' => 'Ⱝ', + 'ⱞ' => 'Ⱞ', + 'ⱡ' => 'Ⱡ', + 'ⱥ' => 'Ⱥ', + 'ⱦ' => 'Ⱦ', + 'ⱨ' => 'Ⱨ', + 'ⱪ' => 'Ⱪ', + 'ⱬ' => 'Ⱬ', + 'ⱳ' => 'Ⱳ', + 'ⱶ' => 'Ⱶ', + 'ⲁ' => 'Ⲁ', + 'ⲃ' => 'Ⲃ', + 'ⲅ' => 'Ⲅ', + 'ⲇ' => 'Ⲇ', + 'ⲉ' => 'Ⲉ', + 'ⲋ' => 'Ⲋ', + 'ⲍ' => 'Ⲍ', + 'ⲏ' => 'Ⲏ', + 'ⲑ' => 'Ⲑ', + 'ⲓ' => 'Ⲓ', + 'ⲕ' => 'Ⲕ', + 'ⲗ' => 'Ⲗ', + 'ⲙ' => 'Ⲙ', + 'ⲛ' => 'Ⲛ', + 'ⲝ' => 'Ⲝ', + 'ⲟ' => 'Ⲟ', + 'ⲡ' => 'Ⲡ', + 'ⲣ' => 'Ⲣ', + 'ⲥ' => 'Ⲥ', + 'ⲧ' => 'Ⲧ', + 'ⲩ' => 'Ⲩ', + 'ⲫ' => 'Ⲫ', + 'ⲭ' => 'Ⲭ', + 'ⲯ' => 'Ⲯ', + 'ⲱ' => 'Ⲱ', + 'ⲳ' => 'Ⲳ', + 'ⲵ' => 'Ⲵ', + 'ⲷ' => 'Ⲷ', + 'ⲹ' => 'Ⲹ', + 'ⲻ' => 'Ⲻ', + 'ⲽ' => 'Ⲽ', + 'ⲿ' => 'Ⲿ', + 'ⳁ' => 'Ⳁ', + 'ⳃ' => 'Ⳃ', + 'ⳅ' => 'Ⳅ', + 'ⳇ' => 'Ⳇ', + 'ⳉ' => 'Ⳉ', + 'ⳋ' => 'Ⳋ', + 'ⳍ' => 'Ⳍ', + 'ⳏ' => 'Ⳏ', + 'ⳑ' => 'Ⳑ', + 'ⳓ' => 'Ⳓ', + 'ⳕ' => 'Ⳕ', + 'ⳗ' => 'Ⳗ', + 'ⳙ' => 'Ⳙ', + 'ⳛ' => 'Ⳛ', + 'ⳝ' => 'Ⳝ', + 'ⳟ' => 'Ⳟ', + 'ⳡ' => 'Ⳡ', + 'ⳣ' => 'Ⳣ', + 'ⳬ' => 'Ⳬ', + 'ⳮ' => 'Ⳮ', + 'ⳳ' => 'Ⳳ', + 'ⴀ' => 'Ⴀ', + 'ⴁ' => 'Ⴁ', + 'ⴂ' => 'Ⴂ', + 'ⴃ' => 'Ⴃ', + 'ⴄ' => 'Ⴄ', + 'ⴅ' => 'Ⴅ', + 'ⴆ' => 'Ⴆ', + 'ⴇ' => 'Ⴇ', + 'ⴈ' => 'Ⴈ', + 'ⴉ' => 'Ⴉ', + 'ⴊ' => 'Ⴊ', + 'ⴋ' => 'Ⴋ', + 'ⴌ' => 'Ⴌ', + 'ⴍ' => 'Ⴍ', + 'ⴎ' => 'Ⴎ', + 'ⴏ' => 'Ⴏ', + 'ⴐ' => 'Ⴐ', + 'ⴑ' => 'Ⴑ', + 'ⴒ' => 'Ⴒ', + 'ⴓ' => 'Ⴓ', + 'ⴔ' => 'Ⴔ', + 'ⴕ' => 'Ⴕ', + 'ⴖ' => 'Ⴖ', + 'ⴗ' => 'Ⴗ', + 'ⴘ' => 'Ⴘ', + 'ⴙ' => 'Ⴙ', + 'ⴚ' => 'Ⴚ', + 'ⴛ' => 'Ⴛ', + 'ⴜ' => 'Ⴜ', + 'ⴝ' => 'Ⴝ', + 'ⴞ' => 'Ⴞ', + 'ⴟ' => 'Ⴟ', + 'ⴠ' => 'Ⴠ', + 'ⴡ' => 'Ⴡ', + 'ⴢ' => 'Ⴢ', + 'ⴣ' => 'Ⴣ', + 'ⴤ' => 'Ⴤ', + 'ⴥ' => 'Ⴥ', + 'ⴧ' => 'Ⴧ', + 'ⴭ' => 'Ⴭ', + 'ꙁ' => 'Ꙁ', + 'ꙃ' => 'Ꙃ', + 'ꙅ' => 'Ꙅ', + 'ꙇ' => 'Ꙇ', + 'ꙉ' => 'Ꙉ', + 'ꙋ' => 'Ꙋ', + 'ꙍ' => 'Ꙍ', + 'ꙏ' => 'Ꙏ', + 'ꙑ' => 'Ꙑ', + 'ꙓ' => 'Ꙓ', + 'ꙕ' => 'Ꙕ', + 'ꙗ' => 'Ꙗ', + 'ꙙ' => 'Ꙙ', + 'ꙛ' => 'Ꙛ', + 'ꙝ' => 'Ꙝ', + 'ꙟ' => 'Ꙟ', + 'ꙡ' => 'Ꙡ', + 'ꙣ' => 'Ꙣ', + 'ꙥ' => 'Ꙥ', + 'ꙧ' => 'Ꙧ', + 'ꙩ' => 'Ꙩ', + 'ꙫ' => 'Ꙫ', + 'ꙭ' => 'Ꙭ', + 'ꚁ' => 'Ꚁ', + 'ꚃ' => 'Ꚃ', + 'ꚅ' => 'Ꚅ', + 'ꚇ' => 'Ꚇ', + 'ꚉ' => 'Ꚉ', + 'ꚋ' => 'Ꚋ', + 'ꚍ' => 'Ꚍ', + 'ꚏ' => 'Ꚏ', + 'ꚑ' => 'Ꚑ', + 'ꚓ' => 'Ꚓ', + 'ꚕ' => 'Ꚕ', + 'ꚗ' => 'Ꚗ', + 'ꚙ' => 'Ꚙ', + 'ꚛ' => 'Ꚛ', + 'ꜣ' => 'Ꜣ', + 'ꜥ' => 'Ꜥ', + 'ꜧ' => 'Ꜧ', + 'ꜩ' => 'Ꜩ', + 'ꜫ' => 'Ꜫ', + 'ꜭ' => 'Ꜭ', + 'ꜯ' => 'Ꜯ', + 'ꜳ' => 'Ꜳ', + 'ꜵ' => 'Ꜵ', + 'ꜷ' => 'Ꜷ', + 'ꜹ' => 'Ꜹ', + 'ꜻ' => 'Ꜻ', + 'ꜽ' => 'Ꜽ', + 'ꜿ' => 'Ꜿ', + 'ꝁ' => 'Ꝁ', + 'ꝃ' => 'Ꝃ', + 'ꝅ' => 'Ꝅ', + 'ꝇ' => 'Ꝇ', + 'ꝉ' => 'Ꝉ', + 'ꝋ' => 'Ꝋ', + 'ꝍ' => 'Ꝍ', + 'ꝏ' => 'Ꝏ', + 'ꝑ' => 'Ꝑ', + 'ꝓ' => 'Ꝓ', + 'ꝕ' => 'Ꝕ', + 'ꝗ' => 'Ꝗ', + 'ꝙ' => 'Ꝙ', + 'ꝛ' => 'Ꝛ', + 'ꝝ' => 'Ꝝ', + 'ꝟ' => 'Ꝟ', + 'ꝡ' => 'Ꝡ', + 'ꝣ' => 'Ꝣ', + 'ꝥ' => 'Ꝥ', + 'ꝧ' => 'Ꝧ', + 'ꝩ' => 'Ꝩ', + 'ꝫ' => 'Ꝫ', + 'ꝭ' => 'Ꝭ', + 'ꝯ' => 'Ꝯ', + 'ꝺ' => 'Ꝺ', + 'ꝼ' => 'Ꝼ', + 'ꝿ' => 'Ꝿ', + 'ꞁ' => 'Ꞁ', + 'ꞃ' => 'Ꞃ', + 'ꞅ' => 'Ꞅ', + 'ꞇ' => 'Ꞇ', + 'ꞌ' => 'Ꞌ', + 'ꞑ' => 'Ꞑ', + 'ꞓ' => 'Ꞓ', + 'ꞗ' => 'Ꞗ', + 'ꞙ' => 'Ꞙ', + 'ꞛ' => 'Ꞛ', + 'ꞝ' => 'Ꞝ', + 'ꞟ' => 'Ꞟ', + 'ꞡ' => 'Ꞡ', + 'ꞣ' => 'Ꞣ', + 'ꞥ' => 'Ꞥ', + 'ꞧ' => 'Ꞧ', + 'ꞩ' => 'Ꞩ', + 'a' => 'A', + 'b' => 'B', + 'c' => 'C', + 'd' => 'D', + 'e' => 'E', + 'f' => 'F', + 'g' => 'G', + 'h' => 'H', + 'i' => 'I', + 'j' => 'J', + 'k' => 'K', + 'l' => 'L', + 'm' => 'M', + 'n' => 'N', + 'o' => 'O', + 'p' => 'P', + 'q' => 'Q', + 'r' => 'R', + 's' => 'S', + 't' => 'T', + 'u' => 'U', + 'v' => 'V', + 'w' => 'W', + 'x' => 'X', + 'y' => 'Y', + 'z' => 'Z', + '𐐨' => '𐐀', + '𐐩' => '𐐁', + '𐐪' => '𐐂', + '𐐫' => '𐐃', + '𐐬' => '𐐄', + '𐐭' => '𐐅', + '𐐮' => '𐐆', + '𐐯' => '𐐇', + '𐐰' => '𐐈', + '𐐱' => '𐐉', + '𐐲' => '𐐊', + '𐐳' => '𐐋', + '𐐴' => '𐐌', + '𐐵' => '𐐍', + '𐐶' => '𐐎', + '𐐷' => '𐐏', + '𐐸' => '𐐐', + '𐐹' => '𐐑', + '𐐺' => '𐐒', + '𐐻' => '𐐓', + '𐐼' => '𐐔', + '𐐽' => '𐐕', + '𐐾' => '𐐖', + '𐐿' => '𐐗', + '𐑀' => '𐐘', + '𐑁' => '𐐙', + '𐑂' => '𐐚', + '𐑃' => '𐐛', + '𐑄' => '𐐜', + '𐑅' => '𐐝', + '𐑆' => '𐐞', + '𐑇' => '𐐟', + '𐑈' => '𐐠', + '𐑉' => '𐐡', + '𐑊' => '𐐢', + '𐑋' => '𐐣', + '𐑌' => '𐐤', + '𐑍' => '𐐥', + '𐑎' => '𐐦', + '𐑏' => '𐐧', + '𑣀' => '𑢠', + '𑣁' => '𑢡', + '𑣂' => '𑢢', + '𑣃' => '𑢣', + '𑣄' => '𑢤', + '𑣅' => '𑢥', + '𑣆' => '𑢦', + '𑣇' => '𑢧', + '𑣈' => '𑢨', + '𑣉' => '𑢩', + '𑣊' => '𑢪', + '𑣋' => '𑢫', + '𑣌' => '𑢬', + '𑣍' => '𑢭', + '𑣎' => '𑢮', + '𑣏' => '𑢯', + '𑣐' => '𑢰', + '𑣑' => '𑢱', + '𑣒' => '𑢲', + '𑣓' => '𑢳', + '𑣔' => '𑢴', + '𑣕' => '𑢵', + '𑣖' => '𑢶', + '𑣗' => '𑢷', + '𑣘' => '𑢸', + '𑣙' => '𑢹', + '𑣚' => '𑢺', + '𑣛' => '𑢻', + '𑣜' => '𑢼', + '𑣝' => '𑢽', + '𑣞' => '𑢾', + '𑣟' => '𑢿', +); + +$result =& $data; +unset($data); + +return $result; diff --git a/vendor/symfony/polyfill-mbstring/bootstrap.php b/vendor/symfony/polyfill-mbstring/bootstrap.php new file mode 100644 index 0000000000..3372291064 --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/bootstrap.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Polyfill\Mbstring as p; + +if (!function_exists('mb_strlen')) { + define('MB_CASE_UPPER', 0); + define('MB_CASE_LOWER', 1); + define('MB_CASE_TITLE', 2); + + function mb_convert_encoding($s, $to, $from = null) { return p\Mbstring::mb_convert_encoding($s, $to, $from); } + function mb_decode_mimeheader($s) { return p\Mbstring::mb_decode_mimeheader($s); } + function mb_encode_mimeheader($s, $charset = null, $transferEnc = null, $lf = null, $indent = null) { return p\Mbstring::mb_encode_mimeheader($s, $charset, $transferEnc, $lf, $indent); } + function mb_convert_case($s, $mode, $enc = null) { return p\Mbstring::mb_convert_case($s, $mode, $enc); } + function mb_internal_encoding($enc = null) { return p\Mbstring::mb_internal_encoding($enc); } + function mb_language($lang = null) { return p\Mbstring::mb_language($lang); } + function mb_list_encodings() { return p\Mbstring::mb_list_encodings(); } + function mb_encoding_aliases($encoding) { return p\Mbstring::mb_encoding_aliases($encoding); } + function mb_check_encoding($var = null, $encoding = null) { return p\Mbstring::mb_check_encoding($var, $encoding); } + function mb_detect_encoding($str, $encodingList = null, $strict = false) { return p\Mbstring::mb_detect_encoding($str, $encodingList, $strict); } + function mb_detect_order($encodingList = null) { return p\Mbstring::mb_detect_order($encodingList); } + function mb_parse_str($s, &$result = array()) { parse_str($s, $result); } + function mb_strlen($s, $enc = null) { return p\Mbstring::mb_strlen($s, $enc); } + function mb_strpos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strpos($s, $needle, $offset, $enc); } + function mb_strtolower($s, $enc = null) { return p\Mbstring::mb_strtolower($s, $enc); } + function mb_strtoupper($s, $enc = null) { return p\Mbstring::mb_strtoupper($s, $enc); } + function mb_substitute_character($char = null) { return p\Mbstring::mb_substitute_character($char); } + function mb_substr($s, $start, $length = 2147483647, $enc = null) { return p\Mbstring::mb_substr($s, $start, $length, $enc); } + function mb_stripos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_stripos($s, $needle, $offset, $enc); } + function mb_stristr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_stristr($s, $needle, $part, $enc); } + function mb_strrchr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strrchr($s, $needle, $part, $enc); } + function mb_strrichr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strrichr($s, $needle, $part, $enc); } + function mb_strripos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strripos($s, $needle, $offset, $enc); } + function mb_strrpos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strrpos($s, $needle, $offset, $enc); } + function mb_strstr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strstr($s, $needle, $part, $enc); } + function mb_get_info($type = 'all') { return p\Mbstring::mb_get_info($type); } + function mb_http_output($enc = null) { return p\Mbstring::mb_http_output($enc); } + function mb_strwidth($s, $enc = null) { return p\Mbstring::mb_strwidth($s, $enc); } + function mb_substr_count($haystack, $needle, $enc = null) { return p\Mbstring::mb_substr_count($haystack, $needle, $enc); } + function mb_output_handler($contents, $status) { return p\Mbstring::mb_output_handler($contents, $status); } + function mb_http_input($type = '') { return p\Mbstring::mb_http_input($type); } + function mb_convert_variables($toEncoding, $fromEncoding, &$a = null, &$b = null, &$c = null, &$d = null, &$e = null, &$f = null) { return p\Mbstring::mb_convert_variables($toEncoding, $fromEncoding, $a, $b, $c, $d, $e, $f); } +} +if (!function_exists('mb_chr')) { + function mb_ord($s, $enc = null) { return p\Mbstring::mb_ord($s, $enc); } + function mb_chr($code, $enc = null) { return p\Mbstring::mb_chr($code, $enc); } + function mb_scrub($s, $enc = null) { $enc = null === $enc ? mb_internal_encoding() : $enc; return mb_convert_encoding($s, $enc, $enc); } +} diff --git a/vendor/symfony/polyfill-mbstring/composer.json b/vendor/symfony/polyfill-mbstring/composer.json new file mode 100644 index 0000000000..48fc3ddf3a --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/composer.json @@ -0,0 +1,34 @@ +{ + "name": "symfony/polyfill-mbstring", + "type": "library", + "description": "Symfony polyfill for the Mbstring extension", + "keywords": ["polyfill", "shim", "compatibility", "portable", "mbstring"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.3" + }, + "autoload": { + "psr-4": { "Symfony\\Polyfill\\Mbstring\\": "" }, + "files": [ "bootstrap.php" ] + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + } +} diff --git a/vendor/zendframework/zendxml/.gitignore b/vendor/zendframework/zendxml/.gitignore new file mode 100644 index 0000000000..0a4f6e27f5 --- /dev/null +++ b/vendor/zendframework/zendxml/.gitignore @@ -0,0 +1,5 @@ +composer.lock +vendor +.buildpath +.project +.settings diff --git a/vendor/zendframework/zendxml/.travis.yml b/vendor/zendframework/zendxml/.travis.yml new file mode 100644 index 0000000000..877b0650be --- /dev/null +++ b/vendor/zendframework/zendxml/.travis.yml @@ -0,0 +1,43 @@ +sudo: false + +language: php + +branches: + except: + - /^release-.*$/ + - /^ghgfk-.*$/ + +cache: + directories: + - $HOME/.composer/cache + +matrix: + allow_failures: + - php: hhvm +matrix: + fast_finish: true + include: + - php: 5.3 + - php: 5.4 + - php: 5.5 + env: + - EXECUTE_CS_CHECK=true + - php: 5.6 + - php: 7 + - php: hhvm + allow_failures: + - php: hhvm + +before_install: + - composer self-update + +install: + - travis_retry composer install --no-interaction --ignore-platform-reqs + +script: + - ./vendor/bin/phpunit -c ./tests + - if [[ $EXECUTE_CS_CHECK == 'true' ]]; then ./vendor/bin/phpcs --standard=PSR2 --ignore=tests/Bootstrap.php library tests ; fi + +notifications: + irc: "irc.freenode.org#zftalk.dev" + email: false diff --git a/vendor/zendframework/zendxml/CHANGELOG.md b/vendor/zendframework/zendxml/CHANGELOG.md new file mode 100644 index 0000000000..bca2961b97 --- /dev/null +++ b/vendor/zendframework/zendxml/CHANGELOG.md @@ -0,0 +1,24 @@ +# Changelog + +All notable changes to this project will be documented in this file, in reverse chronological order by release. + +## 1.0.2 - 2016-02-04 + +### Added + +- Nothing. + +### Deprecated + +- Nothing. + +### Removed + +- Nothing. + +### Fixed + +- [#11](https://github.com/zendframework/ZendXml/pull/11) updates the + dependencies to PHP `^5.3.3 || ^7.0` and PHPUnit `^3.7 || ^4.0`, ensuring + better compatibility with other components, and with PHP 7. The test matrix + was also expanded to add PHP 7 as a required platform. diff --git a/vendor/zendframework/zendxml/LICENSE.md b/vendor/zendframework/zendxml/LICENSE.md new file mode 100644 index 0000000000..141d3a2dd4 --- /dev/null +++ b/vendor/zendframework/zendxml/LICENSE.md @@ -0,0 +1,12 @@ +Copyright (c) 2014-2015, Zend Technologies USA, Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +- Neither the name of Zend Technologies USA, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/zendframework/zendxml/README.md b/vendor/zendframework/zendxml/README.md new file mode 100644 index 0000000000..2c67008da9 --- /dev/null +++ b/vendor/zendframework/zendxml/README.md @@ -0,0 +1,50 @@ +ZendXml +======= + +An utility component for XML usage and best practices in PHP + +Installation +------------ + +You can install using: + +``` +curl -s https://getcomposer.org/installer | php +php composer.phar install +``` + +Notice that this library doesn't have any external dependencies, the usage of composer is for autoloading and standard purpose. + + +ZendXml\Security +---------------- + +This is a security component to prevent [XML eXternal Entity](https://www.owasp.org/index.php/XML_External_Entity_%28XXE%29_Processing) (XXE) and [XML Entity Expansion](http://projects.webappsec.org/w/page/13247002/XML%20Entity%20Expansion) (XEE) attacks on XML documents. + +The XXE attack is prevented disabling the load of external entities in the libxml library used by PHP, using the function [libxml_disable_entity_loader](http://www.php.net/manual/en/function.libxml-disable-entity-loader.php). + +The XEE attack is prevented looking inside the XML document for ENTITY usage. If the XML document uses ENTITY the library throw an Exception. + +We have two static methods to scan and load XML document from a string (scan) and from a file (scanFile). You can decide to get a SimpleXMLElement or DOMDocument as result, using the following use cases: + +```php +use ZendXml\Security as XmlSecurity; + +$xml = << + + test + +XML; + +// SimpleXML use case +$simplexml = XmlSecurity::scan($xml); +printf ("SimpleXMLElement: %s\n", ($simplexml instanceof \SimpleXMLElement) ? 'yes' : 'no'); + +// DOMDocument use case +$dom = new \DOMDocument('1.0'); +$dom = XmlSecurity::scan($xml, $dom); +printf ("DOMDocument: %s\n", ($dom instanceof \DOMDocument) ? 'yes' : 'no'); +``` + + diff --git a/vendor/zendframework/zendxml/composer.json b/vendor/zendframework/zendxml/composer.json new file mode 100644 index 0000000000..d9efb22299 --- /dev/null +++ b/vendor/zendframework/zendxml/composer.json @@ -0,0 +1,40 @@ +{ + "name": "zendframework/zendxml", + "description": "Utility library for XML usage, best practices, and security in PHP", + "type": "library", + "license": "BSD-3-Clause", + "keywords": [ + "zf2", + "xml", + "security" + ], + "homepage": "http://packages.zendframework.com/", + "autoload": { + "psr-0": { + "ZendXml\\": "library/" + } + }, + "autoload-dev": { + "psr-4": { + "ZendTest\\Xml\\": "tests/ZendXmlTest/" + } + }, + "repositories": [ + { + "type": "composer", + "url": "http://packages.zendframework.com/" + } + ], + "require": { + "php": "^5.3.3 || ^7.0" + }, + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "require-dev": { + "phpunit/phpunit": "^3.7 || ^4.0", + "squizlabs/php_codesniffer": "^1.5" + } +} diff --git a/vendor/zendframework/zendxml/library/ZendXml/Exception/ExceptionInterface.php b/vendor/zendframework/zendxml/library/ZendXml/Exception/ExceptionInterface.php new file mode 100644 index 0000000000..c55eb9034c --- /dev/null +++ b/vendor/zendframework/zendxml/library/ZendXml/Exception/ExceptionInterface.php @@ -0,0 +1,14 @@ + 0) { + return true; + } + return false; + }, E_WARNING); + $result = $dom->loadXml($xml, LIBXML_NONET); + restore_error_handler(); + + if (!$result) { + // Entity load to previous setting + if (!self::isPhpFpm()) { + libxml_disable_entity_loader($loadEntities); + libxml_use_internal_errors($useInternalXmlErrors); + } + return false; + } + + // Scan for potential XEE attacks using ENTITY, if not PHP-FPM + if (!self::isPhpFpm()) { + foreach ($dom->childNodes as $child) { + if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) { + if ($child->entities->length > 0) { + throw new Exception\RuntimeException(self::ENTITY_DETECT); + } + } + } + } + + // Entity load to previous setting + if (!self::isPhpFpm()) { + libxml_disable_entity_loader($loadEntities); + libxml_use_internal_errors($useInternalXmlErrors); + } + + if (isset($simpleXml)) { + $result = simplexml_import_dom($dom); + if (!$result instanceof SimpleXMLElement) { + return false; + } + return $result; + } + return $dom; + } + + /** + * Scan XML file for potential XXE/XEE attacks + * + * @param string $file + * @param DOMDocument $dom + * @throws Exception\InvalidArgumentException + * @return SimpleXMLElement|DomDocument + */ + public static function scanFile($file, DOMDocument $dom = null) + { + if (!file_exists($file)) { + throw new Exception\InvalidArgumentException( + "The file $file specified doesn't exist" + ); + } + return self::scan(file_get_contents($file), $dom); + } + + /** + * Return true if PHP is running with PHP-FPM + * + * This method is mainly used to determine whether or not heuristic checks + * (vs libxml checks) should be made, due to threading issues in libxml; + * under php-fpm, threading becomes a concern. + * + * However, PHP versions 5.5.22+ and 5.6.6+ contain a patch to the + * libxml support in PHP that makes the libxml checks viable; in such + * versions, this method will return false to enforce those checks, which + * are more strict and accurate than the heuristic checks. + * + * @return boolean + */ + public static function isPhpFpm() + { + $isVulnerableVersion = ( + version_compare(PHP_VERSION, '5.5.22', 'lt') + || ( + version_compare(PHP_VERSION, '5.6', 'gte') + && version_compare(PHP_VERSION, '5.6.6', 'lt') + ) + ); + + if (substr(php_sapi_name(), 0, 3) === 'fpm' && $isVulnerableVersion) { + return true; + } + return false; + } + + /** + * Determine and return the string(s) to use for the $generator) { + $prefix = $generator('<' . '?xml'); + if (0 === strncmp($xml, $prefix, strlen($prefix))) { + return $encoding; + } + } + + // Fallback + return 'UTF-8'; + } + + /** + * Attempt to detect the specified XML encoding. + * + * Using the file's encoding, determines if an "encoding" attribute is + * present and well-formed in the XML declaration; if so, it returns a + * list with both the ASCII representation of that declaration and the + * original file encoding. + * + * If not, a list containing only the provided file encoding is returned. + * + * @param string $xml + * @param string $fileEncoding + * @return string[] Potential XML encodings + */ + protected static function detectXmlEncoding($xml, $fileEncoding) + { + $encodingMap = self::getAsciiEncodingMap(); + $generator = $encodingMap[$fileEncoding]; + $encAttr = $generator('encoding="'); + $quote = $generator('"'); + $close = $generator('>'); + + $closePos = strpos($xml, $close); + if (false === $closePos) { + return array($fileEncoding); + } + + $encPos = strpos($xml, $encAttr); + if (false === $encPos + || $encPos > $closePos + ) { + return array($fileEncoding); + } + + $encPos += strlen($encAttr); + $quotePos = strpos($xml, $quote, $encPos); + if (false === $quotePos) { + return array($fileEncoding); + } + + $encoding = self::substr($xml, $encPos, $quotePos); + return array( + // Following line works because we're only supporting 8-bit safe encodings at this time. + str_replace('\0', '', $encoding), // detected encoding + $fileEncoding, // file encoding + ); + } + + /** + * Return a list of BOM maps. + * + * Returns a list of common encoding -> BOM maps, along with the character + * length to compare against. + * + * @link https://en.wikipedia.org/wiki/Byte_order_mark + * @return array + */ + protected static function getBomMap() + { + return array( + array( + 'encoding' => 'UTF-32BE', + 'bom' => pack('CCCC', 0x00, 0x00, 0xfe, 0xff), + 'length' => 4, + ), + array( + 'encoding' => 'UTF-32LE', + 'bom' => pack('CCCC', 0xff, 0xfe, 0x00, 0x00), + 'length' => 4, + ), + array( + 'encoding' => 'GB-18030', + 'bom' => pack('CCCC', 0x84, 0x31, 0x95, 0x33), + 'length' => 4, + ), + array( + 'encoding' => 'UTF-16BE', + 'bom' => pack('CC', 0xfe, 0xff), + 'length' => 2, + ), + array( + 'encoding' => 'UTF-16LE', + 'bom' => pack('CC', 0xff, 0xfe), + 'length' => 2, + ), + array( + 'encoding' => 'UTF-8', + 'bom' => pack('CCC', 0xef, 0xbb, 0xbf), + 'length' => 3, + ), + ); + } + + /** + * Return a map of encoding => generator pairs. + * + * Returns a map of encoding => generator pairs, where the generator is a + * callable that accepts a string and returns the appropriate byte order + * sequence of that string for the encoding. + * + * @return array + */ + protected static function getAsciiEncodingMap() + { + return array( + 'UTF-32BE' => function ($ascii) { + return preg_replace('/(.)/', "\0\0\0\\1", $ascii); + }, + 'UTF-32LE' => function ($ascii) { + return preg_replace('/(.)/', "\\1\0\0\0", $ascii); + }, + 'UTF-32odd1' => function ($ascii) { + return preg_replace('/(.)/', "\0\\1\0\0", $ascii); + }, + 'UTF-32odd2' => function ($ascii) { + return preg_replace('/(.)/', "\0\0\\1\0", $ascii); + }, + 'UTF-16BE' => function ($ascii) { + return preg_replace('/(.)/', "\0\\1", $ascii); + }, + 'UTF-16LE' => function ($ascii) { + return preg_replace('/(.)/', "\\1\0", $ascii); + }, + 'UTF-8' => function ($ascii) { + return $ascii; + }, + 'GB-18030' => function ($ascii) { + return $ascii; + }, + ); + } + + /** + * Binary-safe substr. + * + * substr() is not binary-safe; this method loops by character to ensure + * multi-byte characters are aggregated correctly. + * + * @param string $string + * @param int $start + * @param int $end + * @return string + */ + protected static function substr($string, $start, $end) + { + $substr = ''; + for ($i = $start; $i < $end; $i += 1) { + $substr .= $string[$i]; + } + return $substr; + } +} diff --git a/vendor/zendframework/zendxml/tests/Bootstrap.php b/vendor/zendframework/zendxml/tests/Bootstrap.php new file mode 100644 index 0000000000..a9d0e6a551 --- /dev/null +++ b/vendor/zendframework/zendxml/tests/Bootstrap.php @@ -0,0 +1,92 @@ +addDirectoryToWhitelist($zfCoreLibrary . '/' . $lastArg); + } elseif (is_file($zfCoreTests . '/' . $lastArg)) { + $codeCoverageFilter->addDirectoryToWhitelist(dirname($zfCoreLibrary . '/' . $lastArg)); + } else { + $codeCoverageFilter->addDirectoryToWhitelist($zfCoreLibrary); + } + + /* + * Omit from code coverage reports the contents of the tests directory + */ + $codeCoverageFilter->addDirectoryToBlacklist($zfCoreTests, ''); + $codeCoverageFilter->addDirectoryToBlacklist(PEAR_INSTALL_DIR, ''); + $codeCoverageFilter->addDirectoryToBlacklist(PHP_LIBDIR, ''); + + unset($codeCoverageFilter); +} + +/* + * Unset global variables that are no longer needed. + */ +unset($phpUnitVersion); diff --git a/vendor/zendframework/zendxml/tests/ZendXmlTest/MultibyteTest.php b/vendor/zendframework/zendxml/tests/ZendXmlTest/MultibyteTest.php new file mode 100644 index 0000000000..165e8fa512 --- /dev/null +++ b/vendor/zendframework/zendxml/tests/ZendXmlTest/MultibyteTest.php @@ -0,0 +1,125 @@ + array('UTF-16LE', pack('CC', 0xff, 0xfe), 3), + 'UTF-16BE' => array('UTF-16BE', pack('CC', 0xfe, 0xff), 3), + 'UTF-32LE' => array('UTF-32LE', pack('CCCC', 0xff, 0xfe, 0x00, 0x00), 4), + 'UTF-32BE' => array('UTF-32BE', pack('CCCC', 0x00, 0x00, 0xfe, 0xff), 4), + ); + } + + public function getXmlWithXXE() + { + return << + +]> + + retrieved: &pocdata; + +XML; + } + + /** + * Invoke ZendXml\Security::heuristicScan with the provided XML. + * + * @param string $xml + * @return void + * @throws Exception\RuntimeException + */ + public function invokeHeuristicScan($xml) + { + $r = new ReflectionMethod('ZendXml\Security', 'heuristicScan'); + $r->setAccessible(true); + return $r->invoke(null, $xml); + } + + /** + * @dataProvider multibyteEncodings + * @group heuristicDetection + */ + public function testDetectsMultibyteXXEVectorsUnderFPMWithEncodedStringMissingBOM($encoding, $bom, $bomLength) + { + $xml = $this->getXmlWithXXE(); + $xml = str_replace('{ENCODING}', $encoding, $xml); + $xml = iconv('UTF-8', $encoding, $xml); + $this->assertNotSame(0, strncmp($xml, $bom, $bomLength)); + $this->setExpectedException('ZendXml\Exception\RuntimeException', 'ENTITY'); + $this->invokeHeuristicScan($xml); + } + + /** + * @dataProvider multibyteEncodings + */ + public function testDetectsMultibyteXXEVectorsUnderFPMWithEncodedStringUsingBOM($encoding, $bom) + { + $xml = $this->getXmlWithXXE(); + $xml = str_replace('{ENCODING}', $encoding, $xml); + $orig = iconv('UTF-8', $encoding, $xml); + $xml = $bom . $orig; + $this->setExpectedException('ZendXml\Exception\RuntimeException', 'ENTITY'); + $this->invokeHeuristicScan($xml); + } + + public function getXmlWithoutXXE() + { + return << + + retrieved: &pocdata; + +XML; + } + + /** + * @dataProvider multibyteEncodings + */ + public function testDoesNotFlagValidMultibyteXmlAsInvalidUnderFPM($encoding) + { + $xml = $this->getXmlWithoutXXE(); + $xml = str_replace('{ENCODING}', $encoding, $xml); + $xml = iconv('UTF-8', $encoding, $xml); + try { + $result = $this->invokeHeuristicScan($xml); + $this->assertNull($result); + } catch (\Exception $e) { + $this->fail('Security scan raised exception when it should not have'); + } + } + + /** + * @dataProvider multibyteEncodings + * @group mixedEncoding + */ + public function testDetectsXXEWhenXMLDocumentEncodingDiffersFromFileEncoding($encoding, $bom) + { + $xml = $this->getXmlWithXXE(); + $xml = str_replace('{ENCODING}', 'UTF-8', $xml); + $xml = iconv('UTF-8', $encoding, $xml); + $xml = $bom . $xml; + $this->setExpectedException('ZendXml\Exception\RuntimeException', 'ENTITY'); + $this->invokeHeuristicScan($xml); + } +} diff --git a/vendor/zendframework/zendxml/tests/ZendXmlTest/SecurityTest.php b/vendor/zendframework/zendxml/tests/ZendXmlTest/SecurityTest.php new file mode 100644 index 0000000000..fa3b30bf14 --- /dev/null +++ b/vendor/zendframework/zendxml/tests/ZendXmlTest/SecurityTest.php @@ -0,0 +1,135 @@ + +]> + + This result is &harmless; + +XML; + + $this->setExpectedException('ZendXml\Exception\RuntimeException'); + $result = XmlSecurity::scan($xml); + } + + public function testScanForXXE() + { + $file = tempnam(sys_get_temp_dir(), 'ZendXml_Security'); + file_put_contents($file, 'This is a remote content!'); + $xml = << + +]> + + &foo; + +XML; + + try { + $result = XmlSecurity::scan($xml); + } catch (Exception\RuntimeException $e) { + unlink($file); + return; + } + $this->fail('An expected exception has not been raised.'); + } + + public function testScanSimpleXmlResult() + { + $result = XmlSecurity::scan($this->getXml()); + $this->assertTrue($result instanceof SimpleXMLElement); + $this->assertEquals($result->result, 'test'); + } + + public function testScanDom() + { + $dom = new DOMDocument('1.0'); + $result = XmlSecurity::scan($this->getXml(), $dom); + $this->assertTrue($result instanceof DOMDocument); + $node = $result->getElementsByTagName('result')->item(0); + $this->assertEquals($node->nodeValue, 'test'); + } + + public function testScanInvalidXml() + { + $xml = <<test +XML; + + $result = XmlSecurity::scan($xml); + $this->assertFalse($result); + } + + public function testScanInvalidXmlDom() + { + $xml = <<test +XML; + + $dom = new DOMDocument('1.0'); + $result = XmlSecurity::scan($xml, $dom); + $this->assertFalse($result); + } + + public function testScanFile() + { + $file = tempnam(sys_get_temp_dir(), 'ZendXml_Security'); + file_put_contents($file, $this->getXml()); + + $result = XmlSecurity::scanFile($file); + $this->assertTrue($result instanceof SimpleXMLElement); + $this->assertEquals($result->result, 'test'); + unlink($file); + } + + public function testScanXmlWithDTD() + { + $xml = << + + +]> + + test + +XML; + + $dom = new DOMDocument('1.0'); + $result = XmlSecurity::scan($xml, $dom); + $this->assertTrue($result instanceof DOMDocument); + $this->assertTrue($result->validate()); + } + + protected function getXml() + { + return << + + test + +XML; + } +} diff --git a/vendor/zendframework/zendxml/tests/phpunit.xml.dist b/vendor/zendframework/zendxml/tests/phpunit.xml.dist new file mode 100755 index 0000000000..069784bd7f --- /dev/null +++ b/vendor/zendframework/zendxml/tests/phpunit.xml.dist @@ -0,0 +1,27 @@ + + + + ./ZendXmlTest + ./ZendXmlTest/TestAsset + + + + + + + + + + + + + + ./ZendXmlTest + ../vendor + + + + + + +

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̒dz(ZXuWiHi+(LXqp&g4*p_aOu6~eU8 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 i+k5}X>yE|ETruO?A*Tt-O6eJ literal 0 HcmV?d00001 diff --git a/vendor/gregwar/captcha/Font/captcha5.ttf b/vendor/gregwar/captcha/Font/captcha5.ttf new file mode 100644 index 0000000000000000000000000000000000000000..f7cd998656b6ca8b1fb2e82fe63fa19f97c4c2c0 GIT binary patch literal 49724 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 literal 0 HcmV?d00001 diff --git a/vendor/gregwar/captcha/ImageFileHandler.php b/vendor/gregwar/captcha/ImageFileHandler.php new file mode 100644 index 0000000000..6b6bdb87e1 --- /dev/null +++ b/vendor/gregwar/captcha/ImageFileHandler.php @@ -0,0 +1,106 @@ + + * @author Jeremy Livingston + */ +class ImageFileHandler +{ + /** + * Name of folder for captcha images + * @var string + */ + protected $imageFolder; + + /** + * Absolute path to public web folder + * @var string + */ + protected $webPath; + + /** + * Frequency of garbage collection in fractions of 1 + * @var int + */ + protected $gcFreq; + + /** + * Maximum age of images in minutes + * @var int + */ + protected $expiration; + + /** + * @param $imageFolder + * @param $webPath + * @param $gcFreq + * @param $expiration + */ + public function __construct($imageFolder, $webPath, $gcFreq, $expiration) + { + $this->imageFolder = $imageFolder; + $this->webPath = $webPath; + $this->gcFreq = $gcFreq; + $this->expiration = $expiration; + } + + /** + * Saves the provided image content as a file + * + * @param string $contents + * + * @return string + */ + public function saveAsFile($contents) + { + $this->createFolderIfMissing(); + + $filename = md5(uniqid()) . '.jpg'; + $filePath = $this->webPath . '/' . $this->imageFolder . '/' . $filename; + imagejpeg($contents, $filePath, 15); + + return '/' . $this->imageFolder . '/' . $filename; + } + + /** + * Randomly runs garbage collection on the image directory + * + * @return bool + */ + public function collectGarbage() + { + if (!mt_rand(1, $this->gcFreq) == 1) { + return false; + } + + $this->createFolderIfMissing(); + + $finder = new Finder(); + $criteria = sprintf('<= now - %s minutes', $this->expiration); + $finder->in($this->webPath . '/' . $this->imageFolder) + ->date($criteria); + + foreach($finder->files() as $file) { + unlink($file->getPathname()); + } + + return true; + } + + /** + * Creates the folder if it doesn't exist + */ + protected function createFolderIfMissing() + { + if (!file_exists($this->webPath . '/' . $this->imageFolder)) { + mkdir($this->webPath . '/' . $this->imageFolder, 0755); + } + } +} + diff --git a/vendor/gregwar/captcha/LICENSE b/vendor/gregwar/captcha/LICENSE new file mode 100644 index 0000000000..7db6ad8e37 --- /dev/null +++ b/vendor/gregwar/captcha/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) <2012-2015> Grégoire Passault + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/gregwar/captcha/PhraseBuilder.php b/vendor/gregwar/captcha/PhraseBuilder.php new file mode 100644 index 0000000000..b94bd61c04 --- /dev/null +++ b/vendor/gregwar/captcha/PhraseBuilder.php @@ -0,0 +1,34 @@ + + */ +class PhraseBuilder implements PhraseBuilderInterface +{ + /** + * Generates random phrase of given length with given charset + */ + public function build($length = 5, $charset = 'abcdefghijklmnpqrstuvwxyz123456789') + { + $phrase = ''; + $chars = str_split($charset); + + for ($i = 0; $i < $length; $i++) { + $phrase .= $chars[array_rand($chars)]; + } + + return $phrase; + } + + /** + * "Niceize" a code + */ + public function niceize($str) + { + return strtr(strtolower($str), '01', 'ol'); + } +} diff --git a/vendor/gregwar/captcha/PhraseBuilderInterface.php b/vendor/gregwar/captcha/PhraseBuilderInterface.php new file mode 100644 index 0000000000..0a4f536175 --- /dev/null +++ b/vendor/gregwar/captcha/PhraseBuilderInterface.php @@ -0,0 +1,21 @@ + + */ +interface PhraseBuilderInterface +{ + /** + * Generates random phrase of given length with given charset + */ + public function build($length, $charset); + + /** + * "Niceize" a code + */ + public function niceize($str); +} diff --git a/vendor/gregwar/captcha/README.md b/vendor/gregwar/captcha/README.md new file mode 100644 index 0000000000..9f6c17a9f1 --- /dev/null +++ b/vendor/gregwar/captcha/README.md @@ -0,0 +1,108 @@ +Captcha +======= + +![Captchas examples](http://gregwar.com/captchas.png) + +Installation +============ + +With composer : + +``` json +{ + ... + "require": { + "gregwar/captcha": "1.*" + } +} +``` + +Usage +===== + +You can create a captcha with the `CaptchaBuilder` : + +```php +build(); +``` + +You can then save it to a file : + +```php +save('out.jpg'); +``` + +Or output it directly : + +```php +output(); +``` + +Or inline it directly in the HTML page: + +```php + +``` + +You'll be able to get the code and compare it with a user input : + +```php +getPhrase(); +``` + +You can compare the phrase with user input: +```php +if($builder->testPhrase($userInput)) { + // instructions if user phrase is good +} +else { + // user phrase is wrong +} +``` + +API +=== + +You can use theses functions : + +* **__construct($phrase = null)**, constructs the builder with the given phrase, if the phrase is null, a random one will be generated +* **getPhrase()**, allow you to get the phrase contents +* **setDistortion($distortion)**, enable or disable the distortion, call it before `build()` +* **isOCRReadable()**, returns `true` if the OCR can be read using the `ocrad` software, you'll need to have shell_exec enabled, imagemagick and ocrad installed +* **buildAgainstOCR($width = 150, $height = 40, $font = null)**, builds a code until it is not readable by `ocrad` +* **build($width = 150, $height = 40, $font = null)**, builds a code with the given $width, $height and $font. By default, a random font will be used from the library +* **save($filename, $quality = 80)**, saves the captcha into a jpeg in the $filename, with the given quality +* **get($quality = 80)**, returns the jpeg data +* **output($quality = 80)**, directly outputs the jpeg code to a browser +* **setBackgroundColor($r, $g, $b)**, sets the background color to force it (this will disable many effects and is not recommended) +* **setBackgroundImages(array($imagepath1, $imagePath2))**, Sets custom background images to be used as captcha background. It is recommended to disable image effects when passing custom images for background (ignore_all_effects). A random image is selected from the list passed, the full paths to the image files must be passed. +* **setInterpolation($interpolate)**, enable or disable the interpolation (enabled by default), disabling it will be quicker but the images will look uglier +* **setIgnoreAllEffects($ignoreAllEffects)**, disable all effects on the captcha image. Recommended to use when passing custom background images for the captcha. +* **testPhrase($phrase)**, returns true if the given phrase is good +* **setMaxBehindLines($lines)**, sets the maximum number of lines behind the code +* **setMaxFrontLines($lines)**, sets the maximum number of lines on the front of the code + +Symfony 2 Bundle +================ + +You can have a look at the following repository to enjoy the Symfony 2 bundle packaging this captcha generator : +https://github.com/Gregwar/CaptchaBundle + +License +======= + +This library is under MIT license, have a look to the `LICENSE` file diff --git a/vendor/gregwar/captcha/autoload.php b/vendor/gregwar/captcha/autoload.php new file mode 100644 index 0000000000..8b3fa3929c --- /dev/null +++ b/vendor/gregwar/captcha/autoload.php @@ -0,0 +1,16 @@ +=5.3.0", + "ext-gd": "*" + }, + "autoload": { + "psr-4": { + "Gregwar\\Captcha\\": "/" + } + } +} diff --git a/vendor/gregwar/captcha/demo/demo.php b/vendor/gregwar/captcha/demo/demo.php new file mode 100644 index 0000000000..7852745fc1 --- /dev/null +++ b/vendor/gregwar/captcha/demo/demo.php @@ -0,0 +1,14 @@ +build() + ->save('out.jpg') +; diff --git a/vendor/gregwar/captcha/demo/fingerprint.php b/vendor/gregwar/captcha/demo/fingerprint.php new file mode 100644 index 0000000000..ce30d991e2 --- /dev/null +++ b/vendor/gregwar/captcha/demo/fingerprint.php @@ -0,0 +1,15 @@ +build() + ->getFingerprint() +); + +echo "\n"; diff --git a/vendor/gregwar/captcha/demo/index.php b/vendor/gregwar/captcha/demo/index.php new file mode 100644 index 0000000000..e543883bb7 --- /dev/null +++ b/vendor/gregwar/captcha/demo/index.php @@ -0,0 +1,15 @@ + + + + + + +