diff --git a/code_depricated/instafeed.1.pl b/code_depricated/instafeed.1.pl new file mode 100755 index 0000000..e92e05b --- /dev/null +++ b/code_depricated/instafeed.1.pl @@ -0,0 +1,170 @@ +#!usr/bin/perl +use strict; +use warnings; +use Data::Dumper; +use JSON::XS qw(encode_json decode_json); +use File::Slurp qw(read_file write_file); +use Getopt::Long qw(GetOptions); + +my %config = ( + 'uploadPHP' => { + 'USERNAME' => 'dreamyourmansion', + 'PASSWORD' => 'H5AZ#dQZ5Ycf', + 'DEBUG' => 1, + 'TRUNCATED_DEBUG' => 1, + 'PROXY_USER' => 'zino%40onlinehome.de', + 'PROXY_PASSWORD' => 'zinomedial33t', + 'PROXY_IP' => 'de435.nordvpn.com', + 'PROXY_PORT' => 80, + }, + 'profile' => undef, + 'imageDir' => './src/images/', + 'SRCRoot' => './src/', + 'DBFilepath' => '/home/pi/instafeed/src/db/db.dat', + 'uploadPHP_CMD' => '/usr/bin/php /home/pi/instafeed/vendor/mgp25/instagram-php/examples/uploadPhoto.php', + 'uploadPHP_DESCRIPTION_ADD' => "The most beautiful real estates in the world!\n\nBenefit from the flourishing housing market in Germany. Contact us now by DM.\n\nVom Mieter zum Eigentümer! Exklusives Portfolio: Kontaktiere uns jetzt per DM.", + 'uploadPHP_TAGS' => '#investment #immobilie #mansionhouse #dream #poolhouse #villa #realestate #loft #awesome #lifestyle #motivation #luxury', +); + +my (%data, %db, %profiles); +my $dbKeysStart = 0; +GetOptions ('profile=s' => \$config{'profile'}) or die "Usage: $0 --profile *name*\n"; +die "Usage: $0 --profile *name*\n" if !$config{'profile'} ; + +# MAIN + +&UndumpFromFile(); +#print Dumper \%db; +&DirectoryListing(); +# print Dumper \%data; +&FindNewDataset(); +&Summary(); + +sub UndumpFromFile { + &Delimiter((caller(0))[3]); + + if (-e $config{'DBFilepath'}) { + my $json = read_file($config{'DBFilepath'}, { binmode => ':raw' }); + if (!$json) { + warn "DB file $config{'DBFilepath'} is empty.\n"; + return; + } + %db = %{ decode_json $json }; + $dbKeysStart = scalar(keys(%db)); + print "INFO: $config{'DBFilepath'} has " . $dbKeysStart . " keys.\n"; + } + elsif (!-e $config{'DBFilepath'}) { + warn "INFO: NO DB file found at $config{'DBFilepath'}\n"; + exit; + } +} + +sub DirectoryListing { + &Delimiter((caller(0))[3]); + + opendir(DIR, $config{'imageDir'}); + my @files = grep(/\.jpg$/,readdir(DIR)); + closedir(DIR); + %data = map { $_ => { 'FILEPATH' => "$config{'imageDir'}$_" } } @files; + # @hash{@keys} = undef; +} + +sub Summary { + &Delimiter((caller(0))[3]); + + print "$config{'DBFilepath'} has " . scalar(keys(%db)) . " keys (before $dbKeysStart).\n"; +} + +sub FindNewDataset { + &Delimiter((caller(0))[3]); + + my $i = 0; + for my $key (keys %data) { + if (exists $db{$key}) { + print "OLD: $key\n"; + } + elsif (!exists $db{$key}) { + print "NEW: $key\n"; + + my $success = &uploadPHP($data{$key}{'FILEPATH'}); + if ($success) { + print "success is $success\n"; + &AddToDB($key); + &WipeData($key); + last; + } + } + $i++; + } + + if ($i == scalar(keys(%data))) { + warn "\nNO NEW FILES AVAILABLE.\n"; + } +} + +sub uploadPHP { + &Delimiter((caller(0))[3]); + my $filepath = shift; + my $success = 1; + my $captionText = "$config{'uploadPHP_DESCRIPTION_ADD'}\n\n$config{'uploadPHP_TAGS'}"; + + open PHPOUT, "$config{'uploadPHP_CMD'} $filepath \'$captionText\' $config{'uploadPHP'}{'USERNAME'} $config{'uploadPHP'}{'PASSWORD'} $config{'uploadPHP'}{'DEBUG'} $config{'uploadPHP'}{'TRUNCATED_DEBUG'} $config{'uploadPHP'}{'PROXY_USER'} $config{'uploadPHP'}{'PROXY_PASSWORD'} $config{'uploadPHP'}{'PROXY_IP'} $config{'uploadPHP'}{'PROXY_PORT'}|"; + while () { + print $_; # PRINT CURRENT PHP OUPUT LINE + if ($_ =~ m/error/) { + $success = 0; + } + } + + return $success; +} + +sub WipeData { + &Delimiter((caller(0))[3]); + my $key = shift; + + print "Deleting $data{$key}{'FILEPATH'}..."; + unlink($data{$key}{'FILEPATH'}) or die "Could not delete $data{$key}{'FILEPATH'}!\n"; + print " Done.\n"; +} + +sub AddToDB { + &Delimiter((caller(0))[3]); + my $key = shift; + + $data{$key}{'TIMESTAMP_UPLOADED'} = &GetTimestamp('YMDHMS'); + $db{$key} = $data{$key}; + my $json = encode_json \%db; + write_file($config{'DBFilepath'}, { binmode => ':raw' }, $json); +} + +sub Delimiter { + my $SubName = shift; + print "\n" . "-" x 80 . "\nSUB " . $SubName . "\n" . '-' x 80 . "\n"; +} + +sub GetTimestamp { + #&Delimiter((caller(0))[3]); + my $switch = shift; + + my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time); + + my $nice_timestamp; + if ($switch eq 'YMDHMS') { + $nice_timestamp = sprintf ( "%04d%02d%02d_%02d%02d%02d", $year+1900,$mon+1,$mday,$hour,$min,$sec); + } + elsif ($switch eq 'YMD') { + $nice_timestamp = sprintf ( "%04d%02d%02d", $year+1900,$mon+1,$mday); + } + elsif ($switch eq 'year') { + $nice_timestamp = $year+1900; + } + elsif ($switch eq 'month') { + $nice_timestamp = $mon+10; + } + else { + print "Invalid/no switch detected. Use: 'YMDHMS' / 'YMD'\n"; + } + + return $nice_timestamp; +} \ No newline at end of file diff --git a/code_depricated/instafeed.pl b/code_depricated/instafeed.pl new file mode 100755 index 0000000..ffc36f0 --- /dev/null +++ b/code_depricated/instafeed.pl @@ -0,0 +1,253 @@ +#!usr/bin/perl +use strict; +use warnings; +use Data::Dumper; +use JSON::XS qw(encode_json decode_json); +use File::Slurp qw(read_file write_file); +use Getopt::Long qw(GetOptions); +use File::Basename; + +my %config = ( + 'profile' => undef, + 'SRCRoot' => './src/', + 'uploadPHP_CMD' => '/usr/bin/php ./vendor/mgp25/instagram-php/examples/uploadPhoto.php', + 'uploadPHP_debug' => 1, + 'uploadPHP_truncated_debug' => 1, +); + +my %profile = ( + 'dreamyourmansion' => { + 'DBFilepath' => './src/db/db_dreamyourmansion.dat', + 'imageDir' => './src/images/dreamyourmansion', + 'filename_as_title' => 0, + 'uploadPHP' => { + 'username' => 'dreamyourmansion', + 'password' => 'nBLT!4H3aI@c', + 'truncated_debug' => 1, + 'proxy_user' => 'zino%40onlinehome.de', + 'proxy_password' => 'zinomedial33t', + 'proxy_ip' => 'de435.nordvpn.com', + 'proxy_port' => 80, + 'tags' => '#investment #immobilie #mansionhouse #dream #poolhouse #villa #realestate #loft #awesome #lifestyle #motivation #luxury', + 'description_add' => "The most beautiful real estates in the world!\n\nBenefit from the flourishing housing market in Germany. Contact us now by DM.\n\nVom Mieter zum Eigentümer! Exklusives Portfolio: Kontaktiere uns jetzt per DM.", + }, + }, + 'vstbestprices' => { + 'DBFilepath' => './src/db/db_vstbestprices.dat', + 'imageDir' => './src/images/vstbestprices', + 'filename_as_title' => 1, + 'uploadPHP' => { + 'username' => 'vstbestprices', + 'password' => 'Vst#1337vst#1337', + 'truncated_debug' => 1, + 'proxy_user' => 'zino%40onlinehome.de', + 'proxy_password' => 'zinomedial33t', + 'proxy_ip' => 'de435.nordvpn.com', + 'proxy_port' => 80, + 'tags' => '#Beats #FLStudio20 #Producer #Ableton #Beatmaker #Studio #ProTools #Music #DAW #LogicPro #FruityLoops #VST #VSTplugins #NativeInstruments #MIDI #Drums #AutoTune #Spectrasonics #Omnisphere #AutoTune #Plugins #Keyscape #Trilian #Logic', + 'description_add' => 'INSTALLATION SUPPORT is included in all prices so you can relax and focus on producing!', + }, + }, + 'vstbestprices_testing' => { + 'DBFilepath' => './src/db/db_vstbestprices.dat', + 'imageDir' => './src/images/vstbestprices', + 'filename_as_title' => 1, + 'uploadPHP' => { + 'username' => 'adobebestprices', + 'password' => 'vst#1337', + 'truncated_debug' => 1, + 'proxy_user' => 'zino%40onlinehome.de', + 'proxy_password' => 'zinomedial33t', + 'proxy_ip' => 'de435.nordvpn.com', + 'proxy_port' => 80, + 'tags' => '#Beats #FLStudio20 #Producer #Ableton #Beatmaker #Studio #ProTools #Music #DAW #LogicPro #FruityLoops #VST #VSTplugins #NativeInstruments #MIDI #Drums #AutoTune #Spectrasonics #Omnisphere #AutoTune #Plugins #Keyscape #Trilian #Logic', + 'description_add' => 'INSTALLATION SUPPORT is included in all prices so you can relax and focus on producing!', + }, + }, + 'adobebestprices' => { + 'DBFilepath' => './src/db/db_adobebestprices.dat', + 'imageDir' => './src/images/adobebestprices/', + 'filename_as_title' => 0, + 'uploadPHP' => { + 'username' => 'adobebestprices', + 'password' => 'vst#1337', + 'truncated_debug' => 1, + 'proxy_user' => 'zino%40onlinehome.de', + 'proxy_password' => 'zinomedial33t', + 'proxy_ip' => 'de435.nordvpn.com', + 'proxy_port' => 80, + 'tags' => '#adobe #photoshop #adobeillustrator #vector #illustrator #adobephotoshop #vectorart #graphicdesign #aftereffects #logo #cs6 #lightroom #graphic', + 'description_add' => 'Photoshop, Lightroom, Illustrator, Dreamviewer, Premiere for WIN & MAC | Installation support is included in all our prices!', + }, + }, +); + +my (%data, %db); +my $dbKeysStart = 0; +my $profile; + +# MAIN +&CheckParameter(); +&UndumpFromFile(); +#print Dumper \%db; +&DirectoryListing(); +print Dumper \%data; +&FindNewDataset(); +&Summary(); + +sub CheckParameter { + &Delimiter((caller(0))[3]); + + GetOptions ('profile=s' => \$config{'profile'}) or die "Usage: $0 --profile *name*\n"; + die "Usage: $0 --profile *name*\n" if !$config{'profile'} ; + $profile = $config{'profile'}; + if (!exists $profile{$profile}) { + print "Template for profile '$profile' does not exist. Following templates are available:\n"; + print "'$_' " for keys(%profile); + print "\n"; + die; + } +} + +sub UndumpFromFile { + &Delimiter((caller(0))[3]); + + if (-e $profile{$profile}{'DBFilepath'}) { + my $json = read_file($profile{$profile}{'DBFilepath'}, { binmode => ':raw' }); + if (!$json) { + warn "DB file $profile{$profile}{'DBFilepath'} is empty.\n"; + return; + } + %db = %{ decode_json $json }; + $dbKeysStart = scalar(keys(%db)); + print "INFO: $profile{$profile}{'DBFilepath'} has " . $dbKeysStart . " keys.\n"; + } + elsif (!-e $profile{$profile}{'DBFilepath'}) { + print "INFO: NO DB file found at $profile{$profile}{'DBFilepath'}. Creating now... "; + write_file($profile{$profile}{'DBFilepath'}, ''); + print "done.\n"; + die "Please restart."; + # &UndumpFromFile(); + + } +} + +sub DirectoryListing { + &Delimiter((caller(0))[3]); + + # opendir(DIR, $profile{$profile}{'imageDir'}); + # my @files = grep(/\.jpg$|\.png$|\.jpeg$|/,readdir(DIR)); + # closedir(DIR); + my @files = glob ( "$profile{$profile}{'imageDir'}/*" ); + %data = map { $_ => { 'FILEPATH' => "$_" } } @files; +} + +sub Summary { + &Delimiter((caller(0))[3]); + + print "$profile{$profile}{'DBFilepath'} has " . scalar(keys(%db)) . " keys (before $dbKeysStart).\n"; +} + +sub FindNewDataset { + &Delimiter((caller(0))[3]); + + my $i = 0; + for my $key (keys %data) { + if (exists $db{$key}) { + print "OLD: $key\n"; + } + elsif (!exists $db{$key}) { + print "NEW: $key\n"; + + my $success = &uploadPHP($data{$key}{'FILEPATH'}); + &AddToDB($key); + &WipeData($key); + last; + # if ($success) { + # print "success is $success\n"; + # &AddToDB($key); + # &WipeData($key); + # last; + # } + } + $i++; + } + + if ($i == scalar(keys(%data))) { + warn "\nNO NEW FILES AVAILABLE.\n"; + } +} + +sub uploadPHP { + &Delimiter((caller(0))[3]); + my $filepath = shift; + + my $success = 1; + my $captionText = "$profile{$profile}{'uploadPHP'}{'description_add'}\n\n$profile{$profile}{'uploadPHP'}{'tags'}"; + if ($profile{$profile}{'filename_as_title'}) { + my $filename = basename($filepath); + $filename =~ s/(NO INSTALL)|(SymLink Installer)//g; + $filename =~ s/( , )|(\.[^.]+$)//g; + $captionText = "$filename\n\n" . $captionText; + # print Dumper $captionText; + } + open PHPOUT, "$config{'uploadPHP_CMD'} \'$filepath\' \'$captionText\' $profile{$profile}{'uploadPHP'}{'username'} $profile{$profile}{'uploadPHP'}{'password'} $config{'uploadPHP_debug'} $config{'uploadPHP_truncated_debug'} $profile{$profile}{'uploadPHP'}{'proxy_user'} $profile{$profile}{'uploadPHP'}{'proxy_password'} $profile{$profile}{'uploadPHP'}{'proxy_ip'} $profile{$profile}{'uploadPHP'}{'proxy_port'}|"; + while () { + print $_; # PRINT CURRENT PHP OUPUT LINE + if ($_ =~ m/error/) { + $success = 0; + } + } + + return $success; +} + +sub WipeData { + &Delimiter((caller(0))[3]); + my $key = shift; + + print "Deleting $data{$key}{'FILEPATH'}..."; + unlink($data{$key}{'FILEPATH'}) or die "Could not delete $data{$key}{'FILEPATH'}!\n"; + print " Done.\n"; +} + +sub AddToDB { + &Delimiter((caller(0))[3]); + my $key = shift; + + $data{$key}{'TIMESTAMP_UPLOADED'} = &GetTimestamp('YMDHMS'); + $db{$key} = $data{$key}; + my $json = encode_json \%db; + write_file($profile{$profile}{'DBFilepath'}, { binmode => ':raw' }, $json); +} + +sub Delimiter { + my $SubName = shift; + print "\n" . "-" x 80 . "\nSUB " . $SubName . "\n" . '-' x 80 . "\n"; +} + +sub GetTimestamp { + #&Delimiter((caller(0))[3]); + my $switch = shift; + + my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time); + + my $nice_timestamp; + if ($switch eq 'YMDHMS') { + $nice_timestamp = sprintf ( "%04d%02d%02d_%02d%02d%02d", $year+1900,$mon+1,$mday,$hour,$min,$sec); + } + elsif ($switch eq 'YMD') { + $nice_timestamp = sprintf ( "%04d%02d%02d", $year+1900,$mon+1,$mday); + } + elsif ($switch eq 'year') { + $nice_timestamp = $year+1900; + } + elsif ($switch eq 'month') { + $nice_timestamp = $mon+10; + } + else { + print "Invalid/no switch detected. Use: 'YMDHMS' / 'YMD'\n"; + } + + return $nice_timestamp; +} \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100755 index 0000000..ef0128b --- /dev/null +++ b/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "mgp25/instagram-php": "^7.0" + } +} diff --git a/composer.lock b/composer.lock new file mode 100755 index 0000000..d418520 --- /dev/null +++ b/composer.lock @@ -0,0 +1,1294 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "002cf77f209f09b01600428b67fd422d", + "packages": [ + { + "name": "binsoul/net-mqtt", + "version": "0.2.1", + "source": { + "type": "git", + "url": "https://github.com/binsoul/net-mqtt.git", + "reference": "286b28e6014739b19e0e7ce0cd5871cdd0cef9b3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/binsoul/net-mqtt/zipball/286b28e6014739b19e0e7ce0cd5871cdd0cef9b3", + "reference": "286b28e6014739b19e0e7ce0cd5871cdd0cef9b3", + "shasum": "" + }, + "require": { + "php": "~5.6|~7.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~1.0", + "phpunit/phpunit": "~4.0||~5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "BinSoul\\Net\\Mqtt\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sebastian Mößler", + "email": "code@binsoul.de", + "homepage": "https://github.com/binsoul", + "role": "Developer" + } + ], + "description": "MQTT protocol implementation", + "homepage": "https://github.com/binsoul/net-mqtt", + "keywords": [ + "mqtt", + "net" + ], + "time": "2017-04-03T20:17:02+00:00" + }, + { + "name": "binsoul/net-mqtt-client-react", + "version": "0.3.2", + "source": { + "type": "git", + "url": "https://github.com/binsoul/net-mqtt-client-react.git", + "reference": "6a80fea50e927ebb8bb8a631ea7903c22742ded5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/binsoul/net-mqtt-client-react/zipball/6a80fea50e927ebb8bb8a631ea7903c22742ded5", + "reference": "6a80fea50e927ebb8bb8a631ea7903c22742ded5", + "shasum": "" + }, + "require": { + "binsoul/net-mqtt": "~0.2", + "php": "~5.6|~7.0", + "react/promise": "~2.0", + "react/socket": "~0.8" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~1.0", + "phpunit/phpunit": "~4.0||~5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "BinSoul\\Net\\Mqtt\\Client\\React\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sebastian Mößler", + "email": "code@binsoul.de", + "homepage": "https://github.com/binsoul", + "role": "Developer" + } + ], + "description": "Asynchronous MQTT client built on React", + "homepage": "https://github.com/binsoul/net-mqtt-client-react", + "keywords": [ + "client", + "mqtt", + "net" + ], + "time": "2017-08-20T08:06:53+00:00" + }, + { + "name": "clue/http-proxy-react", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/clue/php-http-proxy-react.git", + "reference": "eeff725640ed53386a6adb05ffdbfc2837404fdf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/clue/php-http-proxy-react/zipball/eeff725640ed53386a6adb05ffdbfc2837404fdf", + "reference": "eeff725640ed53386a6adb05ffdbfc2837404fdf", + "shasum": "" + }, + "require": { + "php": ">=5.3", + "react/promise": " ^2.1 || ^1.2.1", + "react/socket": "^1.0 || ^0.8.4", + "ringcentral/psr7": "^1.2" + }, + "require-dev": { + "clue/block-react": "^1.1", + "phpunit/phpunit": "^5.0 || ^4.8", + "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Clue\\React\\HttpProxy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@lueck.tv" + } + ], + "description": "Async HTTP proxy connector, use any TCP/IP-based protocol through an HTTP CONNECT proxy server, built on top of ReactPHP", + "homepage": "https://github.com/clue/php-http-proxy-react", + "keywords": [ + "async", + "connect", + "http", + "proxy", + "reactphp" + ], + "time": "2018-02-13T16:31:32+00:00" + }, + { + "name": "clue/socks-react", + "version": "v0.8.7", + "source": { + "type": "git", + "url": "https://github.com/clue/php-socks-react.git", + "reference": "0fcd6f2f506918ff003f1b995c6e78443f26e8ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/clue/php-socks-react/zipball/0fcd6f2f506918ff003f1b995c6e78443f26e8ea", + "reference": "0fcd6f2f506918ff003f1b995c6e78443f26e8ea", + "shasum": "" + }, + "require": { + "evenement/evenement": "~3.0|~1.0|~2.0", + "php": ">=5.3", + "react/promise": "^2.1 || ^1.2", + "react/socket": "^1.0 || ^0.8.6" + }, + "require-dev": { + "clue/block-react": "^1.1", + "clue/connection-manager-extra": "^1.0 || ^0.7", + "phpunit/phpunit": "^6.0 || ^5.7 || ^4.8.35", + "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Clue\\React\\Socks\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@lueck.tv" + } + ], + "description": "Async SOCKS4, SOCKS4a and SOCKS5 proxy client and server implementation, built on top of ReactPHP", + "homepage": "https://github.com/clue/php-socks-react", + "keywords": [ + "async", + "proxy", + "reactphp", + "socks client", + "socks protocol", + "socks server", + "tcp tunnel" + ], + "time": "2017-12-17T14:47:58+00:00" + }, + { + "name": "corneltek/getoptionkit", + "version": "2.6.0", + "source": { + "type": "git", + "url": "https://github.com/c9s/GetOptionKit.git", + "reference": "995607ddf4fc90ebdb4a7d58fe972d581ad8495f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/c9s/GetOptionKit/zipball/995607ddf4fc90ebdb4a7d58fe972d581ad8495f", + "reference": "995607ddf4fc90ebdb4a7d58fe972d581ad8495f", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6.x-dev" + } + }, + "autoload": { + "psr-4": { + "GetOptionKit\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Yo-An Lin", + "email": "yoanlin93@gmail.com" + } + ], + "description": "Powerful command-line option toolkit", + "homepage": "http://github.com/c9s/GetOptionKit", + "time": "2017-06-30T14:54:48+00:00" + }, + { + "name": "evenement/evenement", + "version": "v3.0.1", + "source": { + "type": "git", + "url": "https://github.com/igorw/evenement.git", + "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/igorw/evenement/zipball/531bfb9d15f8aa57454f5f0285b18bec903b8fb7", + "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7", + "shasum": "" + }, + "require": { + "php": ">=7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "Evenement": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + } + ], + "description": "Événement is a very simple event dispatching library for PHP", + "keywords": [ + "event-dispatcher", + "event-emitter" + ], + "time": "2017-07-23T21:35:13+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "6.4.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "0895c932405407fd3a7368b6910c09a24d26db11" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/0895c932405407fd3a7368b6910c09a24d26db11", + "reference": "0895c932405407fd3a7368b6910c09a24d26db11", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^1.0", + "guzzlehttp/psr7": "^1.6.1", + "php": ">=5.5" + }, + "require-dev": { + "ext-curl": "*", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", + "psr/log": "^1.1" + }, + "suggest": { + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "6.3-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "rest", + "web service" + ], + "time": "2019-10-23T15:58:00+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "v1.3.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646", + "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646", + "shasum": "" + }, + "require": { + "php": ">=5.5.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "time": "2016-12-20T10:07:11+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "1.6.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "239400de7a173fe9901b9ac7c06497751f00727a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/239400de7a173fe9901b9ac7c06497751f00727a", + "reference": "239400de7a173fe9901b9ac7c06497751f00727a", + "shasum": "" + }, + "require": { + "php": ">=5.4.0", + "psr/http-message": "~1.0", + "ralouphie/getallheaders": "^2.0.5 || ^3.0.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "ext-zlib": "*", + "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8" + }, + "suggest": { + "zendframework/zend-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Schultze", + "homepage": "https://github.com/Tobion" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "time": "2019-07-01T23:21:34+00:00" + }, + { + "name": "lazyjsonmapper/lazyjsonmapper", + "version": "v1.6.3", + "source": { + "type": "git", + "url": "https://github.com/lazyjsonmapper/lazyjsonmapper.git", + "reference": "51e093b50f4de15d2d64548b3ca743713eed6ee9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/lazyjsonmapper/lazyjsonmapper/zipball/51e093b50f4de15d2d64548b3ca743713eed6ee9", + "reference": "51e093b50f4de15d2d64548b3ca743713eed6ee9", + "shasum": "" + }, + "require": { + "corneltek/getoptionkit": "2.*", + "php": ">=5.6" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.7.1", + "phpunit/phpunit": "6.*" + }, + "bin": [ + "bin/lazydoctor" + ], + "type": "library", + "autoload": { + "psr-4": { + "LazyJsonMapper\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "SteveJobzniak", + "role": "Developer", + "homepage": "https://github.com/SteveJobzniak" + } + ], + "description": "Advanced, intelligent & automatic object-oriented JSON containers for PHP.", + "homepage": "https://github.com/SteveJobzniak/LazyJsonMapper", + "keywords": [ + "development", + "json" + ], + "time": "2018-05-02T16:57:09+00:00" + }, + { + "name": "mgp25/instagram-php", + "version": "v7.0.1", + "source": { + "type": "git", + "url": "https://github.com/mgp25/Instagram-API.git", + "reference": "53421f90b9ef7743f1c6221c4963f2b9f7a592e8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mgp25/Instagram-API/zipball/53421f90b9ef7743f1c6221c4963f2b9f7a592e8", + "reference": "53421f90b9ef7743f1c6221c4963f2b9f7a592e8", + "shasum": "" + }, + "require": { + "binsoul/net-mqtt-client-react": "^0.3.2", + "clue/http-proxy-react": "^1.1.0", + "clue/socks-react": "^0.8.2", + "ext-bcmath": "*", + "ext-curl": "*", + "ext-exif": "*", + "ext-gd": "*", + "ext-mbstring": "*", + "ext-zlib": "*", + "guzzlehttp/guzzle": "^6.2", + "lazyjsonmapper/lazyjsonmapper": "^1.6.1", + "php": ">=5.6", + "psr/log": "^1.0", + "react/event-loop": "^0.4.3", + "react/promise": "^2.5", + "react/socket": "^0.8", + "symfony/process": "^3.4|^4.0", + "valga/fbns-react": "^0.1.8", + "winbox/args": "1.0.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.11.0", + "monolog/monolog": "^1.23", + "phpunit/phpunit": "^5.7 || ^6.2", + "react/http": "^0.7.2" + }, + "suggest": { + "ext-event": "Installing PHP's native Event extension enables faster Realtime class event handling." + }, + "type": "library", + "autoload": { + "psr-4": { + "InstagramAPI\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "RPL-1.5", + "proprietary" + ], + "authors": [ + { + "name": "mgp25", + "email": "me@mgp25.com", + "role": "Founder" + }, + { + "name": "SteveJobzniak", + "homepage": "https://github.com/SteveJobzniak", + "role": "Developer" + } + ], + "description": "Instagram's private API for PHP", + "keywords": [ + "api", + "instagram", + "php", + "private" + ], + "time": "2019-09-17T00:56:42+00:00" + }, + { + "name": "psr/http-message", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "time": "2016-08-06T14:39:51+00:00" + }, + { + "name": "psr/log", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "bf73deb2b3b896a9d9c75f3f0d88185d2faa27e2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/bf73deb2b3b896a9d9c75f3f0d88185d2faa27e2", + "reference": "bf73deb2b3b896a9d9c75f3f0d88185d2faa27e2", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "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" + ], + "time": "2019-10-25T08:06:51+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "react/cache", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/cache.git", + "reference": "aa10d63a1b40a36a486bdf527f28bac607ee6466" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/cache/zipball/aa10d63a1b40a36a486bdf527f28bac607ee6466", + "reference": "aa10d63a1b40a36a486bdf527f28bac607ee6466", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "react/promise": "~2.0|~1.1" + }, + "require-dev": { + "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Async, Promise-based cache interface for ReactPHP", + "keywords": [ + "cache", + "caching", + "promise", + "reactphp" + ], + "time": "2019-07-11T13:45:28+00:00" + }, + { + "name": "react/dns", + "version": "v0.4.19", + "source": { + "type": "git", + "url": "https://github.com/reactphp/dns.git", + "reference": "6852fb98e22d2e5bb35fe5aeeaa96551b120e7c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/dns/zipball/6852fb98e22d2e5bb35fe5aeeaa96551b120e7c9", + "reference": "6852fb98e22d2e5bb35fe5aeeaa96551b120e7c9", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "react/cache": "^1.0 || ^0.6 || ^0.5", + "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5", + "react/promise": "^2.1 || ^1.2.1", + "react/promise-timer": "^1.2", + "react/stream": "^1.0 || ^0.7 || ^0.6 || ^0.5 || ^0.4.5" + }, + "require-dev": { + "clue/block-react": "^1.2", + "phpunit/phpunit": "^7.0 || ^6.4 || ^5.7 || ^4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Dns\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Async DNS resolver for ReactPHP", + "keywords": [ + "async", + "dns", + "dns-resolver", + "reactphp" + ], + "time": "2019-07-10T21:00:53+00:00" + }, + { + "name": "react/event-loop", + "version": "v0.4.3", + "source": { + "type": "git", + "url": "https://github.com/reactphp/event-loop.git", + "reference": "8bde03488ee897dc6bb3d91e4e17c353f9c5252f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/event-loop/zipball/8bde03488ee897dc6bb3d91e4e17c353f9c5252f", + "reference": "8bde03488ee897dc6bb3d91e4e17c353f9c5252f", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.8" + }, + "suggest": { + "ext-event": "~1.0", + "ext-libev": "*", + "ext-libevent": ">=0.1.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\EventLoop\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Event loop abstraction layer that libraries can use for evented I/O.", + "keywords": [ + "asynchronous", + "event-loop" + ], + "time": "2017-04-27T10:56:23+00:00" + }, + { + "name": "react/promise", + "version": "v2.7.1", + "source": { + "type": "git", + "url": "https://github.com/reactphp/promise.git", + "reference": "31ffa96f8d2ed0341a57848cbb84d88b89dd664d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/promise/zipball/31ffa96f8d2ed0341a57848cbb84d88b89dd664d", + "reference": "31ffa96f8d2ed0341a57848cbb84d88b89dd664d", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Promise\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com" + } + ], + "description": "A lightweight implementation of CommonJS Promises/A for PHP", + "keywords": [ + "promise", + "promises" + ], + "time": "2019-01-07T21:25:54+00:00" + }, + { + "name": "react/promise-timer", + "version": "v1.5.1", + "source": { + "type": "git", + "url": "https://github.com/reactphp/promise-timer.git", + "reference": "35fb910604fd86b00023fc5cda477c8074ad0abc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/promise-timer/zipball/35fb910604fd86b00023fc5cda477c8074ad0abc", + "reference": "35fb910604fd86b00023fc5cda477c8074ad0abc", + "shasum": "" + }, + "require": { + "php": ">=5.3", + "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5", + "react/promise": "^2.7.0 || ^1.2.1" + }, + "require-dev": { + "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Promise\\Timer\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@lueck.tv" + } + ], + "description": "A trivial implementation of timeouts for Promises, built on top of ReactPHP.", + "homepage": "https://github.com/reactphp/promise-timer", + "keywords": [ + "async", + "event-loop", + "promise", + "reactphp", + "timeout", + "timer" + ], + "time": "2019-03-27T18:10:32+00:00" + }, + { + "name": "react/socket", + "version": "v0.8.12", + "source": { + "type": "git", + "url": "https://github.com/reactphp/socket.git", + "reference": "7f7e6c56ccda7418a1a264892a625f38a5bdee0c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/socket/zipball/7f7e6c56ccda7418a1a264892a625f38a5bdee0c", + "reference": "7f7e6c56ccda7418a1a264892a625f38a5bdee0c", + "shasum": "" + }, + "require": { + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.0", + "react/dns": "^0.4.13", + "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5", + "react/promise": "^2.6.0 || ^1.2.1", + "react/promise-timer": "^1.4.0", + "react/stream": "^1.0 || ^0.7.1" + }, + "require-dev": { + "clue/block-react": "^1.2", + "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Socket\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP", + "keywords": [ + "Connection", + "Socket", + "async", + "reactphp", + "stream" + ], + "time": "2018-06-11T14:33:43+00:00" + }, + { + "name": "react/stream", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/stream.git", + "reference": "50426855f7a77ddf43b9266c22320df5bf6c6ce6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/stream/zipball/50426855f7a77ddf43b9266c22320df5bf6c6ce6", + "reference": "50426855f7a77ddf43b9266c22320df5bf6c6ce6", + "shasum": "" + }, + "require": { + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.8", + "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5" + }, + "require-dev": { + "clue/stream-filter": "~1.2", + "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Stream\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP", + "keywords": [ + "event-driven", + "io", + "non-blocking", + "pipe", + "reactphp", + "readable", + "stream", + "writable" + ], + "time": "2019-01-01T16:15:09+00:00" + }, + { + "name": "ringcentral/psr7", + "version": "1.2.2", + "source": { + "type": "git", + "url": "https://github.com/ringcentral/psr7.git", + "reference": "dcd84bbb49b96c616d1dcc8bfb9bef3f2cd53d1c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ringcentral/psr7/zipball/dcd84bbb49b96c616d1dcc8bfb9bef3f2cd53d1c", + "reference": "dcd84bbb49b96c616d1dcc8bfb9bef3f2cd53d1c", + "shasum": "" + }, + "require": { + "php": ">=5.3", + "psr/http-message": "~1.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "RingCentral\\Psr7\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "PSR-7 message implementation", + "keywords": [ + "http", + "message", + "stream", + "uri" + ], + "time": "2018-01-15T21:00:49+00:00" + }, + { + "name": "symfony/process", + "version": "v4.3.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "50556892f3cc47d4200bfd1075314139c4c9ff4b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/50556892f3cc47d4200bfd1075314139c4c9ff4b", + "reference": "50556892f3cc47d4200bfd1075314139c4c9ff4b", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "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 Process Component", + "homepage": "https://symfony.com", + "time": "2019-09-26T21:17:10+00:00" + }, + { + "name": "valga/fbns-react", + "version": "0.1.8", + "source": { + "type": "git", + "url": "https://github.com/valga/fbns-react.git", + "reference": "4bbf513a8ffed7e0c9ca10776033d34515bb8b37" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/valga/fbns-react/zipball/4bbf513a8ffed7e0c9ca10776033d34515bb8b37", + "reference": "4bbf513a8ffed7e0c9ca10776033d34515bb8b37", + "shasum": "" + }, + "require": { + "binsoul/net-mqtt": "~0.2", + "evenement/evenement": "~2.0|~3.0", + "ext-mbstring": "*", + "ext-zlib": "*", + "php": "~5.6|~7.0", + "psr/log": "~1.0", + "react/event-loop": "^0.4.3", + "react/promise": "~2.0", + "react/socket": "~0.8" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~2.4", + "monolog/monolog": "~1.23" + }, + "suggest": { + "ext-event": "For more efficient event loop implementation.", + "ext-gmp": "To be able to run this code on x86 PHP builds." + }, + "type": "library", + "autoload": { + "psr-4": { + "Fbns\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Abyr Valg", + "email": "valga.github@abyrga.ru" + } + ], + "description": "A PHP client for the FBNS built on top of ReactPHP", + "keywords": [ + "FBNS", + "client", + "php" + ], + "time": "2017-10-09T07:54:13+00:00" + }, + { + "name": "winbox/args", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/johnstevenson/winbox-args.git", + "reference": "389a9ed9410e6f422b1031b3e55a402ace716296" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/johnstevenson/winbox-args/zipball/389a9ed9410e6f422b1031b3e55a402ace716296", + "reference": "389a9ed9410e6f422b1031b3e55a402ace716296", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Winbox\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Windows command-line formatter", + "homepage": "http://github.com/johnstevenson/winbox-args", + "keywords": [ + "Escape", + "command", + "windows" + ], + "time": "2016-08-04T14:30:27+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [] +} diff --git a/foo b/foo new file mode 100755 index 0000000..e69de29 diff --git a/instafeed.pl b/instafeed.pl new file mode 100755 index 0000000..8308497 --- /dev/null +++ b/instafeed.pl @@ -0,0 +1,261 @@ +#!usr/bin/perl +use strict; +use warnings; +use Data::Dumper; +use JSON::XS qw(encode_json decode_json); +use File::Slurp qw(read_file write_file); +use Getopt::Long qw(GetOptions); +use File::Basename; +use HTTP::Cookies; +use Cwd qw(cwd); + +my %config = ( + 'profile' => undef, + 'SRCRoot' => './src/', + 'uploadPHP_CMD' => '/usr/bin/php ./vendor/mgp25/instagram-php/examples/uploadPhoto.php', + 'uploadPHP_debug' => 1, + 'uploadPHP_truncated_debug' => 1, + 'uploadPHP_autoload' => cwd . '/src/mpg25-instagram-api/vendor/autoload.php', +); + +my %profile = ( + 'dreamyourmansion' => { + 'DBFilepath' => './src/db/db_dreamyourmansion.dat', + 'imageDir' => './src/images/dreamyourmansion', + 'filename_as_title' => 0, + 'uploadPHP' => { + 'username' => 'dreamyourmansion', + 'password' => 'nBLT!4H3aI@c', + 'truncated_debug' => 1, + 'proxy_user' => 'zino%40onlinehome.de', + 'proxy_password' => 'zinomedial33t', + 'proxy_ip' => 'de786.nordvpn.com', + 'proxy_port' => 80, + 'tags' => '#investment #immobilie #mansionhouse #dream #poolhouse #villa #realestate #loft #awesome #lifestyle #motivation #luxury', + 'description_add' => "The most beautiful real estates in the world!", + }, + }, + 'vstbestprices' => { + 'DBFilepath' => './src/db/db_vstbestprices.dat', + 'imageDir' => './src/images/vstbestprices', + 'filename_as_title' => 1, + 'uploadPHP' => { + 'username' => 'vstbestprices', + 'password' => 'Vst#1337vst#1337', + 'truncated_debug' => 1, + 'proxy_user' => 'zino%40onlinehome.de', + 'proxy_password' => 'zinomedial33t', + 'proxy_ip' => 'de435.nordvpn.com', + 'proxy_port' => 80, + 'tags' => '#Beats #FLStudio20 #Producer #Ableton #Beatmaker #Studio #ProTools #Music #DAW #LogicPro #FruityLoops #VST #VSTplugins #NativeInstruments #MIDI #Drums #AutoTune #Spectrasonics #Omnisphere #AutoTune #Plugins #Keyscape #Trilian #Logic', + 'description_add' => 'INSTALLATION SUPPORT is included in all prices so you can relax and focus on producing!', + }, + }, + 'vstbestprices_testing' => { + 'DBFilepath' => './src/db/db_vstbestprices.dat', + 'imageDir' => './src/images/vstbestprices', + 'filename_as_title' => 1, + 'uploadPHP' => { + 'username' => 'adobebestprices', + 'password' => 'vst#1337', + 'truncated_debug' => 1, + 'proxy_user' => 'zino%40onlinehome.de', + 'proxy_password' => 'zinomedial33t', + 'proxy_ip' => 'de435.nordvpn.com', + 'proxy_port' => 80, + 'tags' => '#Beats #FLStudio20 #Producer #Ableton #Beatmaker #Studio #ProTools #Music #DAW #LogicPro #FruityLoops #VST #VSTplugins #NativeInstruments #MIDI #Drums #AutoTune #Spectrasonics #Omnisphere #AutoTune #Plugins #Keyscape #Trilian #Logic', + 'description_add' => 'INSTALLATION SUPPORT is included in all prices so you can relax and focus on producing!', + }, + }, + 'adobebestprices' => { + 'DBFilepath' => './src/db/db_adobebestprices.dat', + 'imageDir' => './src/images/adobebestprices/', + 'filename_as_title' => 0, + 'uploadPHP' => { + 'username' => 'adobebestprices', + 'password' => 'vst#1337', + 'truncated_debug' => 1, + 'proxy_user' => 'zino%40onlinehome.de', + 'proxy_password' => 'zinomedial33t', + 'proxy_ip' => 'de435.nordvpn.com', + 'proxy_port' => 80, + 'tags' => '#adobe #photoshop #adobeillustrator #vector #illustrator #adobephotoshop #vectorart #graphicdesign #aftereffects #logo #cs6 #lightroom #graphic', + 'description_add' => 'Photoshop, Lightroom, Illustrator, Dreamviewer, Premiere for WIN & MAC | Installation support is included in all our prices!', + }, + }, +); + +my (%data, %db); +my $dbKeysStart = 0; +my $profile; + +# MAIN +&CheckParameter(); +&UndumpFromFile(); +#print Dumper \%db; +&DirectoryListing(); +print Dumper \%data; +&FindNewDataset(); +&Summary(); + +sub CheckParameter { + &Delimiter((caller(0))[3]); + + GetOptions ('profile=s' => \$config{'profile'}) or die &PrintUsage(); + die &PrintUsage() if !$config{'profile'}; + $profile = $config{'profile'}; + if (!exists $profile{$profile}) { + print "Profile '$profile' does not exist.\n"; + &PrintUsage(); + die; + } +} + +sub PrintUsage { + print "Usage: $0 --profile *name*\n"; + print "Following profiles are available:\n"; + print "* '$_'\n" for keys(%profile); +} + +sub UndumpFromFile { + &Delimiter((caller(0))[3]); + + if (-e $profile{$profile}{'DBFilepath'}) { + my $json = read_file($profile{$profile}{'DBFilepath'}, { binmode => ':raw' }); + if (!$json) { + warn "DB file $profile{$profile}{'DBFilepath'} is empty.\n"; + return; + } + %db = %{ decode_json $json }; + $dbKeysStart = scalar(keys(%db)); + print "INFO: $profile{$profile}{'DBFilepath'} has " . $dbKeysStart . " keys.\n"; + } + elsif (!-e $profile{$profile}{'DBFilepath'}) { + print "INFO: NO DB file found at $profile{$profile}{'DBFilepath'}. Creating now... "; + write_file($profile{$profile}{'DBFilepath'}, ''); + print "done.\n"; + die "Please restart."; + # &UndumpFromFile(); + + } +} + +sub DirectoryListing { + &Delimiter((caller(0))[3]); + + # opendir(DIR, $profile{$profile}{'imageDir'}); + # my @files = grep(/\.jpg$|\.png$|\.jpeg$|/,readdir(DIR)); + # closedir(DIR); + my @files = glob ( "$profile{$profile}{'imageDir'}/*" ); + %data = map { $_ => { 'FILEPATH' => "$_" } } @files; +} + +sub Summary { + &Delimiter((caller(0))[3]); + + print "$profile{$profile}{'DBFilepath'} has " . scalar(keys(%db)) . " keys (before $dbKeysStart).\n"; +} + +sub FindNewDataset { + &Delimiter((caller(0))[3]); + + my $i = 0; + for my $key (keys %data) { + if (exists $db{$key}) { + print "OLD: $key\n"; + } + elsif (!exists $db{$key}) { + print "NEW: $key\n"; + + my $success = &uploadPHP($data{$key}{'FILEPATH'}); + &AddToDB($key); + &WipeData($key); + last; + # if ($success) { + # print "success is $success\n"; + # &AddToDB($key); + # &WipeData($key); + # last; + # } + } + $i++; + } + + if ($i == scalar(keys(%data))) { + warn "\nNO NEW FILES AVAILABLE.\n"; + } +} + +sub uploadPHP { + &Delimiter((caller(0))[3]); + my $filepath = shift; + + my $success = 1; + my $captionText = "$profile{$profile}{'uploadPHP'}{'description_add'}\n\n$profile{$profile}{'uploadPHP'}{'tags'}"; + if ($profile{$profile}{'filename_as_title'}) { + my $filename = basename($filepath); + $filename =~ s/(NO INSTALL)|(SymLink Installer)//g; + $filename =~ s/( , )|(\.[^.]+$)//g; + $captionText = "$filename\n\n" . $captionText; + # print Dumper $captionText; + } + open PHPOUT, "$config{'uploadPHP_CMD'} \'$filepath\' \'$captionText\' $profile{$profile}{'uploadPHP'}{'username'} $profile{$profile}{'uploadPHP'}{'password'} $config{'uploadPHP_debug'} $config{'uploadPHP_truncated_debug'} $profile{$profile}{'uploadPHP'}{'proxy_user'} $profile{$profile}{'uploadPHP'}{'proxy_password'} $profile{$profile}{'uploadPHP'}{'proxy_ip'} $profile{$profile}{'uploadPHP'}{'proxy_port'} \'$config{'uploadPHP_autoload'}\'|"; + while () { + print $_; # PRINT CURRENT PHP OUPUT LINE + if ($_ =~ m/error/) { + $success = 0; + } + } + + return $success; +} + +sub WipeData { + &Delimiter((caller(0))[3]); + my $key = shift; + + print "Deleting $data{$key}{'FILEPATH'}..."; + unlink($data{$key}{'FILEPATH'}) or die "Could not delete $data{$key}{'FILEPATH'}!\n"; + print " Done.\n"; +} + +sub AddToDB { + &Delimiter((caller(0))[3]); + my $key = shift; + + $data{$key}{'TIMESTAMP_UPLOADED'} = &GetTimestamp('YMDHMS'); + $db{$key} = $data{$key}; + my $json = encode_json \%db; + write_file($profile{$profile}{'DBFilepath'}, { binmode => ':raw' }, $json); +} + +sub Delimiter { + my $SubName = shift; + print "\n" . "-" x 80 . "\nSUB " . $SubName . "\n" . '-' x 80 . "\n"; +} + +sub GetTimestamp { + #&Delimiter((caller(0))[3]); + my $switch = shift; + + my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time); + + my $nice_timestamp; + if ($switch eq 'YMDHMS') { + $nice_timestamp = sprintf ( "%04d%02d%02d_%02d%02d%02d", $year+1900,$mon+1,$mday,$hour,$min,$sec); + } + elsif ($switch eq 'YMD') { + $nice_timestamp = sprintf ( "%04d%02d%02d", $year+1900,$mon+1,$mday); + } + elsif ($switch eq 'year') { + $nice_timestamp = $year+1900; + } + elsif ($switch eq 'month') { + $nice_timestamp = $mon+10; + } + else { + print "Invalid/no switch detected. Use: 'YMDHMS' / 'YMD'\n"; + } + + return $nice_timestamp; +} \ No newline at end of file diff --git a/instafeed/code_depricated/instafeed.1.pl b/instafeed/code_depricated/instafeed.1.pl new file mode 100755 index 0000000..e92e05b --- /dev/null +++ b/instafeed/code_depricated/instafeed.1.pl @@ -0,0 +1,170 @@ +#!usr/bin/perl +use strict; +use warnings; +use Data::Dumper; +use JSON::XS qw(encode_json decode_json); +use File::Slurp qw(read_file write_file); +use Getopt::Long qw(GetOptions); + +my %config = ( + 'uploadPHP' => { + 'USERNAME' => 'dreamyourmansion', + 'PASSWORD' => 'H5AZ#dQZ5Ycf', + 'DEBUG' => 1, + 'TRUNCATED_DEBUG' => 1, + 'PROXY_USER' => 'zino%40onlinehome.de', + 'PROXY_PASSWORD' => 'zinomedial33t', + 'PROXY_IP' => 'de435.nordvpn.com', + 'PROXY_PORT' => 80, + }, + 'profile' => undef, + 'imageDir' => './src/images/', + 'SRCRoot' => './src/', + 'DBFilepath' => '/home/pi/instafeed/src/db/db.dat', + 'uploadPHP_CMD' => '/usr/bin/php /home/pi/instafeed/vendor/mgp25/instagram-php/examples/uploadPhoto.php', + 'uploadPHP_DESCRIPTION_ADD' => "The most beautiful real estates in the world!\n\nBenefit from the flourishing housing market in Germany. Contact us now by DM.\n\nVom Mieter zum Eigentümer! Exklusives Portfolio: Kontaktiere uns jetzt per DM.", + 'uploadPHP_TAGS' => '#investment #immobilie #mansionhouse #dream #poolhouse #villa #realestate #loft #awesome #lifestyle #motivation #luxury', +); + +my (%data, %db, %profiles); +my $dbKeysStart = 0; +GetOptions ('profile=s' => \$config{'profile'}) or die "Usage: $0 --profile *name*\n"; +die "Usage: $0 --profile *name*\n" if !$config{'profile'} ; + +# MAIN + +&UndumpFromFile(); +#print Dumper \%db; +&DirectoryListing(); +# print Dumper \%data; +&FindNewDataset(); +&Summary(); + +sub UndumpFromFile { + &Delimiter((caller(0))[3]); + + if (-e $config{'DBFilepath'}) { + my $json = read_file($config{'DBFilepath'}, { binmode => ':raw' }); + if (!$json) { + warn "DB file $config{'DBFilepath'} is empty.\n"; + return; + } + %db = %{ decode_json $json }; + $dbKeysStart = scalar(keys(%db)); + print "INFO: $config{'DBFilepath'} has " . $dbKeysStart . " keys.\n"; + } + elsif (!-e $config{'DBFilepath'}) { + warn "INFO: NO DB file found at $config{'DBFilepath'}\n"; + exit; + } +} + +sub DirectoryListing { + &Delimiter((caller(0))[3]); + + opendir(DIR, $config{'imageDir'}); + my @files = grep(/\.jpg$/,readdir(DIR)); + closedir(DIR); + %data = map { $_ => { 'FILEPATH' => "$config{'imageDir'}$_" } } @files; + # @hash{@keys} = undef; +} + +sub Summary { + &Delimiter((caller(0))[3]); + + print "$config{'DBFilepath'} has " . scalar(keys(%db)) . " keys (before $dbKeysStart).\n"; +} + +sub FindNewDataset { + &Delimiter((caller(0))[3]); + + my $i = 0; + for my $key (keys %data) { + if (exists $db{$key}) { + print "OLD: $key\n"; + } + elsif (!exists $db{$key}) { + print "NEW: $key\n"; + + my $success = &uploadPHP($data{$key}{'FILEPATH'}); + if ($success) { + print "success is $success\n"; + &AddToDB($key); + &WipeData($key); + last; + } + } + $i++; + } + + if ($i == scalar(keys(%data))) { + warn "\nNO NEW FILES AVAILABLE.\n"; + } +} + +sub uploadPHP { + &Delimiter((caller(0))[3]); + my $filepath = shift; + my $success = 1; + my $captionText = "$config{'uploadPHP_DESCRIPTION_ADD'}\n\n$config{'uploadPHP_TAGS'}"; + + open PHPOUT, "$config{'uploadPHP_CMD'} $filepath \'$captionText\' $config{'uploadPHP'}{'USERNAME'} $config{'uploadPHP'}{'PASSWORD'} $config{'uploadPHP'}{'DEBUG'} $config{'uploadPHP'}{'TRUNCATED_DEBUG'} $config{'uploadPHP'}{'PROXY_USER'} $config{'uploadPHP'}{'PROXY_PASSWORD'} $config{'uploadPHP'}{'PROXY_IP'} $config{'uploadPHP'}{'PROXY_PORT'}|"; + while () { + print $_; # PRINT CURRENT PHP OUPUT LINE + if ($_ =~ m/error/) { + $success = 0; + } + } + + return $success; +} + +sub WipeData { + &Delimiter((caller(0))[3]); + my $key = shift; + + print "Deleting $data{$key}{'FILEPATH'}..."; + unlink($data{$key}{'FILEPATH'}) or die "Could not delete $data{$key}{'FILEPATH'}!\n"; + print " Done.\n"; +} + +sub AddToDB { + &Delimiter((caller(0))[3]); + my $key = shift; + + $data{$key}{'TIMESTAMP_UPLOADED'} = &GetTimestamp('YMDHMS'); + $db{$key} = $data{$key}; + my $json = encode_json \%db; + write_file($config{'DBFilepath'}, { binmode => ':raw' }, $json); +} + +sub Delimiter { + my $SubName = shift; + print "\n" . "-" x 80 . "\nSUB " . $SubName . "\n" . '-' x 80 . "\n"; +} + +sub GetTimestamp { + #&Delimiter((caller(0))[3]); + my $switch = shift; + + my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time); + + my $nice_timestamp; + if ($switch eq 'YMDHMS') { + $nice_timestamp = sprintf ( "%04d%02d%02d_%02d%02d%02d", $year+1900,$mon+1,$mday,$hour,$min,$sec); + } + elsif ($switch eq 'YMD') { + $nice_timestamp = sprintf ( "%04d%02d%02d", $year+1900,$mon+1,$mday); + } + elsif ($switch eq 'year') { + $nice_timestamp = $year+1900; + } + elsif ($switch eq 'month') { + $nice_timestamp = $mon+10; + } + else { + print "Invalid/no switch detected. Use: 'YMDHMS' / 'YMD'\n"; + } + + return $nice_timestamp; +} \ No newline at end of file diff --git a/instafeed/code_depricated/instafeed.pl b/instafeed/code_depricated/instafeed.pl new file mode 100755 index 0000000..ffc36f0 --- /dev/null +++ b/instafeed/code_depricated/instafeed.pl @@ -0,0 +1,253 @@ +#!usr/bin/perl +use strict; +use warnings; +use Data::Dumper; +use JSON::XS qw(encode_json decode_json); +use File::Slurp qw(read_file write_file); +use Getopt::Long qw(GetOptions); +use File::Basename; + +my %config = ( + 'profile' => undef, + 'SRCRoot' => './src/', + 'uploadPHP_CMD' => '/usr/bin/php ./vendor/mgp25/instagram-php/examples/uploadPhoto.php', + 'uploadPHP_debug' => 1, + 'uploadPHP_truncated_debug' => 1, +); + +my %profile = ( + 'dreamyourmansion' => { + 'DBFilepath' => './src/db/db_dreamyourmansion.dat', + 'imageDir' => './src/images/dreamyourmansion', + 'filename_as_title' => 0, + 'uploadPHP' => { + 'username' => 'dreamyourmansion', + 'password' => 'nBLT!4H3aI@c', + 'truncated_debug' => 1, + 'proxy_user' => 'zino%40onlinehome.de', + 'proxy_password' => 'zinomedial33t', + 'proxy_ip' => 'de435.nordvpn.com', + 'proxy_port' => 80, + 'tags' => '#investment #immobilie #mansionhouse #dream #poolhouse #villa #realestate #loft #awesome #lifestyle #motivation #luxury', + 'description_add' => "The most beautiful real estates in the world!\n\nBenefit from the flourishing housing market in Germany. Contact us now by DM.\n\nVom Mieter zum Eigentümer! Exklusives Portfolio: Kontaktiere uns jetzt per DM.", + }, + }, + 'vstbestprices' => { + 'DBFilepath' => './src/db/db_vstbestprices.dat', + 'imageDir' => './src/images/vstbestprices', + 'filename_as_title' => 1, + 'uploadPHP' => { + 'username' => 'vstbestprices', + 'password' => 'Vst#1337vst#1337', + 'truncated_debug' => 1, + 'proxy_user' => 'zino%40onlinehome.de', + 'proxy_password' => 'zinomedial33t', + 'proxy_ip' => 'de435.nordvpn.com', + 'proxy_port' => 80, + 'tags' => '#Beats #FLStudio20 #Producer #Ableton #Beatmaker #Studio #ProTools #Music #DAW #LogicPro #FruityLoops #VST #VSTplugins #NativeInstruments #MIDI #Drums #AutoTune #Spectrasonics #Omnisphere #AutoTune #Plugins #Keyscape #Trilian #Logic', + 'description_add' => 'INSTALLATION SUPPORT is included in all prices so you can relax and focus on producing!', + }, + }, + 'vstbestprices_testing' => { + 'DBFilepath' => './src/db/db_vstbestprices.dat', + 'imageDir' => './src/images/vstbestprices', + 'filename_as_title' => 1, + 'uploadPHP' => { + 'username' => 'adobebestprices', + 'password' => 'vst#1337', + 'truncated_debug' => 1, + 'proxy_user' => 'zino%40onlinehome.de', + 'proxy_password' => 'zinomedial33t', + 'proxy_ip' => 'de435.nordvpn.com', + 'proxy_port' => 80, + 'tags' => '#Beats #FLStudio20 #Producer #Ableton #Beatmaker #Studio #ProTools #Music #DAW #LogicPro #FruityLoops #VST #VSTplugins #NativeInstruments #MIDI #Drums #AutoTune #Spectrasonics #Omnisphere #AutoTune #Plugins #Keyscape #Trilian #Logic', + 'description_add' => 'INSTALLATION SUPPORT is included in all prices so you can relax and focus on producing!', + }, + }, + 'adobebestprices' => { + 'DBFilepath' => './src/db/db_adobebestprices.dat', + 'imageDir' => './src/images/adobebestprices/', + 'filename_as_title' => 0, + 'uploadPHP' => { + 'username' => 'adobebestprices', + 'password' => 'vst#1337', + 'truncated_debug' => 1, + 'proxy_user' => 'zino%40onlinehome.de', + 'proxy_password' => 'zinomedial33t', + 'proxy_ip' => 'de435.nordvpn.com', + 'proxy_port' => 80, + 'tags' => '#adobe #photoshop #adobeillustrator #vector #illustrator #adobephotoshop #vectorart #graphicdesign #aftereffects #logo #cs6 #lightroom #graphic', + 'description_add' => 'Photoshop, Lightroom, Illustrator, Dreamviewer, Premiere for WIN & MAC | Installation support is included in all our prices!', + }, + }, +); + +my (%data, %db); +my $dbKeysStart = 0; +my $profile; + +# MAIN +&CheckParameter(); +&UndumpFromFile(); +#print Dumper \%db; +&DirectoryListing(); +print Dumper \%data; +&FindNewDataset(); +&Summary(); + +sub CheckParameter { + &Delimiter((caller(0))[3]); + + GetOptions ('profile=s' => \$config{'profile'}) or die "Usage: $0 --profile *name*\n"; + die "Usage: $0 --profile *name*\n" if !$config{'profile'} ; + $profile = $config{'profile'}; + if (!exists $profile{$profile}) { + print "Template for profile '$profile' does not exist. Following templates are available:\n"; + print "'$_' " for keys(%profile); + print "\n"; + die; + } +} + +sub UndumpFromFile { + &Delimiter((caller(0))[3]); + + if (-e $profile{$profile}{'DBFilepath'}) { + my $json = read_file($profile{$profile}{'DBFilepath'}, { binmode => ':raw' }); + if (!$json) { + warn "DB file $profile{$profile}{'DBFilepath'} is empty.\n"; + return; + } + %db = %{ decode_json $json }; + $dbKeysStart = scalar(keys(%db)); + print "INFO: $profile{$profile}{'DBFilepath'} has " . $dbKeysStart . " keys.\n"; + } + elsif (!-e $profile{$profile}{'DBFilepath'}) { + print "INFO: NO DB file found at $profile{$profile}{'DBFilepath'}. Creating now... "; + write_file($profile{$profile}{'DBFilepath'}, ''); + print "done.\n"; + die "Please restart."; + # &UndumpFromFile(); + + } +} + +sub DirectoryListing { + &Delimiter((caller(0))[3]); + + # opendir(DIR, $profile{$profile}{'imageDir'}); + # my @files = grep(/\.jpg$|\.png$|\.jpeg$|/,readdir(DIR)); + # closedir(DIR); + my @files = glob ( "$profile{$profile}{'imageDir'}/*" ); + %data = map { $_ => { 'FILEPATH' => "$_" } } @files; +} + +sub Summary { + &Delimiter((caller(0))[3]); + + print "$profile{$profile}{'DBFilepath'} has " . scalar(keys(%db)) . " keys (before $dbKeysStart).\n"; +} + +sub FindNewDataset { + &Delimiter((caller(0))[3]); + + my $i = 0; + for my $key (keys %data) { + if (exists $db{$key}) { + print "OLD: $key\n"; + } + elsif (!exists $db{$key}) { + print "NEW: $key\n"; + + my $success = &uploadPHP($data{$key}{'FILEPATH'}); + &AddToDB($key); + &WipeData($key); + last; + # if ($success) { + # print "success is $success\n"; + # &AddToDB($key); + # &WipeData($key); + # last; + # } + } + $i++; + } + + if ($i == scalar(keys(%data))) { + warn "\nNO NEW FILES AVAILABLE.\n"; + } +} + +sub uploadPHP { + &Delimiter((caller(0))[3]); + my $filepath = shift; + + my $success = 1; + my $captionText = "$profile{$profile}{'uploadPHP'}{'description_add'}\n\n$profile{$profile}{'uploadPHP'}{'tags'}"; + if ($profile{$profile}{'filename_as_title'}) { + my $filename = basename($filepath); + $filename =~ s/(NO INSTALL)|(SymLink Installer)//g; + $filename =~ s/( , )|(\.[^.]+$)//g; + $captionText = "$filename\n\n" . $captionText; + # print Dumper $captionText; + } + open PHPOUT, "$config{'uploadPHP_CMD'} \'$filepath\' \'$captionText\' $profile{$profile}{'uploadPHP'}{'username'} $profile{$profile}{'uploadPHP'}{'password'} $config{'uploadPHP_debug'} $config{'uploadPHP_truncated_debug'} $profile{$profile}{'uploadPHP'}{'proxy_user'} $profile{$profile}{'uploadPHP'}{'proxy_password'} $profile{$profile}{'uploadPHP'}{'proxy_ip'} $profile{$profile}{'uploadPHP'}{'proxy_port'}|"; + while () { + print $_; # PRINT CURRENT PHP OUPUT LINE + if ($_ =~ m/error/) { + $success = 0; + } + } + + return $success; +} + +sub WipeData { + &Delimiter((caller(0))[3]); + my $key = shift; + + print "Deleting $data{$key}{'FILEPATH'}..."; + unlink($data{$key}{'FILEPATH'}) or die "Could not delete $data{$key}{'FILEPATH'}!\n"; + print " Done.\n"; +} + +sub AddToDB { + &Delimiter((caller(0))[3]); + my $key = shift; + + $data{$key}{'TIMESTAMP_UPLOADED'} = &GetTimestamp('YMDHMS'); + $db{$key} = $data{$key}; + my $json = encode_json \%db; + write_file($profile{$profile}{'DBFilepath'}, { binmode => ':raw' }, $json); +} + +sub Delimiter { + my $SubName = shift; + print "\n" . "-" x 80 . "\nSUB " . $SubName . "\n" . '-' x 80 . "\n"; +} + +sub GetTimestamp { + #&Delimiter((caller(0))[3]); + my $switch = shift; + + my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time); + + my $nice_timestamp; + if ($switch eq 'YMDHMS') { + $nice_timestamp = sprintf ( "%04d%02d%02d_%02d%02d%02d", $year+1900,$mon+1,$mday,$hour,$min,$sec); + } + elsif ($switch eq 'YMD') { + $nice_timestamp = sprintf ( "%04d%02d%02d", $year+1900,$mon+1,$mday); + } + elsif ($switch eq 'year') { + $nice_timestamp = $year+1900; + } + elsif ($switch eq 'month') { + $nice_timestamp = $mon+10; + } + else { + print "Invalid/no switch detected. Use: 'YMDHMS' / 'YMD'\n"; + } + + return $nice_timestamp; +} \ No newline at end of file diff --git a/instafeed/composer.json b/instafeed/composer.json new file mode 100755 index 0000000..ef0128b --- /dev/null +++ b/instafeed/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "mgp25/instagram-php": "^7.0" + } +} diff --git a/instafeed/composer.lock b/instafeed/composer.lock new file mode 100755 index 0000000..d418520 --- /dev/null +++ b/instafeed/composer.lock @@ -0,0 +1,1294 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "002cf77f209f09b01600428b67fd422d", + "packages": [ + { + "name": "binsoul/net-mqtt", + "version": "0.2.1", + "source": { + "type": "git", + "url": "https://github.com/binsoul/net-mqtt.git", + "reference": "286b28e6014739b19e0e7ce0cd5871cdd0cef9b3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/binsoul/net-mqtt/zipball/286b28e6014739b19e0e7ce0cd5871cdd0cef9b3", + "reference": "286b28e6014739b19e0e7ce0cd5871cdd0cef9b3", + "shasum": "" + }, + "require": { + "php": "~5.6|~7.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~1.0", + "phpunit/phpunit": "~4.0||~5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "BinSoul\\Net\\Mqtt\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sebastian Mößler", + "email": "code@binsoul.de", + "homepage": "https://github.com/binsoul", + "role": "Developer" + } + ], + "description": "MQTT protocol implementation", + "homepage": "https://github.com/binsoul/net-mqtt", + "keywords": [ + "mqtt", + "net" + ], + "time": "2017-04-03T20:17:02+00:00" + }, + { + "name": "binsoul/net-mqtt-client-react", + "version": "0.3.2", + "source": { + "type": "git", + "url": "https://github.com/binsoul/net-mqtt-client-react.git", + "reference": "6a80fea50e927ebb8bb8a631ea7903c22742ded5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/binsoul/net-mqtt-client-react/zipball/6a80fea50e927ebb8bb8a631ea7903c22742ded5", + "reference": "6a80fea50e927ebb8bb8a631ea7903c22742ded5", + "shasum": "" + }, + "require": { + "binsoul/net-mqtt": "~0.2", + "php": "~5.6|~7.0", + "react/promise": "~2.0", + "react/socket": "~0.8" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~1.0", + "phpunit/phpunit": "~4.0||~5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "BinSoul\\Net\\Mqtt\\Client\\React\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sebastian Mößler", + "email": "code@binsoul.de", + "homepage": "https://github.com/binsoul", + "role": "Developer" + } + ], + "description": "Asynchronous MQTT client built on React", + "homepage": "https://github.com/binsoul/net-mqtt-client-react", + "keywords": [ + "client", + "mqtt", + "net" + ], + "time": "2017-08-20T08:06:53+00:00" + }, + { + "name": "clue/http-proxy-react", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/clue/php-http-proxy-react.git", + "reference": "eeff725640ed53386a6adb05ffdbfc2837404fdf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/clue/php-http-proxy-react/zipball/eeff725640ed53386a6adb05ffdbfc2837404fdf", + "reference": "eeff725640ed53386a6adb05ffdbfc2837404fdf", + "shasum": "" + }, + "require": { + "php": ">=5.3", + "react/promise": " ^2.1 || ^1.2.1", + "react/socket": "^1.0 || ^0.8.4", + "ringcentral/psr7": "^1.2" + }, + "require-dev": { + "clue/block-react": "^1.1", + "phpunit/phpunit": "^5.0 || ^4.8", + "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Clue\\React\\HttpProxy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@lueck.tv" + } + ], + "description": "Async HTTP proxy connector, use any TCP/IP-based protocol through an HTTP CONNECT proxy server, built on top of ReactPHP", + "homepage": "https://github.com/clue/php-http-proxy-react", + "keywords": [ + "async", + "connect", + "http", + "proxy", + "reactphp" + ], + "time": "2018-02-13T16:31:32+00:00" + }, + { + "name": "clue/socks-react", + "version": "v0.8.7", + "source": { + "type": "git", + "url": "https://github.com/clue/php-socks-react.git", + "reference": "0fcd6f2f506918ff003f1b995c6e78443f26e8ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/clue/php-socks-react/zipball/0fcd6f2f506918ff003f1b995c6e78443f26e8ea", + "reference": "0fcd6f2f506918ff003f1b995c6e78443f26e8ea", + "shasum": "" + }, + "require": { + "evenement/evenement": "~3.0|~1.0|~2.0", + "php": ">=5.3", + "react/promise": "^2.1 || ^1.2", + "react/socket": "^1.0 || ^0.8.6" + }, + "require-dev": { + "clue/block-react": "^1.1", + "clue/connection-manager-extra": "^1.0 || ^0.7", + "phpunit/phpunit": "^6.0 || ^5.7 || ^4.8.35", + "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Clue\\React\\Socks\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@lueck.tv" + } + ], + "description": "Async SOCKS4, SOCKS4a and SOCKS5 proxy client and server implementation, built on top of ReactPHP", + "homepage": "https://github.com/clue/php-socks-react", + "keywords": [ + "async", + "proxy", + "reactphp", + "socks client", + "socks protocol", + "socks server", + "tcp tunnel" + ], + "time": "2017-12-17T14:47:58+00:00" + }, + { + "name": "corneltek/getoptionkit", + "version": "2.6.0", + "source": { + "type": "git", + "url": "https://github.com/c9s/GetOptionKit.git", + "reference": "995607ddf4fc90ebdb4a7d58fe972d581ad8495f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/c9s/GetOptionKit/zipball/995607ddf4fc90ebdb4a7d58fe972d581ad8495f", + "reference": "995607ddf4fc90ebdb4a7d58fe972d581ad8495f", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6.x-dev" + } + }, + "autoload": { + "psr-4": { + "GetOptionKit\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Yo-An Lin", + "email": "yoanlin93@gmail.com" + } + ], + "description": "Powerful command-line option toolkit", + "homepage": "http://github.com/c9s/GetOptionKit", + "time": "2017-06-30T14:54:48+00:00" + }, + { + "name": "evenement/evenement", + "version": "v3.0.1", + "source": { + "type": "git", + "url": "https://github.com/igorw/evenement.git", + "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/igorw/evenement/zipball/531bfb9d15f8aa57454f5f0285b18bec903b8fb7", + "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7", + "shasum": "" + }, + "require": { + "php": ">=7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "Evenement": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + } + ], + "description": "Événement is a very simple event dispatching library for PHP", + "keywords": [ + "event-dispatcher", + "event-emitter" + ], + "time": "2017-07-23T21:35:13+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "6.4.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "0895c932405407fd3a7368b6910c09a24d26db11" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/0895c932405407fd3a7368b6910c09a24d26db11", + "reference": "0895c932405407fd3a7368b6910c09a24d26db11", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^1.0", + "guzzlehttp/psr7": "^1.6.1", + "php": ">=5.5" + }, + "require-dev": { + "ext-curl": "*", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", + "psr/log": "^1.1" + }, + "suggest": { + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "6.3-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "rest", + "web service" + ], + "time": "2019-10-23T15:58:00+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "v1.3.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646", + "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646", + "shasum": "" + }, + "require": { + "php": ">=5.5.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "time": "2016-12-20T10:07:11+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "1.6.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "239400de7a173fe9901b9ac7c06497751f00727a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/239400de7a173fe9901b9ac7c06497751f00727a", + "reference": "239400de7a173fe9901b9ac7c06497751f00727a", + "shasum": "" + }, + "require": { + "php": ">=5.4.0", + "psr/http-message": "~1.0", + "ralouphie/getallheaders": "^2.0.5 || ^3.0.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "ext-zlib": "*", + "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8" + }, + "suggest": { + "zendframework/zend-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Schultze", + "homepage": "https://github.com/Tobion" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "time": "2019-07-01T23:21:34+00:00" + }, + { + "name": "lazyjsonmapper/lazyjsonmapper", + "version": "v1.6.3", + "source": { + "type": "git", + "url": "https://github.com/lazyjsonmapper/lazyjsonmapper.git", + "reference": "51e093b50f4de15d2d64548b3ca743713eed6ee9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/lazyjsonmapper/lazyjsonmapper/zipball/51e093b50f4de15d2d64548b3ca743713eed6ee9", + "reference": "51e093b50f4de15d2d64548b3ca743713eed6ee9", + "shasum": "" + }, + "require": { + "corneltek/getoptionkit": "2.*", + "php": ">=5.6" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.7.1", + "phpunit/phpunit": "6.*" + }, + "bin": [ + "bin/lazydoctor" + ], + "type": "library", + "autoload": { + "psr-4": { + "LazyJsonMapper\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "SteveJobzniak", + "role": "Developer", + "homepage": "https://github.com/SteveJobzniak" + } + ], + "description": "Advanced, intelligent & automatic object-oriented JSON containers for PHP.", + "homepage": "https://github.com/SteveJobzniak/LazyJsonMapper", + "keywords": [ + "development", + "json" + ], + "time": "2018-05-02T16:57:09+00:00" + }, + { + "name": "mgp25/instagram-php", + "version": "v7.0.1", + "source": { + "type": "git", + "url": "https://github.com/mgp25/Instagram-API.git", + "reference": "53421f90b9ef7743f1c6221c4963f2b9f7a592e8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mgp25/Instagram-API/zipball/53421f90b9ef7743f1c6221c4963f2b9f7a592e8", + "reference": "53421f90b9ef7743f1c6221c4963f2b9f7a592e8", + "shasum": "" + }, + "require": { + "binsoul/net-mqtt-client-react": "^0.3.2", + "clue/http-proxy-react": "^1.1.0", + "clue/socks-react": "^0.8.2", + "ext-bcmath": "*", + "ext-curl": "*", + "ext-exif": "*", + "ext-gd": "*", + "ext-mbstring": "*", + "ext-zlib": "*", + "guzzlehttp/guzzle": "^6.2", + "lazyjsonmapper/lazyjsonmapper": "^1.6.1", + "php": ">=5.6", + "psr/log": "^1.0", + "react/event-loop": "^0.4.3", + "react/promise": "^2.5", + "react/socket": "^0.8", + "symfony/process": "^3.4|^4.0", + "valga/fbns-react": "^0.1.8", + "winbox/args": "1.0.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.11.0", + "monolog/monolog": "^1.23", + "phpunit/phpunit": "^5.7 || ^6.2", + "react/http": "^0.7.2" + }, + "suggest": { + "ext-event": "Installing PHP's native Event extension enables faster Realtime class event handling." + }, + "type": "library", + "autoload": { + "psr-4": { + "InstagramAPI\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "RPL-1.5", + "proprietary" + ], + "authors": [ + { + "name": "mgp25", + "email": "me@mgp25.com", + "role": "Founder" + }, + { + "name": "SteveJobzniak", + "homepage": "https://github.com/SteveJobzniak", + "role": "Developer" + } + ], + "description": "Instagram's private API for PHP", + "keywords": [ + "api", + "instagram", + "php", + "private" + ], + "time": "2019-09-17T00:56:42+00:00" + }, + { + "name": "psr/http-message", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "time": "2016-08-06T14:39:51+00:00" + }, + { + "name": "psr/log", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "bf73deb2b3b896a9d9c75f3f0d88185d2faa27e2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/bf73deb2b3b896a9d9c75f3f0d88185d2faa27e2", + "reference": "bf73deb2b3b896a9d9c75f3f0d88185d2faa27e2", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "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" + ], + "time": "2019-10-25T08:06:51+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "react/cache", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/cache.git", + "reference": "aa10d63a1b40a36a486bdf527f28bac607ee6466" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/cache/zipball/aa10d63a1b40a36a486bdf527f28bac607ee6466", + "reference": "aa10d63a1b40a36a486bdf527f28bac607ee6466", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "react/promise": "~2.0|~1.1" + }, + "require-dev": { + "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Async, Promise-based cache interface for ReactPHP", + "keywords": [ + "cache", + "caching", + "promise", + "reactphp" + ], + "time": "2019-07-11T13:45:28+00:00" + }, + { + "name": "react/dns", + "version": "v0.4.19", + "source": { + "type": "git", + "url": "https://github.com/reactphp/dns.git", + "reference": "6852fb98e22d2e5bb35fe5aeeaa96551b120e7c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/dns/zipball/6852fb98e22d2e5bb35fe5aeeaa96551b120e7c9", + "reference": "6852fb98e22d2e5bb35fe5aeeaa96551b120e7c9", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "react/cache": "^1.0 || ^0.6 || ^0.5", + "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5", + "react/promise": "^2.1 || ^1.2.1", + "react/promise-timer": "^1.2", + "react/stream": "^1.0 || ^0.7 || ^0.6 || ^0.5 || ^0.4.5" + }, + "require-dev": { + "clue/block-react": "^1.2", + "phpunit/phpunit": "^7.0 || ^6.4 || ^5.7 || ^4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Dns\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Async DNS resolver for ReactPHP", + "keywords": [ + "async", + "dns", + "dns-resolver", + "reactphp" + ], + "time": "2019-07-10T21:00:53+00:00" + }, + { + "name": "react/event-loop", + "version": "v0.4.3", + "source": { + "type": "git", + "url": "https://github.com/reactphp/event-loop.git", + "reference": "8bde03488ee897dc6bb3d91e4e17c353f9c5252f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/event-loop/zipball/8bde03488ee897dc6bb3d91e4e17c353f9c5252f", + "reference": "8bde03488ee897dc6bb3d91e4e17c353f9c5252f", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.8" + }, + "suggest": { + "ext-event": "~1.0", + "ext-libev": "*", + "ext-libevent": ">=0.1.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\EventLoop\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Event loop abstraction layer that libraries can use for evented I/O.", + "keywords": [ + "asynchronous", + "event-loop" + ], + "time": "2017-04-27T10:56:23+00:00" + }, + { + "name": "react/promise", + "version": "v2.7.1", + "source": { + "type": "git", + "url": "https://github.com/reactphp/promise.git", + "reference": "31ffa96f8d2ed0341a57848cbb84d88b89dd664d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/promise/zipball/31ffa96f8d2ed0341a57848cbb84d88b89dd664d", + "reference": "31ffa96f8d2ed0341a57848cbb84d88b89dd664d", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Promise\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com" + } + ], + "description": "A lightweight implementation of CommonJS Promises/A for PHP", + "keywords": [ + "promise", + "promises" + ], + "time": "2019-01-07T21:25:54+00:00" + }, + { + "name": "react/promise-timer", + "version": "v1.5.1", + "source": { + "type": "git", + "url": "https://github.com/reactphp/promise-timer.git", + "reference": "35fb910604fd86b00023fc5cda477c8074ad0abc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/promise-timer/zipball/35fb910604fd86b00023fc5cda477c8074ad0abc", + "reference": "35fb910604fd86b00023fc5cda477c8074ad0abc", + "shasum": "" + }, + "require": { + "php": ">=5.3", + "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5", + "react/promise": "^2.7.0 || ^1.2.1" + }, + "require-dev": { + "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Promise\\Timer\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@lueck.tv" + } + ], + "description": "A trivial implementation of timeouts for Promises, built on top of ReactPHP.", + "homepage": "https://github.com/reactphp/promise-timer", + "keywords": [ + "async", + "event-loop", + "promise", + "reactphp", + "timeout", + "timer" + ], + "time": "2019-03-27T18:10:32+00:00" + }, + { + "name": "react/socket", + "version": "v0.8.12", + "source": { + "type": "git", + "url": "https://github.com/reactphp/socket.git", + "reference": "7f7e6c56ccda7418a1a264892a625f38a5bdee0c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/socket/zipball/7f7e6c56ccda7418a1a264892a625f38a5bdee0c", + "reference": "7f7e6c56ccda7418a1a264892a625f38a5bdee0c", + "shasum": "" + }, + "require": { + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.0", + "react/dns": "^0.4.13", + "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5", + "react/promise": "^2.6.0 || ^1.2.1", + "react/promise-timer": "^1.4.0", + "react/stream": "^1.0 || ^0.7.1" + }, + "require-dev": { + "clue/block-react": "^1.2", + "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Socket\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP", + "keywords": [ + "Connection", + "Socket", + "async", + "reactphp", + "stream" + ], + "time": "2018-06-11T14:33:43+00:00" + }, + { + "name": "react/stream", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/stream.git", + "reference": "50426855f7a77ddf43b9266c22320df5bf6c6ce6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/stream/zipball/50426855f7a77ddf43b9266c22320df5bf6c6ce6", + "reference": "50426855f7a77ddf43b9266c22320df5bf6c6ce6", + "shasum": "" + }, + "require": { + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.8", + "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5" + }, + "require-dev": { + "clue/stream-filter": "~1.2", + "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Stream\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP", + "keywords": [ + "event-driven", + "io", + "non-blocking", + "pipe", + "reactphp", + "readable", + "stream", + "writable" + ], + "time": "2019-01-01T16:15:09+00:00" + }, + { + "name": "ringcentral/psr7", + "version": "1.2.2", + "source": { + "type": "git", + "url": "https://github.com/ringcentral/psr7.git", + "reference": "dcd84bbb49b96c616d1dcc8bfb9bef3f2cd53d1c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ringcentral/psr7/zipball/dcd84bbb49b96c616d1dcc8bfb9bef3f2cd53d1c", + "reference": "dcd84bbb49b96c616d1dcc8bfb9bef3f2cd53d1c", + "shasum": "" + }, + "require": { + "php": ">=5.3", + "psr/http-message": "~1.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "RingCentral\\Psr7\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "PSR-7 message implementation", + "keywords": [ + "http", + "message", + "stream", + "uri" + ], + "time": "2018-01-15T21:00:49+00:00" + }, + { + "name": "symfony/process", + "version": "v4.3.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "50556892f3cc47d4200bfd1075314139c4c9ff4b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/50556892f3cc47d4200bfd1075314139c4c9ff4b", + "reference": "50556892f3cc47d4200bfd1075314139c4c9ff4b", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "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 Process Component", + "homepage": "https://symfony.com", + "time": "2019-09-26T21:17:10+00:00" + }, + { + "name": "valga/fbns-react", + "version": "0.1.8", + "source": { + "type": "git", + "url": "https://github.com/valga/fbns-react.git", + "reference": "4bbf513a8ffed7e0c9ca10776033d34515bb8b37" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/valga/fbns-react/zipball/4bbf513a8ffed7e0c9ca10776033d34515bb8b37", + "reference": "4bbf513a8ffed7e0c9ca10776033d34515bb8b37", + "shasum": "" + }, + "require": { + "binsoul/net-mqtt": "~0.2", + "evenement/evenement": "~2.0|~3.0", + "ext-mbstring": "*", + "ext-zlib": "*", + "php": "~5.6|~7.0", + "psr/log": "~1.0", + "react/event-loop": "^0.4.3", + "react/promise": "~2.0", + "react/socket": "~0.8" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~2.4", + "monolog/monolog": "~1.23" + }, + "suggest": { + "ext-event": "For more efficient event loop implementation.", + "ext-gmp": "To be able to run this code on x86 PHP builds." + }, + "type": "library", + "autoload": { + "psr-4": { + "Fbns\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Abyr Valg", + "email": "valga.github@abyrga.ru" + } + ], + "description": "A PHP client for the FBNS built on top of ReactPHP", + "keywords": [ + "FBNS", + "client", + "php" + ], + "time": "2017-10-09T07:54:13+00:00" + }, + { + "name": "winbox/args", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/johnstevenson/winbox-args.git", + "reference": "389a9ed9410e6f422b1031b3e55a402ace716296" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/johnstevenson/winbox-args/zipball/389a9ed9410e6f422b1031b3e55a402ace716296", + "reference": "389a9ed9410e6f422b1031b3e55a402ace716296", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Winbox\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Windows command-line formatter", + "homepage": "http://github.com/johnstevenson/winbox-args", + "keywords": [ + "Escape", + "command", + "windows" + ], + "time": "2016-08-04T14:30:27+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [] +} diff --git a/instafeed/foo b/instafeed/foo new file mode 100755 index 0000000..e69de29 diff --git a/instafeed/instafeed.pl b/instafeed/instafeed.pl new file mode 100755 index 0000000..8308497 --- /dev/null +++ b/instafeed/instafeed.pl @@ -0,0 +1,261 @@ +#!usr/bin/perl +use strict; +use warnings; +use Data::Dumper; +use JSON::XS qw(encode_json decode_json); +use File::Slurp qw(read_file write_file); +use Getopt::Long qw(GetOptions); +use File::Basename; +use HTTP::Cookies; +use Cwd qw(cwd); + +my %config = ( + 'profile' => undef, + 'SRCRoot' => './src/', + 'uploadPHP_CMD' => '/usr/bin/php ./vendor/mgp25/instagram-php/examples/uploadPhoto.php', + 'uploadPHP_debug' => 1, + 'uploadPHP_truncated_debug' => 1, + 'uploadPHP_autoload' => cwd . '/src/mpg25-instagram-api/vendor/autoload.php', +); + +my %profile = ( + 'dreamyourmansion' => { + 'DBFilepath' => './src/db/db_dreamyourmansion.dat', + 'imageDir' => './src/images/dreamyourmansion', + 'filename_as_title' => 0, + 'uploadPHP' => { + 'username' => 'dreamyourmansion', + 'password' => 'nBLT!4H3aI@c', + 'truncated_debug' => 1, + 'proxy_user' => 'zino%40onlinehome.de', + 'proxy_password' => 'zinomedial33t', + 'proxy_ip' => 'de786.nordvpn.com', + 'proxy_port' => 80, + 'tags' => '#investment #immobilie #mansionhouse #dream #poolhouse #villa #realestate #loft #awesome #lifestyle #motivation #luxury', + 'description_add' => "The most beautiful real estates in the world!", + }, + }, + 'vstbestprices' => { + 'DBFilepath' => './src/db/db_vstbestprices.dat', + 'imageDir' => './src/images/vstbestprices', + 'filename_as_title' => 1, + 'uploadPHP' => { + 'username' => 'vstbestprices', + 'password' => 'Vst#1337vst#1337', + 'truncated_debug' => 1, + 'proxy_user' => 'zino%40onlinehome.de', + 'proxy_password' => 'zinomedial33t', + 'proxy_ip' => 'de435.nordvpn.com', + 'proxy_port' => 80, + 'tags' => '#Beats #FLStudio20 #Producer #Ableton #Beatmaker #Studio #ProTools #Music #DAW #LogicPro #FruityLoops #VST #VSTplugins #NativeInstruments #MIDI #Drums #AutoTune #Spectrasonics #Omnisphere #AutoTune #Plugins #Keyscape #Trilian #Logic', + 'description_add' => 'INSTALLATION SUPPORT is included in all prices so you can relax and focus on producing!', + }, + }, + 'vstbestprices_testing' => { + 'DBFilepath' => './src/db/db_vstbestprices.dat', + 'imageDir' => './src/images/vstbestprices', + 'filename_as_title' => 1, + 'uploadPHP' => { + 'username' => 'adobebestprices', + 'password' => 'vst#1337', + 'truncated_debug' => 1, + 'proxy_user' => 'zino%40onlinehome.de', + 'proxy_password' => 'zinomedial33t', + 'proxy_ip' => 'de435.nordvpn.com', + 'proxy_port' => 80, + 'tags' => '#Beats #FLStudio20 #Producer #Ableton #Beatmaker #Studio #ProTools #Music #DAW #LogicPro #FruityLoops #VST #VSTplugins #NativeInstruments #MIDI #Drums #AutoTune #Spectrasonics #Omnisphere #AutoTune #Plugins #Keyscape #Trilian #Logic', + 'description_add' => 'INSTALLATION SUPPORT is included in all prices so you can relax and focus on producing!', + }, + }, + 'adobebestprices' => { + 'DBFilepath' => './src/db/db_adobebestprices.dat', + 'imageDir' => './src/images/adobebestprices/', + 'filename_as_title' => 0, + 'uploadPHP' => { + 'username' => 'adobebestprices', + 'password' => 'vst#1337', + 'truncated_debug' => 1, + 'proxy_user' => 'zino%40onlinehome.de', + 'proxy_password' => 'zinomedial33t', + 'proxy_ip' => 'de435.nordvpn.com', + 'proxy_port' => 80, + 'tags' => '#adobe #photoshop #adobeillustrator #vector #illustrator #adobephotoshop #vectorart #graphicdesign #aftereffects #logo #cs6 #lightroom #graphic', + 'description_add' => 'Photoshop, Lightroom, Illustrator, Dreamviewer, Premiere for WIN & MAC | Installation support is included in all our prices!', + }, + }, +); + +my (%data, %db); +my $dbKeysStart = 0; +my $profile; + +# MAIN +&CheckParameter(); +&UndumpFromFile(); +#print Dumper \%db; +&DirectoryListing(); +print Dumper \%data; +&FindNewDataset(); +&Summary(); + +sub CheckParameter { + &Delimiter((caller(0))[3]); + + GetOptions ('profile=s' => \$config{'profile'}) or die &PrintUsage(); + die &PrintUsage() if !$config{'profile'}; + $profile = $config{'profile'}; + if (!exists $profile{$profile}) { + print "Profile '$profile' does not exist.\n"; + &PrintUsage(); + die; + } +} + +sub PrintUsage { + print "Usage: $0 --profile *name*\n"; + print "Following profiles are available:\n"; + print "* '$_'\n" for keys(%profile); +} + +sub UndumpFromFile { + &Delimiter((caller(0))[3]); + + if (-e $profile{$profile}{'DBFilepath'}) { + my $json = read_file($profile{$profile}{'DBFilepath'}, { binmode => ':raw' }); + if (!$json) { + warn "DB file $profile{$profile}{'DBFilepath'} is empty.\n"; + return; + } + %db = %{ decode_json $json }; + $dbKeysStart = scalar(keys(%db)); + print "INFO: $profile{$profile}{'DBFilepath'} has " . $dbKeysStart . " keys.\n"; + } + elsif (!-e $profile{$profile}{'DBFilepath'}) { + print "INFO: NO DB file found at $profile{$profile}{'DBFilepath'}. Creating now... "; + write_file($profile{$profile}{'DBFilepath'}, ''); + print "done.\n"; + die "Please restart."; + # &UndumpFromFile(); + + } +} + +sub DirectoryListing { + &Delimiter((caller(0))[3]); + + # opendir(DIR, $profile{$profile}{'imageDir'}); + # my @files = grep(/\.jpg$|\.png$|\.jpeg$|/,readdir(DIR)); + # closedir(DIR); + my @files = glob ( "$profile{$profile}{'imageDir'}/*" ); + %data = map { $_ => { 'FILEPATH' => "$_" } } @files; +} + +sub Summary { + &Delimiter((caller(0))[3]); + + print "$profile{$profile}{'DBFilepath'} has " . scalar(keys(%db)) . " keys (before $dbKeysStart).\n"; +} + +sub FindNewDataset { + &Delimiter((caller(0))[3]); + + my $i = 0; + for my $key (keys %data) { + if (exists $db{$key}) { + print "OLD: $key\n"; + } + elsif (!exists $db{$key}) { + print "NEW: $key\n"; + + my $success = &uploadPHP($data{$key}{'FILEPATH'}); + &AddToDB($key); + &WipeData($key); + last; + # if ($success) { + # print "success is $success\n"; + # &AddToDB($key); + # &WipeData($key); + # last; + # } + } + $i++; + } + + if ($i == scalar(keys(%data))) { + warn "\nNO NEW FILES AVAILABLE.\n"; + } +} + +sub uploadPHP { + &Delimiter((caller(0))[3]); + my $filepath = shift; + + my $success = 1; + my $captionText = "$profile{$profile}{'uploadPHP'}{'description_add'}\n\n$profile{$profile}{'uploadPHP'}{'tags'}"; + if ($profile{$profile}{'filename_as_title'}) { + my $filename = basename($filepath); + $filename =~ s/(NO INSTALL)|(SymLink Installer)//g; + $filename =~ s/( , )|(\.[^.]+$)//g; + $captionText = "$filename\n\n" . $captionText; + # print Dumper $captionText; + } + open PHPOUT, "$config{'uploadPHP_CMD'} \'$filepath\' \'$captionText\' $profile{$profile}{'uploadPHP'}{'username'} $profile{$profile}{'uploadPHP'}{'password'} $config{'uploadPHP_debug'} $config{'uploadPHP_truncated_debug'} $profile{$profile}{'uploadPHP'}{'proxy_user'} $profile{$profile}{'uploadPHP'}{'proxy_password'} $profile{$profile}{'uploadPHP'}{'proxy_ip'} $profile{$profile}{'uploadPHP'}{'proxy_port'} \'$config{'uploadPHP_autoload'}\'|"; + while () { + print $_; # PRINT CURRENT PHP OUPUT LINE + if ($_ =~ m/error/) { + $success = 0; + } + } + + return $success; +} + +sub WipeData { + &Delimiter((caller(0))[3]); + my $key = shift; + + print "Deleting $data{$key}{'FILEPATH'}..."; + unlink($data{$key}{'FILEPATH'}) or die "Could not delete $data{$key}{'FILEPATH'}!\n"; + print " Done.\n"; +} + +sub AddToDB { + &Delimiter((caller(0))[3]); + my $key = shift; + + $data{$key}{'TIMESTAMP_UPLOADED'} = &GetTimestamp('YMDHMS'); + $db{$key} = $data{$key}; + my $json = encode_json \%db; + write_file($profile{$profile}{'DBFilepath'}, { binmode => ':raw' }, $json); +} + +sub Delimiter { + my $SubName = shift; + print "\n" . "-" x 80 . "\nSUB " . $SubName . "\n" . '-' x 80 . "\n"; +} + +sub GetTimestamp { + #&Delimiter((caller(0))[3]); + my $switch = shift; + + my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time); + + my $nice_timestamp; + if ($switch eq 'YMDHMS') { + $nice_timestamp = sprintf ( "%04d%02d%02d_%02d%02d%02d", $year+1900,$mon+1,$mday,$hour,$min,$sec); + } + elsif ($switch eq 'YMD') { + $nice_timestamp = sprintf ( "%04d%02d%02d", $year+1900,$mon+1,$mday); + } + elsif ($switch eq 'year') { + $nice_timestamp = $year+1900; + } + elsif ($switch eq 'month') { + $nice_timestamp = $mon+10; + } + else { + print "Invalid/no switch detected. Use: 'YMDHMS' / 'YMD'\n"; + } + + return $nice_timestamp; +} \ No newline at end of file diff --git a/instafeed/log/dreamyourmansion.log b/instafeed/log/dreamyourmansion.log new file mode 100755 index 0000000..b9a715c --- /dev/null +++ b/instafeed/log/dreamyourmansion.log @@ -0,0 +1,23 @@ + +-------------------------------------------------------------------------------- +SUB main::CheckParameter +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +SUB main::UndumpFromFile +-------------------------------------------------------------------------------- +INFO: ./src/db/db_dreamyourmansion.dat has 191 keys. + +-------------------------------------------------------------------------------- +SUB main::DirectoryListing +-------------------------------------------------------------------------------- +$VAR1 = {}; + +-------------------------------------------------------------------------------- +SUB main::FindNewDataset +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +SUB main::Summary +-------------------------------------------------------------------------------- +./src/db/db_dreamyourmansion.dat has 191 keys (before 191). diff --git a/instafeed/log/vstbestprices.log b/instafeed/log/vstbestprices.log new file mode 100755 index 0000000..240dee3 --- /dev/null +++ b/instafeed/log/vstbestprices.log @@ -0,0 +1,23 @@ + +-------------------------------------------------------------------------------- +SUB main::CheckParameter +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +SUB main::UndumpFromFile +-------------------------------------------------------------------------------- +INFO: ./src/db/db_vstbestprices.dat has 115 keys. + +-------------------------------------------------------------------------------- +SUB main::DirectoryListing +-------------------------------------------------------------------------------- +$VAR1 = {}; + +-------------------------------------------------------------------------------- +SUB main::FindNewDataset +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +SUB main::Summary +-------------------------------------------------------------------------------- +./src/db/db_vstbestprices.dat has 115 keys (before 115). diff --git a/instafeed/src/db/db_dreamyourmansion.dat b/instafeed/src/db/db_dreamyourmansion.dat new file mode 100755 index 0000000..3b33729 --- /dev/null +++ b/instafeed/src/db/db_dreamyourmansion.dat @@ -0,0 +1 @@ +{"./src/images/dreamyourmansion/beach-clouds-dawn-732199.jpg":{"TIMESTAMP_UPLOADED":"20191117_122322","FILEPATH":"./src/images/dreamyourmansion/beach-clouds-dawn-732199.jpg"},"./src/images/dreamyourmansion/angsana-beach-clouds-2417862.jpg":{"TIMESTAMP_UPLOADED":"20191030_223310","FILEPATH":"./src/images/dreamyourmansion/angsana-beach-clouds-2417862.jpg"},"angsana-beach-clouds-2417862.jpg":{"FILEPATH":"/home/pi/instafeed/src/images/angsana-beach-clouds-2417862.jpg","TIMESTAMP_UPLOADED":"20190918_170406"},"./src/images/dreamyourmansion/coast-coastline-daylight-1449778.jpg":{"FILEPATH":"./src/images/dreamyourmansion/coast-coastline-daylight-1449778.jpg","TIMESTAMP_UPLOADED":"20191121_122318"},"./src/images/dreamyourmansion/apartment-architectural-design-architecture-1115804.jpg":{"FILEPATH":"./src/images/dreamyourmansion/apartment-architectural-design-architecture-1115804.jpg","TIMESTAMP_UPLOADED":"20191126_122316"},"./src/images/dreamyourmansion/abraham-lincoln-architecture-attractions-220820.jpg":{"TIMESTAMP_UPLOADED":"20191105_122319","FILEPATH":"./src/images/dreamyourmansion/abraham-lincoln-architecture-attractions-220820.jpg"},"./src/images/dreamyourmansion/AQl-J19ocWE.jpg":{"TIMESTAMP_UPLOADED":"20201009_130503","FILEPATH":"./src/images/dreamyourmansion/AQl-J19ocWE.jpg"},"./src/images/dreamyourmansion/architectural-design-architecture-blue-sky-462358.jpg":{"FILEPATH":"./src/images/dreamyourmansion/architectural-design-architecture-blue-sky-462358.jpg","TIMESTAMP_UPLOADED":"20191101_122317"},"./src/images/dreamyourmansion/castle-facade-fountain-87378.jpg":{"TIMESTAMP_UPLOADED":"20191027_122325","FILEPATH":"./src/images/dreamyourmansion/castle-facade-fountain-87378.jpg"},"./src/images/dreamyourmansion/architecture-art-chandelier-9298.jpg":{"FILEPATH":"./src/images/dreamyourmansion/architecture-art-chandelier-9298.jpg","TIMESTAMP_UPLOADED":"20191106_002324"},"./src/images/dreamyourmansion/ancient-architecture-attractions-208631.jpg":{"TIMESTAMP_UPLOADED":"20191026_122316","FILEPATH":"./src/images/dreamyourmansion/ancient-architecture-attractions-208631.jpg"},"./src/images/dreamyourmansion/architecture-building-daylight-206172.jpg":{"FILEPATH":"./src/images/dreamyourmansion/architecture-building-daylight-206172.jpg","TIMESTAMP_UPLOADED":"20191117_002319"},"./src/images/dreamyourmansion/architecture-brick-wall-bricks-2263682.jpg":{"TIMESTAMP_UPLOADED":"20191030_222855","FILEPATH":"./src/images/dreamyourmansion/architecture-brick-wall-bricks-2263682.jpg"},"./src/images/dreamyourmansion/blue-exotic-hotel-189296.jpg":{"TIMESTAMP_UPLOADED":"20191027_002324","FILEPATH":"./src/images/dreamyourmansion/blue-exotic-hotel-189296.jpg"},"./src/images/dreamyourmansion/clean-holiday-hotel-221457.jpg":{"FILEPATH":"./src/images/dreamyourmansion/clean-holiday-hotel-221457.jpg","TIMESTAMP_UPLOADED":"20191112_002324"},"architectural-design-architecture-daylight-1706625.jpg":{"TIMESTAMP_UPLOADED":"20190926_062631","FILEPATH":"/home/pi/instafeed/src/images/architectural-design-architecture-daylight-1706625.jpg"},"./src/images/dreamyourmansion/beach-blue-water-chair-264468.jpg":{"FILEPATH":"./src/images/dreamyourmansion/beach-blue-water-chair-264468.jpg","TIMESTAMP_UPLOADED":"20191103_002323"},"./src/images/dreamyourmansion/17th-century-courtyard-duchess-of-lauderdale-36355.jpg":{"TIMESTAMP_UPLOADED":"20191201_122317","FILEPATH":"./src/images/dreamyourmansion/17th-century-courtyard-duchess-of-lauderdale-36355.jpg"},"./src/images/dreamyourmansion/architecture-building-city-210493.jpg":{"TIMESTAMP_UPLOADED":"20191109_122320","FILEPATH":"./src/images/dreamyourmansion/architecture-building-city-210493.jpg"},"./src/images/dreamyourmansion/architecture-attractions-buildings-208608.jpg":{"FILEPATH":"./src/images/dreamyourmansion/architecture-attractions-buildings-208608.jpg","TIMESTAMP_UPLOADED":"20191202_002314"},"architecture-building-buy-259098.jpg":{"TIMESTAMP_UPLOADED":"20190920_112034","FILEPATH":"/home/pi/instafeed/src/images/architecture-building-buy-259098.jpg"},"./src/images/dreamyourmansion/architecture-building-castle-534095.jpg":{"TIMESTAMP_UPLOADED":"20191031_122320","FILEPATH":"./src/images/dreamyourmansion/architecture-building-castle-534095.jpg"},"alcohol-bottles-architecture-coconut-trees-1134178.jpg":{"FILEPATH":"/home/pi/instafeed/src/images/alcohol-bottles-architecture-coconut-trees-1134178.jpg","TIMESTAMP_UPLOADED":"20190929_002636"},"apartment-architectural-design-architecture-323774.jpg":{"FILEPATH":"/home/pi/instafeed/src/images/apartment-architectural-design-architecture-323774.jpg","TIMESTAMP_UPLOADED":"20190921_062635"},"./src/images/dreamyourmansion/aerial-photography-beach-bird-s-eye-view-1198838.jpg":{"FILEPATH":"./src/images/dreamyourmansion/aerial-photography-beach-bird-s-eye-view-1198838.jpg","TIMESTAMP_UPLOADED":"20191129_002321"},"./src/images/dreamyourmansion/design-doors-doorway-1834706.jpg":{"FILEPATH":"./src/images/dreamyourmansion/design-doors-doorway-1834706.jpg","TIMESTAMP_UPLOADED":"20191025_002324"},"./src/images/dreamyourmansion/architecture-construction-daylight-534228.jpg":{"FILEPATH":"./src/images/dreamyourmansion/architecture-construction-daylight-534228.jpg","TIMESTAMP_UPLOADED":"20191118_002323"},"architecture-caribbean-chairs-2565222.jpg":{"FILEPATH":"/home/pi/instafeed/src/images/architecture-caribbean-chairs-2565222.jpg","TIMESTAMP_UPLOADED":"20190918_180800"},"./src/images/dreamyourmansion/architecture-building-buses-877994.jpg":{"TIMESTAMP_UPLOADED":"20191111_002328","FILEPATH":"./src/images/dreamyourmansion/architecture-building-buses-877994.jpg"},"./src/images/dreamyourmansion/architecture-buildings-contemporary-1488267.jpg":{"TIMESTAMP_UPLOADED":"20191122_122316","FILEPATH":"./src/images/dreamyourmansion/architecture-buildings-contemporary-1488267.jpg"},"./src/images/dreamyourmansion/architecture-dug-out-pool-hotel-1134175.jpg":{"FILEPATH":"./src/images/dreamyourmansion/architecture-dug-out-pool-hotel-1134175.jpg","TIMESTAMP_UPLOADED":"20191007_002321"},"arched-window-architecture-art-1040893.jpg":{"TIMESTAMP_UPLOADED":"20190927_062636","FILEPATH":"/home/pi/instafeed/src/images/arched-window-architecture-art-1040893.jpg"},"backyard-lights-mansion-32870.jpg":{"FILEPATH":"/home/pi/instafeed/src/images/backyard-lights-mansion-32870.jpg","TIMESTAMP_UPLOADED":"20190927_122634"},"./src/images/dreamyourmansion/arches-architecture-art-316080.jpg":{"FILEPATH":"./src/images/dreamyourmansion/arches-architecture-art-316080.jpg","TIMESTAMP_UPLOADED":"20191014_002328"},"architecture-building-daylight-206172.jpg":{"FILEPATH":"/home/pi/instafeed/src/images/architecture-building-daylight-206172.jpg","TIMESTAMP_UPLOADED":"20190918_145241"},"./src/images/dreamyourmansion/beach-chairs-chairs-clouds-2549029.jpg":{"TIMESTAMP_UPLOADED":"20191024_002328","FILEPATH":"./src/images/dreamyourmansion/beach-chairs-chairs-clouds-2549029.jpg"},"beach-building-daylight-1714975.jpg":{"FILEPATH":"/home/pi/instafeed/src/images/beach-building-daylight-1714975.jpg","TIMESTAMP_UPLOADED":"20190927_182632"},"./src/images/dreamyourmansion/architecture-building-daylight-126271.jpg":{"FILEPATH":"./src/images/dreamyourmansion/architecture-building-daylight-126271.jpg","TIMESTAMP_UPLOADED":"20191017_002325"},"architecture-building-daylight-208421.jpg":{"FILEPATH":"/home/pi/instafeed/src/images/architecture-building-daylight-208421.jpg","TIMESTAMP_UPLOADED":"20190928_182645"},"./src/images/dreamyourmansion/architectural-design-architecture-building-140963.jpg":{"FILEPATH":"./src/images/dreamyourmansion/architectural-design-architecture-building-140963.jpg","TIMESTAMP_UPLOADED":"20191123_002317"},"beam-cabin-clouds-531450.jpg":{"FILEPATH":"/home/pi/instafeed/src/images/beam-cabin-clouds-531450.jpg","TIMESTAMP_UPLOADED":"20190923_182628"},"aerial-photography-beach-bird-s-eye-view-1198838.jpg":{"TIMESTAMP_UPLOADED":"20190918_151656","FILEPATH":"/home/pi/instafeed/src/images/aerial-photography-beach-bird-s-eye-view-1198838.jpg"},"dug-out-pool-garden-swimming-pool-1746876.jpg":{"FILEPATH":"/home/pi/instafeed/src/images/dug-out-pool-garden-swimming-pool-1746876.jpg","TIMESTAMP_UPLOADED":"20190918_170233"},"./src/images/dreamyourmansion/architecture-building-buy-221540.jpg":{"TIMESTAMP_UPLOADED":"20191006_122317","FILEPATH":"./src/images/dreamyourmansion/architecture-building-buy-221540.jpg"},"./src/images/dreamyourmansion/couple-investment-key-1288482.jpg":{"FILEPATH":"./src/images/dreamyourmansion/couple-investment-key-1288482.jpg","TIMESTAMP_UPLOADED":"20191012_002320"},"architecture-buildings-contemporary-1488267.jpg":{"TIMESTAMP_UPLOADED":"20190918_170318","FILEPATH":"/home/pi/instafeed/src/images/architecture-buildings-contemporary-1488267.jpg"},"architecture-building-castle-1270902.jpg":{"FILEPATH":"/home/pi/instafeed/src/images/architecture-building-castle-1270902.jpg","TIMESTAMP_UPLOADED":"20190925_122641"},"architecture-building-daylight-126271.jpg":{"TIMESTAMP_UPLOADED":"20190918_170440","FILEPATH":"/home/pi/instafeed/src/images/architecture-building-daylight-126271.jpg"},"./src/images/dreamyourmansion/KtOid0FLjqU.jpg":{"FILEPATH":"./src/images/dreamyourmansion/KtOid0FLjqU.jpg","TIMESTAMP_UPLOADED":"20201009_131359"},"./src/images/dreamyourmansion/architecture-building-driveway-164522.jpg":{"TIMESTAMP_UPLOADED":"20191004_002333","FILEPATH":"./src/images/dreamyourmansion/architecture-building-driveway-164522.jpg"},"./src/images/dreamyourmansion/dug-out-pool-hotel-poolside-1134176.jpg":{"TIMESTAMP_UPLOADED":"20191129_122319","FILEPATH":"./src/images/dreamyourmansion/dug-out-pool-hotel-poolside-1134176.jpg"},"./src/images/dreamyourmansion/building-exterior-cabin-colors-2343533.jpg":{"FILEPATH":"./src/images/dreamyourmansion/building-exterior-cabin-colors-2343533.jpg","TIMESTAMP_UPLOADED":"20191019_122324"},"beach-boat-bridge-1450350.jpg":{"FILEPATH":"/home/pi/instafeed/src/images/beach-boat-bridge-1450350.jpg","TIMESTAMP_UPLOADED":"20190926_182633"},"./src/images/dreamyourmansion/architecture-chimney-cloudy-skies-1569003.jpg":{"TIMESTAMP_UPLOADED":"20191108_122321","FILEPATH":"./src/images/dreamyourmansion/architecture-chimney-cloudy-skies-1569003.jpg"},"./src/images/dreamyourmansion/architectural-design-architecture-brickwalls-191323.jpg":{"FILEPATH":"./src/images/dreamyourmansion/architectural-design-architecture-brickwalls-191323.jpg","TIMESTAMP_UPLOADED":"20191005_002326"},"./src/images/dreamyourmansion/architecture-building-facade-2710554.jpg":{"TIMESTAMP_UPLOADED":"20191005_122319","FILEPATH":"./src/images/dreamyourmansion/architecture-building-facade-2710554.jpg"},"./src/images/dreamyourmansion/architecture-balcony-blue-sky-343240.jpg":{"TIMESTAMP_UPLOADED":"20191019_002320","FILEPATH":"./src/images/dreamyourmansion/architecture-balcony-blue-sky-343240.jpg"},"architecture-building-daylight-210538.jpg":{"TIMESTAMP_UPLOADED":"20191001_221142","FILEPATH":"./src/images/architecture-building-daylight-210538.jpg"},"ancient-architecture-building-771023.jpg":{"FILEPATH":"/home/pi/instafeed/src/images/ancient-architecture-building-771023.jpg","TIMESTAMP_UPLOADED":"20190921_122639"},"./src/images/dreamyourmansion/architecture-chandelier-clean-210463.jpg":{"FILEPATH":"./src/images/dreamyourmansion/architecture-chandelier-clean-210463.jpg","TIMESTAMP_UPLOADED":"20191021_122321"},"./src/images/dreamyourmansion/architecture-building-buildings-2356336.jpg":{"TIMESTAMP_UPLOADED":"20191114_002323","FILEPATH":"./src/images/dreamyourmansion/architecture-building-buildings-2356336.jpg"},"./src/images/dreamyourmansion/beach-bridge-clouds-1320686.jpg":{"FILEPATH":"./src/images/dreamyourmansion/beach-bridge-clouds-1320686.jpg","TIMESTAMP_UPLOADED":"20191104_002332"},"ceiling-chairs-clean-2343465.jpg":{"FILEPATH":"/home/pi/instafeed/src/images/ceiling-chairs-clean-2343465.jpg","TIMESTAMP_UPLOADED":"20190920_122621"},"./src/images/dreamyourmansion/clouds-coconut-trees-daylight-434657.jpg":{"TIMESTAMP_UPLOADED":"20191012_122322","FILEPATH":"./src/images/dreamyourmansion/clouds-coconut-trees-daylight-434657.jpg"},"architecture-backyard-chairs-2775312.jpg":{"TIMESTAMP_UPLOADED":"20190928_062639","FILEPATH":"/home/pi/instafeed/src/images/architecture-backyard-chairs-2775312.jpg"},"architecture-dug-out-pool-hotel-1134175.jpg":{"TIMESTAMP_UPLOADED":"20190813_191818","FILEPATH":"/home/pi/instafeed/src/images/architecture-dug-out-pool-hotel-1134175.jpg"},"./src/images/dreamyourmansion/so3wgJLwDxo.jpg":{"TIMESTAMP_UPLOADED":"20201009_130725","FILEPATH":"./src/images/dreamyourmansion/so3wgJLwDxo.jpg"},"./src/images/dreamyourmansion/broker-buy-customers-1368687.jpg":{"TIMESTAMP_UPLOADED":"20191030_222938","FILEPATH":"./src/images/dreamyourmansion/broker-buy-customers-1368687.jpg"},"./src/images/dreamyourmansion/ceiling-chairs-contemporary-1884261.jpg":{"TIMESTAMP_UPLOADED":"20191008_122323","FILEPATH":"./src/images/dreamyourmansion/ceiling-chairs-contemporary-1884261.jpg"},"animal-architecture-beautiful-162107.jpg":{"FILEPATH":"/home/pi/instafeed/src/images/animal-architecture-beautiful-162107.jpg","TIMESTAMP_UPLOADED":"20190928_122631"},"architecture-building-daylight-804044.jpg":{"TIMESTAMP_UPLOADED":"20190918_170215","FILEPATH":"/home/pi/instafeed/src/images/architecture-building-daylight-804044.jpg"},"./src/images/dreamyourmansion/daylight-door-entrance-242264.jpg":{"FILEPATH":"./src/images/dreamyourmansion/daylight-door-entrance-242264.jpg","TIMESTAMP_UPLOADED":"20191112_122323"},"./src/images/dreamyourmansion/architecture-design-family-929961.jpg":{"TIMESTAMP_UPLOADED":"20191119_122317","FILEPATH":"./src/images/dreamyourmansion/architecture-design-family-929961.jpg"},"architecture-ceiling-chair-1065883.jpg":{"TIMESTAMP_UPLOADED":"20190923_122630","FILEPATH":"/home/pi/instafeed/src/images/architecture-ceiling-chair-1065883.jpg"},"./src/images/dreamyourmansion/architecture-bed-bedroom-1103808.jpg":{"FILEPATH":"./src/images/dreamyourmansion/architecture-bed-bedroom-1103808.jpg","TIMESTAMP_UPLOADED":"20191016_002302"},"calm-clouds-exotic-297984.jpg":{"FILEPATH":"/home/pi/instafeed/src/images/calm-clouds-exotic-297984.jpg","TIMESTAMP_UPLOADED":"20190925_182639"},"./src/images/dreamyourmansion/architecture-beach-building-258154.jpg":{"FILEPATH":"./src/images/dreamyourmansion/architecture-beach-building-258154.jpg","TIMESTAMP_UPLOADED":"20191021_002322"},"architectural-design-architecture-building-exterior-2212875.jpg":{"TIMESTAMP_UPLOADED":"20190813_192233","FILEPATH":"/home/pi/instafeed/src/images/architectural-design-architecture-building-exterior-2212875.jpg"},"./src/images/dreamyourmansion/architecture-atrium-building-220768.jpg":{"TIMESTAMP_UPLOADED":"20191020_122317","FILEPATH":"./src/images/dreamyourmansion/architecture-atrium-building-220768.jpg"},"architecture-building-daylight-208747.jpg":{"FILEPATH":"/home/pi/instafeed/src/images/architecture-building-daylight-208747.jpg","TIMESTAMP_UPLOADED":"20190922_182631"},"./src/images/dreamyourmansion/architecture-beautiful-exterior-106399.jpg":{"FILEPATH":"./src/images/dreamyourmansion/architecture-beautiful-exterior-106399.jpg","TIMESTAMP_UPLOADED":"20191107_122322"},"./src/images/dreamyourmansion/background-beach-beautiful-2606523.jpg":{"FILEPATH":"./src/images/dreamyourmansion/background-beach-beautiful-2606523.jpg","TIMESTAMP_UPLOADED":"20191102_122326"},"architecture-building-industry-209274.jpg":{"TIMESTAMP_UPLOADED":"20191001_221941","FILEPATH":"./src/images/dreamyourmansion/architecture-building-industry-209274.jpg"},"architecture-art-chandelier-9298.jpg":{"FILEPATH":"/home/pi/instafeed/src/images/architecture-art-chandelier-9298.jpg","TIMESTAMP_UPLOADED":"20190918_170304"},"./src/images/dreamyourmansion/architectural-design-architecture-cabin-1795507.jpg":{"TIMESTAMP_UPLOADED":"20191127_002320","FILEPATH":"./src/images/dreamyourmansion/architectural-design-architecture-cabin-1795507.jpg"},"./src/images/dreamyourmansion/architecture-beautiful-home-building-280229.jpg":{"TIMESTAMP_UPLOADED":"20191113_122321","FILEPATH":"./src/images/dreamyourmansion/architecture-beautiful-home-building-280229.jpg"},"broker-buy-customers-1368687.jpg":{"TIMESTAMP_UPLOADED":"20190918_180708","FILEPATH":"/home/pi/instafeed/src/images/broker-buy-customers-1368687.jpg"},"./src/images/dreamyourmansion/aerial-photography-architecture-bali-2480608.jpg":{"TIMESTAMP_UPLOADED":"20191130_122319","FILEPATH":"./src/images/dreamyourmansion/aerial-photography-architecture-bali-2480608.jpg"},"./src/images/dreamyourmansion/architectural-design-architecture-daylight-2083459.jpg":{"TIMESTAMP_UPLOADED":"20191127_122320","FILEPATH":"./src/images/dreamyourmansion/architectural-design-architecture-daylight-2083459.jpg"},"./src/images/dreamyourmansion/building-downtown-real-estate-36362.jpg":{"FILEPATH":"./src/images/dreamyourmansion/building-downtown-real-estate-36362.jpg","TIMESTAMP_UPLOADED":"20191009_122317"},"beach-blue-coast-1724420.jpg":{"FILEPATH":"/home/pi/instafeed/src/images/beach-blue-coast-1724420.jpg","TIMESTAMP_UPLOADED":"20190918_170543"},"./src/images/dreamyourmansion/architecture-building-dry-leaves-1757516.jpg":{"FILEPATH":"./src/images/dreamyourmansion/architecture-building-dry-leaves-1757516.jpg","TIMESTAMP_UPLOADED":"20191106_122322"},"./src/images/dreamyourmansion/beach-daylight-exotic-2631613.jpg":{"TIMESTAMP_UPLOADED":"20191011_122735","FILEPATH":"./src/images/dreamyourmansion/beach-daylight-exotic-2631613.jpg"},"./src/images/dreamyourmansion/architectural-design-architecture-building-exterior-2212875.jpg":{"FILEPATH":"./src/images/dreamyourmansion/architectural-design-architecture-building-exterior-2212875.jpg","TIMESTAMP_UPLOADED":"20191016_122323"},"./src/images/dreamyourmansion/architecture-chair-color-1080696.jpg":{"FILEPATH":"./src/images/dreamyourmansion/architecture-chair-color-1080696.jpg","TIMESTAMP_UPLOADED":"20191102_002323"},"./src/images/dreamyourmansion/architecture-building-daylight-173229.jpg":{"FILEPATH":"./src/images/dreamyourmansion/architecture-building-daylight-173229.jpg","TIMESTAMP_UPLOADED":"20191201_002317"},"./src/images/dreamyourmansion/architecture-buy-construction-461024.jpg":{"TIMESTAMP_UPLOADED":"20191017_122332","FILEPATH":"./src/images/dreamyourmansion/architecture-buy-construction-461024.jpg"},"architecture-autumn-building-exterior-2179603.jpg":{"FILEPATH":"/home/pi/instafeed/src/images/architecture-autumn-building-exterior-2179603.jpg","TIMESTAMP_UPLOADED":"20190924_182640"},"chairs-contemporary-daylight-1439711.jpg":{"TIMESTAMP_UPLOADED":"20190918_150225","FILEPATH":"/home/pi/instafeed/src/images/chairs-contemporary-daylight-1439711.jpg"},"./src/images/dreamyourmansion/ancient-architecture-building-140019.jpg":{"TIMESTAMP_UPLOADED":"20191011_002336","FILEPATH":"./src/images/dreamyourmansion/ancient-architecture-building-140019.jpg"},"architecture-building-city-221106.jpg":{"TIMESTAMP_UPLOADED":"20190926_122633","FILEPATH":"/home/pi/instafeed/src/images/architecture-building-city-221106.jpg"},"./src/images/dreamyourmansion/adventure-aerial-shot-beach-1456293.jpg":{"FILEPATH":"./src/images/dreamyourmansion/adventure-aerial-shot-beach-1456293.jpg","TIMESTAMP_UPLOADED":"20191030_230729"},"./src/images/dreamyourmansion/ancient-arched-window-architecture-532902.jpg":{"TIMESTAMP_UPLOADED":"20191119_002316","FILEPATH":"./src/images/dreamyourmansion/ancient-arched-window-architecture-532902.jpg"},"./src/images/dreamyourmansion/2d4lAQAlbDA.jpg":{"FILEPATH":"./src/images/dreamyourmansion/2d4lAQAlbDA.jpg","TIMESTAMP_UPLOADED":"20201009_130139"},"./src/images/dreamyourmansion/architecture-building-entrance-187815.jpg":{"TIMESTAMP_UPLOADED":"20191025_122321","FILEPATH":"./src/images/dreamyourmansion/architecture-building-entrance-187815.jpg"},"./src/images/dreamyourmansion/clouds-evening-evening-sky-1724422.jpg":{"FILEPATH":"./src/images/dreamyourmansion/clouds-evening-evening-sky-1724422.jpg","TIMESTAMP_UPLOADED":"20191018_122323"},"./src/images/dreamyourmansion/architecture-building-daylight-158148.jpg":{"FILEPATH":"./src/images/dreamyourmansion/architecture-building-daylight-158148.jpg","TIMESTAMP_UPLOADED":"20191030_223154"},"./src/images/dreamyourmansion/arched-window-architecture-blue-sky-259602.jpg":{"TIMESTAMP_UPLOADED":"20191110_122321","FILEPATH":"./src/images/dreamyourmansion/arched-window-architecture-blue-sky-259602.jpg"},"./src/images/dreamyourmansion/chairs-contemporary-daylight-1439711.jpg":{"TIMESTAMP_UPLOADED":"20191115_002322","FILEPATH":"./src/images/dreamyourmansion/chairs-contemporary-daylight-1439711.jpg"},"architecture-building-daylight-1710484.jpg":{"FILEPATH":"/home/pi/instafeed/src/images/architecture-building-daylight-1710484.jpg","TIMESTAMP_UPLOADED":"20190925_002632"},"./src/images/dreamyourmansion/afternoon-architecture-backyard-271815.jpg":{"FILEPATH":"./src/images/dreamyourmansion/afternoon-architecture-backyard-271815.jpg","TIMESTAMP_UPLOADED":"20191108_002318"},"./src/images/dreamyourmansion/beach-chairs-blue-chairs-2549018.jpg":{"FILEPATH":"./src/images/dreamyourmansion/beach-chairs-blue-chairs-2549018.jpg","TIMESTAMP_UPLOADED":"20191128_002323"},"./src/images/dreamyourmansion/architecture-brick-building-209315.jpg":{"FILEPATH":"./src/images/dreamyourmansion/architecture-brick-building-209315.jpg","TIMESTAMP_UPLOADED":"20191128_122314"},"./src/images/dreamyourmansion/architecture-daylight-driveway-277667.jpg":{"FILEPATH":"./src/images/dreamyourmansion/architecture-daylight-driveway-277667.jpg","TIMESTAMP_UPLOADED":"20191004_122339"},"architecture-bridge-chairs-261410.jpg":{"FILEPATH":"/home/pi/instafeed/src/images/architecture-bridge-chairs-261410.jpg","TIMESTAMP_UPLOADED":"20190921_182646"},"architecture-building-lawn-164539.jpg":{"TIMESTAMP_UPLOADED":"20190919_120616","FILEPATH":"/home/pi/instafeed/src/images/architecture-building-lawn-164539.jpg"},"./src/images/dreamyourmansion/appliances-architecture-ceiling-534151.jpg":{"TIMESTAMP_UPLOADED":"20191008_002318","FILEPATH":"./src/images/dreamyourmansion/appliances-architecture-ceiling-534151.jpg"},"contemporary-counter-door-1380019.jpg":{"FILEPATH":"/home/pi/instafeed/src/images/contemporary-counter-door-1380019.jpg","TIMESTAMP_UPLOADED":"20190924_122603"},"./src/images/dreamyourmansion/2-storey-house-architecture-building-1694360.jpg":{"FILEPATH":"./src/images/dreamyourmansion/2-storey-house-architecture-building-1694360.jpg","TIMESTAMP_UPLOADED":"20191121_002321"},"./src/images/dreamyourmansion/alcohol-bottles-architecture-daytime-1134177.jpg":{"TIMESTAMP_UPLOADED":"20191014_122321","FILEPATH":"./src/images/dreamyourmansion/alcohol-bottles-architecture-daytime-1134177.jpg"},"./src/images/dreamyourmansion/architecture-balcony-building-1658083.jpg":{"TIMESTAMP_UPLOADED":"20191126_002321","FILEPATH":"./src/images/dreamyourmansion/architecture-balcony-building-1658083.jpg"},"./src/images/dreamyourmansion/chairs-columns-decor-1327389.jpg":{"TIMESTAMP_UPLOADED":"20191022_002324","FILEPATH":"./src/images/dreamyourmansion/chairs-columns-decor-1327389.jpg"},"./src/images/dreamyourmansion/architecture-daylight-design-534157.jpg":{"TIMESTAMP_UPLOADED":"20191111_122319","FILEPATH":"./src/images/dreamyourmansion/architecture-daylight-design-534157.jpg"},"abinger-common-ancient-architecture-161791.jpg":{"TIMESTAMP_UPLOADED":"20190921_002637","FILEPATH":"/home/pi/instafeed/src/images/abinger-common-ancient-architecture-161791.jpg"},"./src/images/dreamyourmansion/beach-bungalows-clouds-1449746.jpg":{"TIMESTAMP_UPLOADED":"20191028_002323","FILEPATH":"./src/images/dreamyourmansion/beach-bungalows-clouds-1449746.jpg"},"./src/images/dreamyourmansion/beach-blue-coast-1724420.jpg":{"TIMESTAMP_UPLOADED":"20191006_002323","FILEPATH":"./src/images/dreamyourmansion/beach-blue-coast-1724420.jpg"},"./src/images/dreamyourmansion/architecture-black-and-white-facade-259820.jpg":{"TIMESTAMP_UPLOADED":"20191030_221509","FILEPATH":"./src/images/dreamyourmansion/architecture-black-and-white-facade-259820.jpg"},"contemporary-counter-daylight-1504025.jpg":{"FILEPATH":"/home/pi/instafeed/src/images/contemporary-counter-daylight-1504025.jpg","TIMESTAMP_UPLOADED":"20190928_002636"},"building-exterior-cabin-colors-2343533.jpg":{"FILEPATH":"/home/pi/instafeed/src/images/building-exterior-cabin-colors-2343533.jpg","TIMESTAMP_UPLOADED":"20190916_213744"},"./src/images/dreamyourmansion/dug-out-pool-garden-swimming-pool-1746876.jpg":{"TIMESTAMP_UPLOADED":"20191029_002326","FILEPATH":"./src/images/dreamyourmansion/dug-out-pool-garden-swimming-pool-1746876.jpg"},"architecture-building-garden-1358837.jpg":{"TIMESTAMP_UPLOADED":"20190922_122636","FILEPATH":"/home/pi/instafeed/src/images/architecture-building-garden-1358837.jpg"},"ceiling-chairs-contemporary-1864888.jpg":{"FILEPATH":"/home/pi/instafeed/src/images/ceiling-chairs-contemporary-1864888.jpg","TIMESTAMP_UPLOADED":"20190924_002633"},"./src/images/dreamyourmansion/architecture-building-driveway-284991.jpg":{"FILEPATH":"./src/images/dreamyourmansion/architecture-building-driveway-284991.jpg","TIMESTAMP_UPLOADED":"20191103_122320"},"./src/images/dreamyourmansion/apartment-architecture-artistic-1608165.jpg":{"TIMESTAMP_UPLOADED":"20191023_122324","FILEPATH":"./src/images/dreamyourmansion/apartment-architecture-artistic-1608165.jpg"},"./src/images/dreamyourmansion/architectural-design-architecture-building-1212053.jpg":{"TIMESTAMP_UPLOADED":"20191101_002319","FILEPATH":"./src/images/dreamyourmansion/architectural-design-architecture-building-1212053.jpg"},"architecture-attraction-building-210474.jpg":{"FILEPATH":"/home/pi/instafeed/src/images/architecture-attraction-building-210474.jpg","TIMESTAMP_UPLOADED":"20190923_062641"},"aqua-boardwalk-clouds-2525899.jpg":{"TIMESTAMP_UPLOADED":"20190929_062638","FILEPATH":"/home/pi/instafeed/src/images/aqua-boardwalk-clouds-2525899.jpg"},"./src/images/dreamyourmansion/apartment-architecture-balcony-347141.jpg":{"TIMESTAMP_UPLOADED":"20191004_163559","FILEPATH":"./src/images/dreamyourmansion/apartment-architecture-balcony-347141.jpg"},"./src/images/dreamyourmansion/architecture-backyard-clouds-2513972.jpg":{"TIMESTAMP_UPLOADED":"20191030_122320","FILEPATH":"./src/images/dreamyourmansion/architecture-backyard-clouds-2513972.jpg"},"./src/images/dreamyourmansion/architecture-art-chair-1365225.jpg":{"FILEPATH":"./src/images/dreamyourmansion/architecture-art-chair-1365225.jpg","TIMESTAMP_UPLOADED":"20191031_002321"},"./src/images/dreamyourmansion/architecture-attractive-balcony-210496.jpg":{"TIMESTAMP_UPLOADED":"20191026_002317","FILEPATH":"./src/images/dreamyourmansion/architecture-attractive-balcony-210496.jpg"},"./src/images/dreamyourmansion/architecture-building-campus-207684.jpg":{"TIMESTAMP_UPLOADED":"20191123_122315","FILEPATH":"./src/images/dreamyourmansion/architecture-building-campus-207684.jpg"},"high-angle-shot-hotel-palm-1488291.jpg":{"TIMESTAMP_UPLOADED":"20190919_122613","FILEPATH":"/home/pi/instafeed/src/images/high-angle-shot-hotel-palm-1488291.jpg"},"./src/images/dreamyourmansion/architecture-balcony-daylight-1060950.jpg":{"TIMESTAMP_UPLOADED":"20191007_122322","FILEPATH":"./src/images/dreamyourmansion/architecture-balcony-daylight-1060950.jpg"},"./src/images/dreamyourmansion/architectural-design-architecture-country-home-2287310.jpg":{"TIMESTAMP_UPLOADED":"20191109_002323","FILEPATH":"./src/images/dreamyourmansion/architectural-design-architecture-country-home-2287310.jpg"},"./src/images/dreamyourmansion/architecture-dug-out-pool-family-1488327.jpg":{"TIMESTAMP_UPLOADED":"20191013_122318","FILEPATH":"./src/images/dreamyourmansion/architecture-dug-out-pool-family-1488327.jpg"},"architectural-design-architecture-balcony-2307277.jpg":{"TIMESTAMP_UPLOADED":"20190927_002638","FILEPATH":"/home/pi/instafeed/src/images/architectural-design-architecture-balcony-2307277.jpg"},"./src/images/dreamyourmansion/PyFzygP2eNg.jpg":{"TIMESTAMP_UPLOADED":"20201009_125756","FILEPATH":"./src/images/dreamyourmansion/PyFzygP2eNg.jpg"},"./src/images/dreamyourmansion/arched-window-architectural-design-architecture-1598546.jpg":{"TIMESTAMP_UPLOADED":"20191113_002323","FILEPATH":"./src/images/dreamyourmansion/arched-window-architectural-design-architecture-1598546.jpg"},"architecture-baroque-building-326784.jpg":{"FILEPATH":"/home/pi/instafeed/src/images/architecture-baroque-building-326784.jpg","TIMESTAMP_UPLOADED":"20190930_002330"},"./src/images/dreamyourmansion/architectural-design-architecture-daylight-112283.jpg":{"TIMESTAMP_UPLOADED":"20191125_122316","FILEPATH":"./src/images/dreamyourmansion/architectural-design-architecture-daylight-112283.jpg"},"architecture-building-grass-275516.jpg":{"TIMESTAMP_UPLOADED":"20190925_062633","FILEPATH":"/home/pi/instafeed/src/images/architecture-building-grass-275516.jpg"},"architecture-daylight-exterior-1327445.jpg":{"TIMESTAMP_UPLOADED":"20190926_002634","FILEPATH":"/home/pi/instafeed/src/images/architecture-daylight-exterior-1327445.jpg"},"banking-buy-construction-210617.jpg":{"TIMESTAMP_UPLOADED":"20190916_213409","FILEPATH":"/home/pi/instafeed/src/images/banking-buy-construction-210617.jpg"},"./src/images/dreamyourmansion/beach-beautiful-blue-279574.jpg":{"FILEPATH":"./src/images/dreamyourmansion/beach-beautiful-blue-279574.jpg","TIMESTAMP_UPLOADED":"20191107_002318"},"./src/images/dreamyourmansion/architectural-design-architecture-body-of-water-1438834.jpg":{"FILEPATH":"./src/images/dreamyourmansion/architectural-design-architecture-body-of-water-1438834.jpg","TIMESTAMP_UPLOADED":"20191028_122323"},"architecture-daylight-exterior-112291.jpg":{"TIMESTAMP_UPLOADED":"20190920_182632","FILEPATH":"/home/pi/instafeed/src/images/architecture-daylight-exterior-112291.jpg"},"./src/images/dreamyourmansion/accommodation-beach-bed-1531672.jpg":{"TIMESTAMP_UPLOADED":"20191022_122325","FILEPATH":"./src/images/dreamyourmansion/accommodation-beach-bed-1531672.jpg"},"architecture-asheville-biltmore-estate-259823.jpg":{"TIMESTAMP_UPLOADED":"20190922_062630","FILEPATH":"/home/pi/instafeed/src/images/architecture-asheville-biltmore-estate-259823.jpg"},"./src/images/dreamyourmansion/administration-architecture-building-460716.jpg":{"FILEPATH":"./src/images/dreamyourmansion/administration-architecture-building-460716.jpg","TIMESTAMP_UPLOADED":"20191115_122318"},"./src/images/dreamyourmansion/architectural-design-architecture-construction-1800387.jpg":{"FILEPATH":"./src/images/dreamyourmansion/architectural-design-architecture-construction-1800387.jpg","TIMESTAMP_UPLOADED":"20191015_122302"},"./src/images/dreamyourmansion/architecture-beach-blue-261101.jpg":{"FILEPATH":"./src/images/dreamyourmansion/architecture-beach-blue-261101.jpg","TIMESTAMP_UPLOADED":"20191114_122327"},"./src/images/dreamyourmansion/architectural-design-architecture-building-2280844.jpg":{"FILEPATH":"./src/images/dreamyourmansion/architectural-design-architecture-building-2280844.jpg","TIMESTAMP_UPLOADED":"20191104_122324"},"./src/images/dreamyourmansion/architecture-daylight-driveway-6343.jpg":{"TIMESTAMP_UPLOADED":"20191110_002323","FILEPATH":"./src/images/dreamyourmansion/architecture-daylight-driveway-6343.jpg"},"./src/images/dreamyourmansion/architecture-building-facade-164558.jpg":{"TIMESTAMP_UPLOADED":"20191120_122314","FILEPATH":"./src/images/dreamyourmansion/architecture-building-facade-164558.jpg"},"./src/images/dreamyourmansion/architecture-bricks-buildings-242246.jpg":{"TIMESTAMP_UPLOADED":"20191010_002328","FILEPATH":"./src/images/dreamyourmansion/architecture-bricks-buildings-242246.jpg"},"./src/images/dreamyourmansion/_hw4aUQ81ic.jpg":{"TIMESTAMP_UPLOADED":"20201009_130847","FILEPATH":"./src/images/dreamyourmansion/_hw4aUQ81ic.jpg"},"./src/images/dreamyourmansion/architecture-balcony-building-534182.jpg":{"TIMESTAMP_UPLOADED":"20191029_122320","FILEPATH":"./src/images/dreamyourmansion/architecture-balcony-building-534182.jpg"},"./src/images/dreamyourmansion/architectural-design-architecture-clouds-1732414.jpg":{"TIMESTAMP_UPLOADED":"20191125_002320","FILEPATH":"./src/images/dreamyourmansion/architectural-design-architecture-clouds-1732414.jpg"},"./src/images/dreamyourmansion/architectural-architecture-building-816198.jpg":{"FILEPATH":"./src/images/dreamyourmansion/architectural-architecture-building-816198.jpg","TIMESTAMP_UPLOADED":"20191120_002322"},"./src/images/dreamyourmansion/vbSRUrNm3Ik.jpg":{"TIMESTAMP_UPLOADED":"20201009_131152","FILEPATH":"./src/images/dreamyourmansion/vbSRUrNm3Ik.jpg"},"dark-huts-lights-128303.jpg":{"FILEPATH":"/home/pi/instafeed/src/images/dark-huts-lights-128303.jpg","TIMESTAMP_UPLOADED":"20190924_062603"},"./src/images/dreamyourmansion/architecture-building-daylight-804044.jpg":{"FILEPATH":"./src/images/dreamyourmansion/architecture-building-daylight-804044.jpg","TIMESTAMP_UPLOADED":"20191124_002319"},"architecture-building-condo-280492.jpg":{"FILEPATH":"/home/pi/instafeed/src/images/architecture-building-condo-280492.jpg","TIMESTAMP_UPLOADED":"20190918_195459"},"./src/images/dreamyourmansion/administration-architecture-building-962989.jpg":{"FILEPATH":"./src/images/dreamyourmansion/administration-architecture-building-962989.jpg","TIMESTAMP_UPLOADED":"20191105_002321"},"./src/images/dreamyourmansion/blue-daylight-nature-2549021.jpg":{"TIMESTAMP_UPLOADED":"20191002_155557","FILEPATH":"./src/images/dreamyourmansion/blue-daylight-nature-2549021.jpg"},"./src/images/dreamyourmansion/architectural-design-architecture-building-exterior-616155.jpg":{"FILEPATH":"./src/images/dreamyourmansion/architectural-design-architecture-building-exterior-616155.jpg","TIMESTAMP_UPLOADED":"20191020_002317"},"./src/images/dreamyourmansion/apartment-architecture-balcony-276539.jpg":{"TIMESTAMP_UPLOADED":"20191030_002316","FILEPATH":"./src/images/dreamyourmansion/apartment-architecture-balcony-276539.jpg"},"./src/images/dreamyourmansion/architecture-backyard-brickwalls-221024.jpg":{"FILEPATH":"./src/images/dreamyourmansion/architecture-backyard-brickwalls-221024.jpg","TIMESTAMP_UPLOADED":"20191024_122320"},"./src/images/dreamyourmansion/architecture-backlit-building-2440984.jpg":{"FILEPATH":"./src/images/dreamyourmansion/architecture-backlit-building-2440984.jpg","TIMESTAMP_UPLOADED":"20191118_122321"},"./src/images/dreamyourmansion/architecture-building-cabin-279857.jpg":{"FILEPATH":"./src/images/dreamyourmansion/architecture-building-cabin-279857.jpg","TIMESTAMP_UPLOADED":"20191018_002320"},"./src/images/dreamyourmansion/architecture-daylight-door-1661566.jpg":{"FILEPATH":"./src/images/dreamyourmansion/architecture-daylight-door-1661566.jpg","TIMESTAMP_UPLOADED":"20191010_122332"},"./src/images/dreamyourmansion/architectural-design-architecture-blue-sky-2664118.jpg":{"FILEPATH":"./src/images/dreamyourmansion/architectural-design-architecture-blue-sky-2664118.jpg","TIMESTAMP_UPLOADED":"20191124_122319"},"./src/images/dreamyourmansion/architecture-calm-dug-out-pool-2677398.jpg":{"TIMESTAMP_UPLOADED":"20191002_161141","FILEPATH":"./src/images/dreamyourmansion/architecture-calm-dug-out-pool-2677398.jpg"},"./src/images/dreamyourmansion/chairs-contemporary-counter-1790224.jpg":{"TIMESTAMP_UPLOADED":"20191030_222530","FILEPATH":"./src/images/dreamyourmansion/chairs-contemporary-counter-1790224.jpg"},"./src/images/dreamyourmansion/banking-buy-construction-210617.jpg":{"TIMESTAMP_UPLOADED":"20191130_002317","FILEPATH":"./src/images/dreamyourmansion/banking-buy-construction-210617.jpg"},"./src/images/dreamyourmansion/architectural-design-architecture-bridge-283657.jpg":{"FILEPATH":"./src/images/dreamyourmansion/architectural-design-architecture-bridge-283657.jpg","TIMESTAMP_UPLOADED":"20191023_002324"},"architecture-building-columns-208732.jpg":{"TIMESTAMP_UPLOADED":"20190930_122302","FILEPATH":"/home/pi/instafeed/src/images/architecture-building-columns-208732.jpg"},"./src/images/dreamyourmansion/beach-beautiful-blue-374549.jpg":{"FILEPATH":"./src/images/dreamyourmansion/beach-beautiful-blue-374549.jpg","TIMESTAMP_UPLOADED":"20191122_002321"},"./src/images/dreamyourmansion/ceiling-chairs-contemporary-1864898.jpg":{"FILEPATH":"./src/images/dreamyourmansion/ceiling-chairs-contemporary-1864898.jpg","TIMESTAMP_UPLOADED":"20191015_002302"},"architecture-balcony-blue-sky-343240.jpg":{"TIMESTAMP_UPLOADED":"20190918_145805","FILEPATH":"/home/pi/instafeed/src/images/architecture-balcony-blue-sky-343240.jpg"},"architecture-building-construction-53610.jpg":{"TIMESTAMP_UPLOADED":"20190922_002638","FILEPATH":"/home/pi/instafeed/src/images/architecture-building-construction-53610.jpg"},"beach-chairs-clouds-hotel-338504.jpg":{"FILEPATH":"/home/pi/instafeed/src/images/beach-chairs-clouds-hotel-338504.jpg","TIMESTAMP_UPLOADED":"20190923_002632"},"architecture-grass-landscape-221522.jpg":{"TIMESTAMP_UPLOADED":"20190929_122330","FILEPATH":"/home/pi/instafeed/src/images/architecture-grass-landscape-221522.jpg"},"./src/images/dreamyourmansion/beach-clouds-couple-1287454.jpg":{"FILEPATH":"./src/images/dreamyourmansion/beach-clouds-couple-1287454.jpg","TIMESTAMP_UPLOADED":"20191116_122320"},"./src/images/dreamyourmansion/abandoned-architecture-barn-2360673.jpg":{"TIMESTAMP_UPLOADED":"20191013_002324","FILEPATH":"./src/images/dreamyourmansion/abandoned-architecture-barn-2360673.jpg"},"./src/images/dreamyourmansion/architecture-art-building-1381729.jpg":{"TIMESTAMP_UPLOADED":"20191116_002326","FILEPATH":"./src/images/dreamyourmansion/architecture-art-building-1381729.jpg"},"./src/images/dreamyourmansion/architecture-bushes-chimneys-208736.jpg":{"TIMESTAMP_UPLOADED":"20191009_002321","FILEPATH":"./src/images/dreamyourmansion/architecture-bushes-chimneys-208736.jpg"}} \ No newline at end of file diff --git a/instafeed/src/db/db_vstbestprices.dat b/instafeed/src/db/db_vstbestprices.dat new file mode 100755 index 0000000..5d21f1e --- /dev/null +++ b/instafeed/src/db/db_vstbestprices.dat @@ -0,0 +1 @@ +{"./src/images/vstbestprices//Klevgrand – Modley v1.0.1 – R2R (VST, VST3, AAX, AU) [WiN.OSX x64].png":{"FILEPATH":"./src/images/vstbestprices//Klevgrand – Modley v1.0.1 – R2R (VST, VST3, AAX, AU) [WiN.OSX x64].png","TIMESTAMP_UPLOADED":"20191023_163320"},"./src/images/vstbestprices//Tracktion Software Waveform v9.3.6 [OSX x64].png":{"FILEPATH":"./src/images/vstbestprices//Tracktion Software Waveform v9.3.6 [OSX x64].png","TIMESTAMP_UPLOADED":"20191007_163326"},"./src/images/vstbestprices//Waves Casual – Nylon v1.0.1 (VSTi, VST3) [WiN x64].png":{"FILEPATH":"./src/images/vstbestprices//Waves Casual – Nylon v1.0.1 (VSTi, VST3) [WiN x64].png","TIMESTAMP_UPLOADED":"20191014_083316"},"./src/images/vstbestprices/Positive Grid – BIAS Pedal Pro 2.3.3.5467 (STANDALONE, VST, RTAS, AAX) [WiN x64].jpeg":{"TIMESTAMP_UPLOADED":"20191116_163317","FILEPATH":"./src/images/vstbestprices/Positive Grid – BIAS Pedal Pro 2.3.3.5467 (STANDALONE, VST, RTAS, AAX) [WiN x64].jpeg"},"./src/images/vstbestprices//PG Music – Band-in-a-Box 2019 + RealBand 2019 PlusPAK Build 628 + RealTracks Sets 301-328 + RealTracks Update 2019 (STANDALONE, VST, VST3, AAX) [WiN x86 x64].jpeg":{"FILEPATH":"./src/images/vstbestprices//PG Music – Band-in-a-Box 2019 + RealBand 2019 PlusPAK Build 628 + RealTracks Sets 301-328 + RealTracks Update 2019 (STANDALONE, VST, VST3, AAX) [WiN x86 x64].jpeg","TIMESTAMP_UPLOADED":"20191020_003317"},"./src/images/vstbestprices//Serato – Studio 1.0.0 [WiN x64].jpeg":{"TIMESTAMP_UPLOADED":"20191023_083317","FILEPATH":"./src/images/vstbestprices//Serato – Studio 1.0.0 [WiN x64].jpeg"},"./src/images/vstbestprices//kiloHearts – Toolbox Ultimate v1.7.3 – Rev2 (VSTi, VST, AAX) [WiN x64].png":{"FILEPATH":"./src/images/vstbestprices//kiloHearts – Toolbox Ultimate v1.7.3 – Rev2 (VSTi, VST, AAX) [WiN x64].png","TIMESTAMP_UPLOADED":"20191020_163322"},"./src/images/vstbestprices//W.A.Production – Screamo 1.0.1 (VST, VST3, AAX, AU) [WIN.OSX x64 x86].jpeg":{"FILEPATH":"./src/images/vstbestprices//W.A.Production – Screamo 1.0.1 (VST, VST3, AAX, AU) [WIN.OSX x64 x86].jpeg","TIMESTAMP_UPLOADED":"20191028_083317"},"./src/images/vstbestprices//United Plugins – Soundevice Digital Royal Compressor 1.0 (VST, VST3, AAX) [WiN x64].jpeg":{"FILEPATH":"./src/images/vstbestprices//United Plugins – Soundevice Digital Royal Compressor 1.0 (VST, VST3, AAX) [WiN x64].jpeg","TIMESTAMP_UPLOADED":"20191016_083302"},"./src/images/vstbestprices//Raising Jake Studios – SideMinder ME v1.1.0 (VST, VST3, AAX, AU) [WiN.OSX x64].png":{"TIMESTAMP_UPLOADED":"20191005_003318","FILEPATH":"./src/images/vstbestprices//Raising Jake Studios – SideMinder ME v1.1.0 (VST, VST3, AAX, AU) [WiN.OSX x64].png"},"./src/images/vstbestprices//NoiseAsh – Rule Tec All Collection v1.1.0 (VST, AU, AAX) [WiN.OSX x86 x64].png":{"TIMESTAMP_UPLOADED":"20191025_083318","FILEPATH":"./src/images/vstbestprices//NoiseAsh – Rule Tec All Collection v1.1.0 (VST, AU, AAX) [WiN.OSX x86 x64].png"},"./src/images/vstbestprices/W.A.Production – Imprint v1.0.1 (VST, VST3, AAX, AU) [WiN.OSX x64 x86].jpeg":{"FILEPATH":"./src/images/vstbestprices/W.A.Production – Imprint v1.0.1 (VST, VST3, AAX, AU) [WiN.OSX x64 x86].jpeg","TIMESTAMP_UPLOADED":"20191119_003318"},"./src/images/vstbestprices/Venn Audio – V-Clip v1.0.02 – FiX (VST, VST3, AAX) [WiN x64].jpeg":{"FILEPATH":"./src/images/vstbestprices/Venn Audio – V-Clip v1.0.02 – FiX (VST, VST3, AAX) [WiN x64].jpeg","TIMESTAMP_UPLOADED":"20191114_003308"},"./src/images/vstbestprices//Waves – 10 Complete 10.7.2019 (STANDALONE, VST, VST3, RTAS, AAX, AU) [WIN.OSX x86 x64].jpeg":{"FILEPATH":"./src/images/vstbestprices//Waves – 10 Complete 10.7.2019 (STANDALONE, VST, VST3, RTAS, AAX, AU) [WIN.OSX x86 x64].jpeg","TIMESTAMP_UPLOADED":"20191018_083316"},"./src/images/vstbestprices//u-he – Hive v2.0.0.8791 (VSTi, VST3, AAX) [WiN x86 x64].jpeg":{"TIMESTAMP_UPLOADED":"20191024_083316","FILEPATH":"./src/images/vstbestprices//u-he – Hive v2.0.0.8791 (VSTi, VST3, AAX) [WiN x86 x64].jpeg"},"./src/images/vstbestprices//Tracktion MOK Waverazor v.2.0.2 (AU, VSTi, VSTi3) [OSX].png":{"TIMESTAMP_UPLOADED":"20191008_003316","FILEPATH":"./src/images/vstbestprices//Tracktion MOK Waverazor v.2.0.2 (AU, VSTi, VSTi3) [OSX].png"},"./src/images/vstbestprices/Analog Obsession – Full Bundle 04.2019 (VST, AU) [WIN.OSX x86 x64].jpeg":{"TIMESTAMP_UPLOADED":"20191116_003316","FILEPATH":"./src/images/vstbestprices/Analog Obsession – Full Bundle 04.2019 (VST, AU) [WIN.OSX x86 x64].jpeg"},"./src/images/vstbestprices//SoundSpot – Mercury Bundle 2019.6 (VST, VST3, AAX) [WiN x86 x64].jpeg":{"TIMESTAMP_UPLOADED":"20191015_163302","FILEPATH":"./src/images/vstbestprices//SoundSpot – Mercury Bundle 2019.6 (VST, VST3, AAX) [WiN x86 x64].jpeg"},"./src/images/vstbestprices//Venn Audio – V-Clip – NO INSTALL, SymLink Installer (VST, VST3, AAX) [WiN x64].jpeg":{"TIMESTAMP_UPLOADED":"20191010_163523","FILEPATH":"./src/images/vstbestprices//Venn Audio – V-Clip – NO INSTALL, SymLink Installer (VST, VST3, AAX) [WiN x64].jpeg"},"./src/images/vstbestprices//DDMF – MagicDeathEye v1.0.1 – R2R (VST, VST3, AAX) [WiN x86 x64].png":{"FILEPATH":"./src/images/vstbestprices//DDMF – MagicDeathEye v1.0.1 – R2R (VST, VST3, AAX) [WiN x86 x64].png","TIMESTAMP_UPLOADED":"20191027_083317"},"./src/images/vstbestprices//W.A Production – Ascension 1.0.1 (STANDALONE, VSTi, VSTi3) [WiN x86 x64].jpeg":{"TIMESTAMP_UPLOADED":"20191029_003317","FILEPATH":"./src/images/vstbestprices//W.A Production – Ascension 1.0.1 (STANDALONE, VSTi, VSTi3) [WiN x86 x64].jpeg"},"./src/images/vstbestprices//MAGIX – Vandal v1.112 (VST) [WiN x86 x64].png":{"FILEPATH":"./src/images/vstbestprices//MAGIX – Vandal v1.112 (VST) [WiN x86 x64].png","TIMESTAMP_UPLOADED":"20191026_163320"},"./src/images/vstbestprices//HoRNet – SW34EQ MK2 2.1.2 (VST, VST3, AU) [WIN.OSX x86 x64].jpeg":{"TIMESTAMP_UPLOADED":"20191026_003316","FILEPATH":"./src/images/vstbestprices//HoRNet – SW34EQ MK2 2.1.2 (VST, VST3, AU) [WIN.OSX x86 x64].jpeg"},"./src/images/vstbestprices/Positive Grid – BIAS FX 2 Elite Complete + DeskTop v2.1.1.4655 (STANDALONE, VST, AAX) [WiN x86 x64].jpeg":{"FILEPATH":"./src/images/vstbestprices/Positive Grid – BIAS FX 2 Elite Complete + DeskTop v2.1.1.4655 (STANDALONE, VST, AAX) [WiN x86 x64].jpeg","TIMESTAMP_UPLOADED":"20191115_163324"},"./src/images/vstbestprices//Kuassa – Efektor Distortion Bundle 1.0.6 (VST, VST3, AAX, AU) [WIN.OSX x86 x64].jpeg":{"FILEPATH":"./src/images/vstbestprices//Kuassa – Efektor Distortion Bundle 1.0.6 (VST, VST3, AAX, AU) [WIN.OSX x86 x64].jpeg","TIMESTAMP_UPLOADED":"20191014_163320"},"./src/images/vstbestprices/Kuassa – Amplifikation Matchlock 1.0.0 (STANDALONE, VST, VST3, AAX, AU) [WIN.OSX x86 x64].jpeg":{"FILEPATH":"./src/images/vstbestprices/Kuassa – Amplifikation Matchlock 1.0.0 (STANDALONE, VST, VST3, AAX, AU) [WIN.OSX x86 x64].jpeg","TIMESTAMP_UPLOADED":"20191120_083312"},"./src/images/vstbestprices//uJAM – Virtual Drummer HEAVY 2.0.0.192 (VSTi, AAX) [WiN x64].jpeg":{"TIMESTAMP_UPLOADED":"20191017_083318","FILEPATH":"./src/images/vstbestprices//uJAM – Virtual Drummer HEAVY 2.0.0.192 (VSTi, AAX) [WiN x64].jpeg"},"./src/images/vstbestprices//LUXONIX – ravity Bundle v1.4.3 (STANDALONE, VSTi) [WiN x86].jpeg":{"TIMESTAMP_UPLOADED":"20191007_083320","FILEPATH":"./src/images/vstbestprices//LUXONIX – ravity Bundle v1.4.3 (STANDALONE, VSTi) [WiN x86].jpeg"},"./src/images/vstbestprices//kiloHearts – Toolbox Ultimate 1.7.1 (VST, VSTi, AAX) [WiN x64].jpeg":{"FILEPATH":"./src/images/vstbestprices//kiloHearts – Toolbox Ultimate 1.7.1 (VST, VSTi, AAX) [WiN x64].jpeg","TIMESTAMP_UPLOADED":"20191022_083318"},"./src/images/vstbestprices//Kuassa – Bundle – NO INSTALL, SymLink Installer (VST, VST3, AAX, STANDALONE) [WiN x86 x64].jpeg":{"TIMESTAMP_UPLOADED":"20191012_003316","FILEPATH":"./src/images/vstbestprices//Kuassa – Bundle – NO INSTALL, SymLink Installer (VST, VST3, AAX, STANDALONE) [WiN x86 x64].jpeg"},"./src/images/vstbestprices//NUGEN – Audio Stereoplacer 3.2.0.1 (VST, VST3, RTAS, AAX, AU) [WIN.OSX x86 x64].jpeg":{"TIMESTAMP_UPLOADED":"20191025_003317","FILEPATH":"./src/images/vstbestprices//NUGEN – Audio Stereoplacer 3.2.0.1 (VST, VST3, RTAS, AAX, AU) [WIN.OSX x86 x64].jpeg"},"./src/images/vstbestprices/ISM – DuckDelay v1.0.0 – R2R (VST, VST3, AAX, AU) [WiN.OSX x86 x64].jpeg":{"TIMESTAMP_UPLOADED":"20191119_083313","FILEPATH":"./src/images/vstbestprices/ISM – DuckDelay v1.0.0 – R2R (VST, VST3, AAX, AU) [WiN.OSX x86 x64].jpeg"},"./src/images/vstbestprices//Serato – DJ Pro Suite v2.1.2 [WiN x64].jpeg":{"TIMESTAMP_UPLOADED":"20191022_003318","FILEPATH":"./src/images/vstbestprices//Serato – DJ Pro Suite v2.1.2 [WiN x64].jpeg"},"./src/images/vstbestprices//Stillwell Audio – All Plugins Bundle v3.0.3 (RTAS, VST, VST3) [WiN x86 x64].jpeg":{"TIMESTAMP_UPLOADED":"20191013_163318","FILEPATH":"./src/images/vstbestprices//Stillwell Audio – All Plugins Bundle v3.0.3 (RTAS, VST, VST3) [WiN x86 x64].jpeg"},"./src/images/vstbestprices//Tracktion Software DAW Essentials Collection v1.0.25 (AU, VST) [OSX x64].png":{"FILEPATH":"./src/images/vstbestprices//Tracktion Software DAW Essentials Collection v1.0.25 (AU, VST) [OSX x64].png","TIMESTAMP_UPLOADED":"20191006_163320"},"./src/images/vstbestprices/Klevgrand – FX Bundle 6.2019 (VST, AAX) [WiN x86 x64].jpeg":{"TIMESTAMP_UPLOADED":"20191118_163318","FILEPATH":"./src/images/vstbestprices/Klevgrand – FX Bundle 6.2019 (VST, AAX) [WiN x86 x64].jpeg"},"./src/images/vstbestprices//FeelyoursoundHQ – ChordPotion v1.1.0 (VST) [WiN x64].png":{"TIMESTAMP_UPLOADED":"20191025_163320","FILEPATH":"./src/images/vstbestprices//FeelyoursoundHQ – ChordPotion v1.1.0 (VST) [WiN x64].png"},"./src/images/vstbestprices/Gramotech – Babylon v19.2.4 (VST3) [WiN x64].png":{"TIMESTAMP_UPLOADED":"20191030_221328","FILEPATH":"./src/images/vstbestprices/Gramotech – Babylon v19.2.4 (VST3) [WiN x64].png"},"./src/images/vstbestprices//AIR Music Tech – Hybrid 3.0.7 – REPACK (VSTi) [WiN x86 x64].png":{"TIMESTAMP_UPLOADED":"20191006_003318","FILEPATH":"./src/images/vstbestprices//AIR Music Tech – Hybrid 3.0.7 – REPACK (VSTi) [WiN x86 x64].png"},"./src/images/vstbestprices//HoRNet – DeeLay Plus 1.2.3 (VST, VST3, AAX, AU) [WIN.OSX x86 x64].jpeg":{"FILEPATH":"./src/images/vstbestprices//HoRNet – DeeLay Plus 1.2.3 (VST, VST3, AAX, AU) [WIN.OSX x86 x64].jpeg","TIMESTAMP_UPLOADED":"20191024_163319"},"./src/images/vstbestprices//NUGEN – Audio Stereoizer 3.4.0.1 (VST, VST3, RTAS, AAX, AU) [WIN.OSX x86 x64].jpeg":{"TIMESTAMP_UPLOADED":"20191019_163319","FILEPATH":"./src/images/vstbestprices//NUGEN – Audio Stereoizer 3.4.0.1 (VST, VST3, RTAS, AAX, AU) [WIN.OSX x86 x64].jpeg"},"./src/images/vstbestprices/29Palms – Mastering The Mix Collection 2019.6 – FIXED (STANDALONE, VST, VST3, AAX) [WiN x86 x64].jpeg":{"TIMESTAMP_UPLOADED":"20191030_221110","FILEPATH":"./src/images/vstbestprices/29Palms – Mastering The Mix Collection 2019.6 – FIXED (STANDALONE, VST, VST3, AAX) [WiN x86 x64].jpeg"},"./src/images/vstbestprices//Acon Digital – Verberate Immersive 2 v2.0.2 – R2R (AU, VST, VST3, AAX) [WiN.OSX x86 x64].png":{"FILEPATH":"./src/images/vstbestprices//Acon Digital – Verberate Immersive 2 v2.0.2 – R2R (AU, VST, VST3, AAX) [WiN.OSX x86 x64].png","TIMESTAMP_UPLOADED":"20191021_163322"},"./src/images/vstbestprices/Producers Vault – METALES 1.1 (VSTi) [WiN x86 x64].png":{"TIMESTAMP_UPLOADED":"20191002_160508","FILEPATH":"./src/images/vstbestprices/Producers Vault – METALES 1.1 (VSTi) [WiN x86 x64].png"},"./src/images/vstbestprices//FeelYourSound – XotoPad 2.8.0 [WiN x86].jpeg":{"FILEPATH":"./src/images/vstbestprices//FeelYourSound – XotoPad 2.8.0 [WiN x86].jpeg","TIMESTAMP_UPLOADED":"20191028_163320"},"./src/images/vstbestprices//Tracktion Software BioTek 2 v2.1.2 (AU, VSTi) [OSX x64].png":{"TIMESTAMP_UPLOADED":"20191009_083317","FILEPATH":"./src/images/vstbestprices//Tracktion Software BioTek 2 v2.1.2 (AU, VSTi) [OSX x64].png"},"./src/images/vstbestprices//Ample Sound – Ample Bass Upright III v3.00 (VSTi, VSTi3, AAX, AUi) [OSX x64].png":{"FILEPATH":"./src/images/vstbestprices//Ample Sound – Ample Bass Upright III v3.00 (VSTi, VSTi3, AAX, AUi) [OSX x64].png","TIMESTAMP_UPLOADED":"20191018_003323"},"./src/images/vstbestprices//Native Instruments Massive v1.5.5 (STANDALONE, AU, VST) [OSX].png":{"TIMESTAMP_UPLOADED":"20191005_083321","FILEPATH":"./src/images/vstbestprices//Native Instruments Massive v1.5.5 (STANDALONE, AU, VST) [OSX].png"},"./src/images/vstbestprices//Kuassa – Efektor CP3603 1.0.1 (VST, VST3, AAX) [WiN x86 x64].jpeg":{"TIMESTAMP_UPLOADED":"20191021_003316","FILEPATH":"./src/images/vstbestprices//Kuassa – Efektor CP3603 1.0.1 (VST, VST3, AAX) [WiN x86 x64].jpeg"},"./src/images/vstbestprices//29Palms – Mastering The Mix Collection v18.6.2019 (EXE, VST, VST3, AAX) [WiN x86 x64].jpeg":{"FILEPATH":"./src/images/vstbestprices//29Palms – Mastering The Mix Collection v18.6.2019 (EXE, VST, VST3, AAX) [WiN x86 x64].jpeg","TIMESTAMP_UPLOADED":"20191013_083316"},"./src/images/vstbestprices/Hexachord – Orb Composer Pro v1.2.1 (VSTi) [WiN x64].png":{"TIMESTAMP_UPLOADED":"20191002_160929","FILEPATH":"./src/images/vstbestprices/Hexachord – Orb Composer Pro v1.2.1 (VSTi) [WiN x64].png"},"./src/images/vstbestprices//Zplane – Bundle – NO INSTALL, SymLink Installer (VST, VST3, AAX, STANDALONE) [WiN x86 x64].png":{"FILEPATH":"./src/images/vstbestprices//Zplane – Bundle – NO INSTALL, SymLink Installer (VST, VST3, AAX, STANDALONE) [WiN x86 x64].png","TIMESTAMP_UPLOADED":"20191005_163319"},"./src/images/vstbestprices//Initial Audio – Master Suite v1.0.0 – R2R (VST, VST3, AU) [WIN.OSX x86 x64].png":{"FILEPATH":"./src/images/vstbestprices//Initial Audio – Master Suite v1.0.0 – R2R (VST, VST3, AU) [WIN.OSX x86 x64].png","TIMESTAMP_UPLOADED":"20191030_083314"},"./src/images/vstbestprices//Output – Bundle – NO INSTALL, SymLink Installer (VST, VST3, AAX) [WiN x86 x64].png":{"FILEPATH":"./src/images/vstbestprices//Output – Bundle – NO INSTALL, SymLink Installer (VST, VST3, AAX) [WiN x86 x64].png","TIMESTAMP_UPLOADED":"20191004_163325"},"./src/images/vstbestprices//uJAM – Virtual Drummer PHAT 2.0.0.258 (VSTi, AAX) [WiN x86 x64].jpeg":{"TIMESTAMP_UPLOADED":"20191015_003302","FILEPATH":"./src/images/vstbestprices//uJAM – Virtual Drummer PHAT 2.0.0.258 (VSTi, AAX) [WiN x86 x64].jpeg"},"./src/images/vstbestprices/Unfiltered Audio – Plugins Bundle 2019.6 rev.2 (VST, VST3, AAX) [WiN x86 x64].jpeg":{"TIMESTAMP_UPLOADED":"20191115_003315","FILEPATH":"./src/images/vstbestprices/Unfiltered Audio – Plugins Bundle 2019.6 rev.2 (VST, VST3, AAX) [WiN x86 x64].jpeg"},"./src/images/vstbestprices/Hexachords – Orb Composer S Pro v1.4.4 (VSTi) [WiN x64].jpeg":{"TIMESTAMP_UPLOADED":"20191116_083317","FILEPATH":"./src/images/vstbestprices/Hexachords – Orb Composer S Pro v1.4.4 (VSTi) [WiN x64].jpeg"},"./src/images/vstbestprices//United Plugins – JMG Sound Hyperspace 1.0 (VST, VST3, AAX) [WiN x64].jpeg":{"TIMESTAMP_UPLOADED":"20191030_003314","FILEPATH":"./src/images/vstbestprices//United Plugins – JMG Sound Hyperspace 1.0 (VST, VST3, AAX) [WiN x64].jpeg"},"./src/images/vstbestprices//Audiority – Pedalboard Distortions 1.0.1 (VST, AAX, AU) [WIN.OSX x86 x64].jpeg":{"TIMESTAMP_UPLOADED":"20191017_003316","FILEPATH":"./src/images/vstbestprices//Audiority – Pedalboard Distortions 1.0.1 (VST, AAX, AU) [WIN.OSX x86 x64].jpeg"},"./src/images/vstbestprices//Apple – Logic Pro X 10.4.6 [OSX].png":{"FILEPATH":"./src/images/vstbestprices//Apple – Logic Pro X 10.4.6 [OSX].png","TIMESTAMP_UPLOADED":"20191019_003317"},"./src/images/vstbestprices//NoiseAsh – Bundle – NO INSTALL, SymLink Installer (VST, AAX) [WiN x86 x64].png":{"FILEPATH":"./src/images/vstbestprices//NoiseAsh – Bundle – NO INSTALL, SymLink Installer (VST, AAX) [WiN x86 x64].png","TIMESTAMP_UPLOADED":"20191012_163319"},"./src/images/vstbestprices//IK Multimedia – T-RackS 5 Complete 5.2.1 (STANDALONE, AU, VST2, VST3, AAX) [OSX x64].png":{"FILEPATH":"./src/images/vstbestprices//IK Multimedia – T-RackS 5 Complete 5.2.1 (STANDALONE, AU, VST2, VST3, AAX) [OSX x64].png","TIMESTAMP_UPLOADED":"20191016_003302"},"./src/images/vstbestprices//Audio Damage – AD043 Filterstation2 v2.0.5 (VST, VST3, AAX, AU) [WiN.OSX x86 x64].png":{"TIMESTAMP_UPLOADED":"20191008_163319","FILEPATH":"./src/images/vstbestprices//Audio Damage – AD043 Filterstation2 v2.0.5 (VST, VST3, AAX, AU) [WiN.OSX x86 x64].png"},"./src/images/vstbestprices//Native Instruments – Massive X v1.0.0 (VSTi, AAX) [WiN x64].png":{"FILEPATH":"./src/images/vstbestprices//Native Instruments – Massive X v1.0.0 (VSTi, AAX) [WiN x64].png","TIMESTAMP_UPLOADED":"20191002_154808"},"./src/images/vstbestprices//Xfer Records – Serum v1.23b7 (VSTi, AAX) [WiN x64 x86].png":{"FILEPATH":"./src/images/vstbestprices//Xfer Records – Serum v1.23b7 (VSTi, AAX) [WiN x64 x86].png","TIMESTAMP_UPLOADED":"20191030_220658"},"./src/images/vstbestprices/Steinberg – SpectraLayers Pro v6.0.10 (VST3, AAX, SAL) [WiN x64].jpeg":{"TIMESTAMP_UPLOADED":"20191002_160626","FILEPATH":"./src/images/vstbestprices/Steinberg – SpectraLayers Pro v6.0.10 (VST3, AAX, SAL) [WiN x64].jpeg"},"./src/images/vstbestprices//Soundspot – Mercury Bundle 2019.05.30 (VST, VST3, AU) [OSX].png":{"TIMESTAMP_UPLOADED":"20191026_083317","FILEPATH":"./src/images/vstbestprices//Soundspot – Mercury Bundle 2019.05.30 (VST, VST3, AU) [OSX].png"},"./src/images/vstbestprices/Ample Sound – Ample Bass P III v3.00 (VSTi, VSTi3, AAX) [WiN x64].jpeg":{"FILEPATH":"./src/images/vstbestprices/Ample Sound – Ample Bass P III v3.00 (VSTi, VSTi3, AAX) [WiN x64].jpeg","TIMESTAMP_UPLOADED":"20191118_083317"},"./src/images/vstbestprices//iZotope – Neutron Advanced 3.00 – NO INSTALL, SymLink Installer (VST, VST3, AAX) [WiN x64].jpeg":{"FILEPATH":"./src/images/vstbestprices//iZotope – Neutron Advanced 3.00 – NO INSTALL, SymLink Installer (VST, VST3, AAX) [WiN x64].jpeg","TIMESTAMP_UPLOADED":"20191003_163329"},"./src/images/vstbestprices//Wavesfactory – Mercury FX 1.0.0 (VST, AU) [WIN.OSX x86 x64].jpeg":{"TIMESTAMP_UPLOADED":"20191020_083315","FILEPATH":"./src/images/vstbestprices//Wavesfactory – Mercury FX 1.0.0 (VST, AU) [WIN.OSX x86 x64].jpeg"},"./src/images/vstbestprices/Voxengo – Powershaper 1.1 (VST, VST3, AAX, AU) [WIN.OSX x86 x64].jpeg":{"TIMESTAMP_UPLOADED":"20191117_083316","FILEPATH":"./src/images/vstbestprices/Voxengo – Powershaper 1.1 (VST, VST3, AAX, AU) [WIN.OSX x86 x64].jpeg"},"./src/images/vstbestprices/AIR Music Tech – Grand 1.2.7 – REPACK (VSTi) [WiN x86 x64].jpeg":{"FILEPATH":"./src/images/vstbestprices/AIR Music Tech – Grand 1.2.7 – REPACK (VSTi) [WiN x86 x64].jpeg","TIMESTAMP_UPLOADED":"20191002_160000"},"./src/images/vstbestprices/ToneBoosters – All Plugin Bundle 1.3.0 (VST, VST3, AAX, AU) [WIN.OSX x86 x64].jpeg":{"TIMESTAMP_UPLOADED":"20191120_163316","FILEPATH":"./src/images/vstbestprices/ToneBoosters – All Plugin Bundle 1.3.0 (VST, VST3, AAX, AU) [WIN.OSX x86 x64].jpeg"},"./src/images/vstbestprices//Inertia Sound Systems – Instinct – NO INSTALL, SymLink Installer (VST) [WiN x64].png":{"TIMESTAMP_UPLOADED":"20191008_083319","FILEPATH":"./src/images/vstbestprices//Inertia Sound Systems – Instinct – NO INSTALL, SymLink Installer (VST) [WiN x64].png"},"./src/images/vstbestprices//MeldaProduction – MAudioPlugins 13.03 (VST, VST3, AAX) [WiN x86 x64].jpeg":{"TIMESTAMP_UPLOADED":"20191019_083316","FILEPATH":"./src/images/vstbestprices//MeldaProduction – MAudioPlugins 13.03 (VST, VST3, AAX) [WiN x86 x64].jpeg"},"./src/images/vstbestprices//Output – Portal v1.0.1 (VST, VST3, AU) [WiN x86 x64].png":{"FILEPATH":"./src/images/vstbestprices//Output – Portal v1.0.1 (VST, VST3, AU) [WiN x86 x64].png","TIMESTAMP_UPLOADED":"20191014_003318"},"./src/images/vstbestprices//NoiseAsh – Need 31102 Console EQ v1.0 – R2R (VST, AAX, AU) [WiN.OSX x86 x64].png":{"FILEPATH":"./src/images/vstbestprices//NoiseAsh – Need 31102 Console EQ v1.0 – R2R (VST, AAX, AU) [WiN.OSX x86 x64].png","TIMESTAMP_UPLOADED":"20191022_163326"},"./src/images/vstbestprices//Ample Sound – Ample Guitar M III 3.01 (VSTi, VSTi3, AAX, AUi) [OSX x64].png":{"FILEPATH":"./src/images/vstbestprices//Ample Sound – Ample Guitar M III 3.01 (VSTi, VSTi3, AAX, AUi) [OSX x64].png","TIMESTAMP_UPLOADED":"20191027_003318"},"./src/images/vstbestprices//AIR Music Tech – Velvet 2.0.7 – REPACK (VSTi) [WiN x86 x64].png":{"TIMESTAMP_UPLOADED":"20191009_003317","FILEPATH":"./src/images/vstbestprices//AIR Music Tech – Velvet 2.0.7 – REPACK (VSTi) [WiN x86 x64].png"},"./src/images/vstbestprices//Propellerhead – Reason Limited v1.5.3 [OSX x64].png":{"TIMESTAMP_UPLOADED":"20191009_163319","FILEPATH":"./src/images/vstbestprices//Propellerhead – Reason Limited v1.5.3 [OSX x64].png"},"./src/images/vstbestprices/Cockos – REAPER v5.979 + Rus + Portable + Plugins [Win x86 x64].png":{"FILEPATH":"./src/images/vstbestprices/Cockos – REAPER v5.979 + Rus + Portable + Plugins [Win x86 x64].png","TIMESTAMP_UPLOADED":"20191117_163320"},"./src/images/vstbestprices/Guda Audio – EnvelopR 1.4 (VST, AU) [WIN.OSX x86 x64].jpeg":{"FILEPATH":"./src/images/vstbestprices/Guda Audio – EnvelopR 1.4 (VST, AU) [WIN.OSX x86 x64].jpeg","TIMESTAMP_UPLOADED":"20191118_003316"},"./src/images/vstbestprices//Steinberg – Groove Agent 5.0.10.99 (STANDALONE, VSTi3, AAX) [WiN x64].jpeg":{"FILEPATH":"./src/images/vstbestprices//Steinberg – Groove Agent 5.0.10.99 (STANDALONE, VSTi3, AAX) [WiN x64].jpeg","TIMESTAMP_UPLOADED":"20191004_083329"},"./src/images/vstbestprices/Voxengo – bundle 6.2019 rev – 2 (VST, VST3, AAX) [WiN x86 x64].jpeg":{"FILEPATH":"./src/images/vstbestprices/Voxengo – bundle 6.2019 rev – 2 (VST, VST3, AAX) [WiN x86 x64].jpeg","TIMESTAMP_UPLOADED":"20191120_003313"},"./src/images/vstbestprices/Positive Grid – BIAS AMP 2 Elite Complete 2.2.8.1409 (STANDALONE, VST, RTAS, AAX) [WiN x86 x64].jpeg":{"FILEPATH":"./src/images/vstbestprices/Positive Grid – BIAS AMP 2 Elite Complete 2.2.8.1409 (STANDALONE, VST, RTAS, AAX) [WiN x86 x64].jpeg","TIMESTAMP_UPLOADED":"20191030_221015"},"./src/images/vstbestprices//u-he – Diva v1.4.3.8791 (VSTi, VST3, AAX) [WiN x86 x64].jpeg":{"FILEPATH":"./src/images/vstbestprices//u-he – Diva v1.4.3.8791 (VSTi, VST3, AAX) [WiN x86 x64].jpeg","TIMESTAMP_UPLOADED":"20191007_003319"},"./src/images/vstbestprices//Sonible – smartComp v1.0.0 (VST, VST3, AAX) [WiN x86 x64].png":{"TIMESTAMP_UPLOADED":"20191030_163327","FILEPATH":"./src/images/vstbestprices//Sonible – smartComp v1.0.0 (VST, VST3, AAX) [WiN x86 x64].png"},"./src/images/vstbestprices//Ableton – Live Suite v10.1 – R2R [WiN.OSX x64].png":{"FILEPATH":"./src/images/vstbestprices//Ableton – Live Suite v10.1 – R2R [WiN.OSX x64].png","TIMESTAMP_UPLOADED":"20191018_163321"},"./src/images/vstbestprices//UJAM – Drums 2 Bundle – NO INSTALL, SymLink Installer (VSTi, AAX) [WiN x64].jpeg":{"TIMESTAMP_UPLOADED":"20191004_003325","FILEPATH":"./src/images/vstbestprices//UJAM – Drums 2 Bundle – NO INSTALL, SymLink Installer (VSTi, AAX) [WiN x64].jpeg"},"./src/images/vstbestprices//PreSonus – Studio One 4 Professional 4.5.1 -R2R [WIN.OSX x64].jpeg":{"FILEPATH":"./src/images/vstbestprices//PreSonus – Studio One 4 Professional 4.5.1 -R2R [WIN.OSX x64].jpeg","TIMESTAMP_UPLOADED":"20191023_003317"},"./src/images/vstbestprices//Vengeance Producer Suite – Avenger v1.4.10 + Factory content (VSTi, VSTi3, AAX) [WiN x64].jpeg":{"FILEPATH":"./src/images/vstbestprices//Vengeance Producer Suite – Avenger v1.4.10 + Factory content (VSTi, VSTi3, AAX) [WiN x64].jpeg","TIMESTAMP_UPLOADED":"20191012_083317"},"./src/images/vstbestprices//zplane – Elastique & PPM Bundle 6.2019 (SAL, RTAS, VST, VST3, AAX) [WiN x86 x64].jpeg":{"TIMESTAMP_UPLOADED":"20191024_003317","FILEPATH":"./src/images/vstbestprices//zplane – Elastique & PPM Bundle 6.2019 (SAL, RTAS, VST, VST3, AAX) [WiN x86 x64].jpeg"},"./src/images/vstbestprices//VAST Dynamics – Vaporizer2 v2.4.1 – NO INSTALL, SymLink Installer (VSTi, VSTi3, STANDALONE) [WiN x86 x64].jpeg":{"FILEPATH":"./src/images/vstbestprices//VAST Dynamics – Vaporizer2 v2.4.1 – NO INSTALL, SymLink Installer (VSTi, VSTi3, STANDALONE) [WiN x86 x64].jpeg","TIMESTAMP_UPLOADED":"20191006_083317"},"./src/images/vstbestprices//Ample Sound – Ample Ethno Ukulele III v3.00 (VSTi, VSTi3, AAX) [WiN x64].jpeg":{"TIMESTAMP_UPLOADED":"20191016_163323","FILEPATH":"./src/images/vstbestprices//Ample Sound – Ample Ethno Ukulele III v3.00 (VSTi, VSTi3, AAX) [WiN x64].jpeg"},"./src/images/vstbestprices//Tracktion Software Collective v1.2.1 (AU, VST) [OSX x64].png":{"TIMESTAMP_UPLOADED":"20191013_003317","FILEPATH":"./src/images/vstbestprices//Tracktion Software Collective v1.2.1 (AU, VST) [OSX x64].png"},"./src/images/vstbestprices//AIR Music Tech – Xpand 2.2.7 – REPACK (VSTi) [WiN x86 x64].png":{"FILEPATH":"./src/images/vstbestprices//AIR Music Tech – Xpand 2.2.7 – REPACK (VSTi) [WiN x86 x64].png","TIMESTAMP_UPLOADED":"20191010_083329"},"./src/images/vstbestprices//Kuassa – Amplification Bundle 2019.7 (STANDALONE, VST, VST3, AAX) [WiN x86 x64].jpeg":{"FILEPATH":"./src/images/vstbestprices//Kuassa – Amplification Bundle 2019.7 (STANDALONE, VST, VST3, AAX) [WiN x86 x64].jpeg","TIMESTAMP_UPLOADED":"20191029_163324"},"./src/images/vstbestprices/Image-Line – FL Studio Producer Edition + Signature Bundle v20.5.0.1142 [WiN x86 x64].png":{"FILEPATH":"./src/images/vstbestprices/Image-Line – FL Studio Producer Edition + Signature Bundle v20.5.0.1142 [WiN x86 x64].png","TIMESTAMP_UPLOADED":"20191119_163319"},"./src/images/vstbestprices//iZotope – Neutron 3 Advanced v3.00 PROPER – R2R (VST, VST3, AAX) [WiN х86 x64].png":{"TIMESTAMP_UPLOADED":"20191027_163320","FILEPATH":"./src/images/vstbestprices//iZotope – Neutron 3 Advanced v3.00 PROPER – R2R (VST, VST3, AAX) [WiN х86 x64].png"},"./src/images/vstbestprices//SonicLAB – Cosmos Saturn7 (STANDALONE, VSTi3) [WiN x64].jpeg":{"TIMESTAMP_UPLOADED":"20191002_210735","FILEPATH":"./src/images/vstbestprices//SonicLAB – Cosmos Saturn7 (STANDALONE, VSTi3) [WiN x64].jpeg"},"./src/images/vstbestprices/Overloud – Gem OTD-2 1.0.0 (STANDALONE, VST, VST3, AAX, AU) [WIN.OSX x86 x64].jpeg":{"FILEPATH":"./src/images/vstbestprices/Overloud – Gem OTD-2 1.0.0 (STANDALONE, VST, VST3, AAX, AU) [WIN.OSX x86 x64].jpeg","TIMESTAMP_UPLOADED":"20191115_083308"},"./src/images/vstbestprices//Gramotech – Pompeii v19.5.0 (VST3) [WiN x64].png":{"TIMESTAMP_UPLOADED":"20191015_083302","FILEPATH":"./src/images/vstbestprices//Gramotech – Pompeii v19.5.0 (VST3) [WiN x64].png"},"./src/images/vstbestprices//Tracktion Software Master Mix 1.2.0 (AU, VST, VST3) [OSX x64].png":{"FILEPATH":"./src/images/vstbestprices//Tracktion Software Master Mix 1.2.0 (AU, VST, VST3) [OSX x64].png","TIMESTAMP_UPLOADED":"20191011_083358"},"./src/images/vstbestprices//Sonic Academy – KICK 2 v1.1.1 -R2R (VSTi, AAX, AU) [WiN x86 x64].png":{"TIMESTAMP_UPLOADED":"20191017_163320","FILEPATH":"./src/images/vstbestprices//Sonic Academy – KICK 2 v1.1.1 -R2R (VSTi, AAX, AU) [WiN x86 x64].png"},"./src/images/vstbestprices//uJAM – Virtual Drummer SOLID 2.0.0.2504 (VSTi, AAX) [WiN x64].jpeg":{"TIMESTAMP_UPLOADED":"20191029_083314","FILEPATH":"./src/images/vstbestprices//uJAM – Virtual Drummer SOLID 2.0.0.2504 (VSTi, AAX) [WiN x64].jpeg"},"./src/images/vstbestprices/Inertia Sound Systems – Instinct v1.0.0 (VST) [WiN x64].png":{"FILEPATH":"./src/images/vstbestprices/Inertia Sound Systems – Instinct v1.0.0 (VST) [WiN x64].png","TIMESTAMP_UPLOADED":"20191121_003313"},"./src/images/vstbestprices//Overloud – TH-U Complete v1.0.13 – R2R (EXE, VST, VST3, AU, AAX) [WIN.OSX x86 x64].jpeg":{"TIMESTAMP_UPLOADED":"20191028_003319","FILEPATH":"./src/images/vstbestprices//Overloud – TH-U Complete v1.0.13 – R2R (EXE, VST, VST3, AU, AAX) [WIN.OSX x86 x64].jpeg"},"./src/images/vstbestprices/AIR Music Tech – DB-33 1.2.7 – REPACK (VSTi) [WiN x86 x64].png":{"TIMESTAMP_UPLOADED":"20191002_155730","FILEPATH":"./src/images/vstbestprices/AIR Music Tech – DB-33 1.2.7 – REPACK (VSTi) [WiN x86 x64].png"},"./src/images/vstbestprices//Z3 Audiolabs – Repeat X 1.1 – NO INSTALL, SymLink Installer (VST) [WiN x86].png":{"FILEPATH":"./src/images/vstbestprices//Z3 Audiolabs – Repeat X 1.1 – NO INSTALL, SymLink Installer (VST) [WiN x86].png","TIMESTAMP_UPLOADED":"20191010_003317"},"./src/images/vstbestprices/Tritik – Echorus 1.0.4 (VST, AAX, AU) [WIN.OSX x86 x64].jpeg":{"FILEPATH":"./src/images/vstbestprices/Tritik – Echorus 1.0.4 (VST, AAX, AU) [WIN.OSX x86 x64].jpeg","TIMESTAMP_UPLOADED":"20191117_003317"},"./src/images/vstbestprices/Tracktion Software Waveform v9.3.4 [OSX x64].png":{"FILEPATH":"./src/images/vstbestprices/Tracktion Software Waveform v9.3.4 [OSX x64].png","TIMESTAMP_UPLOADED":"20191002_160823"},"./src/images/vstbestprices//Tracktion Software Waveform 10.2.1 [OSX x64].png":{"TIMESTAMP_UPLOADED":"20191011_003436","FILEPATH":"./src/images/vstbestprices//Tracktion Software Waveform 10.2.1 [OSX x64].png"},"./src/images/vstbestprices/Native Instruments – Reaktor 6.3.1 (STANDALONE, VSTi, AU) [OSX x64].png":{"FILEPATH":"./src/images/vstbestprices/Native Instruments – Reaktor 6.3.1 (STANDALONE, VSTi, AU) [OSX x64].png","TIMESTAMP_UPLOADED":"20191114_123330"},"./src/images/vstbestprices//VAST Dynamics – Vaporizer2 v2.4.1 (VSTi, VSTi3, AUi) [WIN.OSX x86 x64].jpeg":{"FILEPATH":"./src/images/vstbestprices//VAST Dynamics – Vaporizer2 v2.4.1 (VSTi, VSTi3, AUi) [WIN.OSX x86 x64].jpeg","TIMESTAMP_UPLOADED":"20191021_083318"},"./src/images/vstbestprices//United Plugins – Bundle – NO INSTALL, SymLink Installer (VST, VST3, AAX) [WiN x64].png":{"TIMESTAMP_UPLOADED":"20191011_163320","FILEPATH":"./src/images/vstbestprices//United Plugins – Bundle – NO INSTALL, SymLink Installer (VST, VST3, AAX) [WiN x64].png"}} \ No newline at end of file diff --git a/instafeed/src/images/dreamyourmansion/7VPFyhB_j8Y.jpg b/instafeed/src/images/dreamyourmansion/7VPFyhB_j8Y.jpg new file mode 100755 index 0000000..0bc8a77 Binary files /dev/null and b/instafeed/src/images/dreamyourmansion/7VPFyhB_j8Y.jpg differ diff --git a/instafeed/src/images/dreamyourmansion/RKdLlTyjm5g.jpg b/instafeed/src/images/dreamyourmansion/RKdLlTyjm5g.jpg new file mode 100755 index 0000000..9c2832d Binary files /dev/null and b/instafeed/src/images/dreamyourmansion/RKdLlTyjm5g.jpg differ diff --git a/instafeed/src/images/dreamyourmansion/UV81E0oXXWQ.jpg b/instafeed/src/images/dreamyourmansion/UV81E0oXXWQ.jpg new file mode 100755 index 0000000..446e0a0 Binary files /dev/null and b/instafeed/src/images/dreamyourmansion/UV81E0oXXWQ.jpg differ diff --git a/instafeed/vendor/autoload.php b/instafeed/vendor/autoload.php new file mode 100755 index 0000000..f626d9e --- /dev/null +++ b/instafeed/vendor/autoload.php @@ -0,0 +1,7 @@ +/dev/null" to send STDOUT to the void... + * That way you'll ONLY see critical status messages during the processing. + */ + +set_time_limit(0); +date_default_timezone_set('UTC'); + +// Verify minimum PHP version. +if (!defined('PHP_VERSION_ID') || PHP_VERSION_ID < 50600) { + fwrite(STDERR, 'LazyDoctor requires PHP 5.6 or higher.'.PHP_EOL); + exit(1); +} + +// Register a simple GetOptionKit autoloader. This is fine because +// GetOptionKit has no 3rd party library dependencies. +spl_autoload_register(function ($class) { + // Check if this is a "GetOptionKit" load-request. + static $prefix = 'GetOptionKit\\'; + static $len = 13; // strlen($prefix) + if (strncmp($prefix, $class, $len) !== 0) { + return; + } + + // Find the "GetOptionKit" source folder. + static $dirs = [ + __DIR__.'/../../../corneltek/getoptionkit/src', + __DIR__.'/../vendor/corneltek/getoptionkit/src', + ]; + $baseDir = null; + foreach ($dirs as $dir) { + if (is_dir($dir) && ($dir = realpath($dir)) !== false) { + $baseDir = $dir; + break; + } + } + if ($baseDir === null) { + return; + } + + // Get the relative class name. + $relativeClass = substr($class, $len); + + // Generate PSR-4 file path to the class. + $file = sprintf('%s/%s.php', $baseDir, str_replace('\\', '/', $relativeClass)); + if (is_file($file)) { + require $file; + } +}); + +// Parse command line options... +use GetOptionKit\OptionCollection; +use GetOptionKit\OptionParser; +use GetOptionKit\OptionPrinter\ConsoleOptionPrinter; + +$specs = new OptionCollection(); +$specs->add('c|composer:=file', 'Path to your project\'s composer.json file.'); +$specs->add('p|properties?=boolean', 'Document virtual properties (if enabled for the classes).'); +$specs->add('f|functions?=boolean', 'Document virtual functions (if enabled for the classes).'); +$specs->add('o|document-overridden?=boolean', 'Always document virtual functions/properties even when they have been manually overridden by the class (or its parents).'); +$specs->add('w|windows?=boolean', 'Generate Windows-style ("\r\n") documentation line endings instead of the default Unix-style ("\n").'); +$specs->add('validate-only?=boolean', 'Validate current docs for all classes but don\'t write anything to disk.'); +$specs->add('h|help?=boolean', 'Show all available options.'); + +try { + $parser = new OptionParser($specs); + $result = $parser->parse($argv); + $options = [ + 'composer' => isset($result->keys['composer']) ? $result->keys['composer']->value : null, + 'properties' => isset($result->keys['properties']) && $result->keys['properties']->value !== false, + 'functions' => isset($result->keys['functions']) && $result->keys['functions']->value !== false, + 'document-overridden' => isset($result->keys['document-overridden']) && $result->keys['document-overridden']->value !== false, + 'windows' => isset($result->keys['windows']) && $result->keys['windows']->value !== false, + 'validate-only' => isset($result->keys['validate-only']) && $result->keys['validate-only']->value !== false, + 'help' => isset($result->keys['help']) && $result->keys['help']->value !== false, + ]; +} catch (Exception $e) { + // Warns in case of invalid option values. + fwrite(STDERR, $e->getMessage().PHP_EOL); + exit(1); +} + +// Verify options... +echo '[ LazyDoctor ]'.PHP_EOL.PHP_EOL; +if ($options['composer'] === null || $options['help']) { + if ($options['composer'] === null) { + fwrite(STDERR, 'You must provide the --composer option.'.PHP_EOL.PHP_EOL); + } + $printer = new ConsoleOptionPrinter(); + echo 'Available options:'.PHP_EOL.PHP_EOL; + echo $printer->render($specs); + exit($options['composer'] === null && !$options['help'] ? 1 : 0); +} + +if ($options['composer']->getBasename() !== 'composer.json') { + fwrite(STDERR, 'You must point to your project\'s composer.json file.'.PHP_EOL.'You used: "'.$options['composer']->getRealPath().'".'.PHP_EOL); + exit(1); +} + +// Decode the composer.json file... +$json = @json_decode(file_get_contents($options['composer']->getRealPath()), true); +if ($json === null) { + fwrite(STDERR, 'Unable to decode composer.json.'.PHP_EOL); + exit(1); +} + +// Determine the project folder's real root path... +$projectRoot = $options['composer']->getPathInfo()->getRealPath(); + +// Determine their namespace PSR-4 paths via their project's composer.json... +$namespaces = []; +foreach (['autoload', 'autoload-dev'] as $type) { + if (!isset($json[$type]['psr-4']) || !is_array($json[$type]['psr-4'])) { + continue; + } + + foreach ($json[$type]['psr-4'] as $namespace => $dir) { + // We don't support composer's empty "fallback" namespaces. + if ($namespace === '') { + fwrite(STDERR, 'Encountered illegal unnamed PSR-4 autoload namespace in composer.json.'.PHP_EOL); + exit(1); + } + + // Ensure that the namespace ends in backslash. + if (substr_compare($namespace, '\\', strlen($namespace) - 1, 1) !== 0) { + fwrite(STDERR, 'Encountered illegal namespace "'.$namespace.'" (does not end in backslash) in composer.json.'.PHP_EOL); + exit(1); + } + + // Ensure that the value is a string. + // NOTE: We allow empty strings, which corresponds to root folder. + if (!is_string($dir)) { + fwrite(STDERR, 'Encountered illegal non-string value for namespace "'.$namespace.'".'.PHP_EOL); + exit(1); + } + + // Now resolve the path name... + $path = sprintf('%s/%s', $projectRoot, $dir); + $realpath = realpath($path); + if ($realpath === false) { + fwrite(STDERR, 'Unable to resolve real path for "'.$path.'".'.PHP_EOL); + exit(1); + } + + // We don't allow the same directory to be defined multiple times. + if (isset($namespaces[$realpath])) { + fwrite(STDERR, 'Encountered duplicate namespace directory "'.$realpath.'" in composer.json.'.PHP_EOL); + exit(1); + } + + // And we're done! The namespace and its path have been resolved. + $namespaces[$realpath] = $namespace; + } +} + +// Verify that we found some namespaces... +if (empty($namespaces)) { + fwrite(STDERR, 'There are no PSR-4 autoload namespaces in your composer.json.'.PHP_EOL); + exit(1); +} + +// Now load the project's autoload.php file. +// NOTE: This is necessary so that we can autoload their classes... +$autoload = sprintf('%s/vendor/autoload.php', $projectRoot); +$realautoload = realpath($autoload); +if ($realautoload === false) { + fwrite(STDERR, 'Unable to find the project\'s Composer autoloader ("'.$autoload.'").'.PHP_EOL); + exit(1); +} +require $realautoload; + +// Verify that their project's autoloader contains LazyJsonMapper... +if (!class_exists('\LazyJsonMapper\LazyJsonMapper', true)) { // TRUE = Autoload. + fwrite(STDERR, 'Target project doesn\'t contain the LazyJsonMapper library.'.PHP_EOL); + exit(1); +} + +// Alright, display the current options... +echo 'Project: "'.$projectRoot.'".'.PHP_EOL + .'- Documentation Line Endings: '.($options['windows'] ? 'Windows ("\r\n")' : 'Unix ("\n")').'.'.PHP_EOL + .'- ['.($options['properties'] ? 'X' : ' ').'] Document Virtual Properties ("@property").'.PHP_EOL + .'- ['.($options['functions'] ? 'X' : ' ').'] Document Virtual Functions ("@method").'.PHP_EOL + .'- ['.($options['document-overridden'] ? 'X' : ' ').'] Document Overridden Properties/Functions.'.PHP_EOL; +if ($options['validate-only']) { + echo '- This is a validation run. Nothing will be written to disk.'.PHP_EOL; +} + +// We can now use our custom classes, since the autoloader has been imported... +use LazyJsonMapper\Exception\LazyJsonMapperException; +use LazyJsonMapper\Export\PropertyDescription; +use LazyJsonMapper\Property\PropertyMapCache; +use LazyJsonMapper\Property\PropertyMapCompiler; +use LazyJsonMapper\Utilities; + +/** + * Automatic LazyJsonMapper-class documentation generator. + * + * @copyright 2017 The LazyJsonMapper Project + * @license http://www.apache.org/licenses/LICENSE-2.0 + * @author SteveJobzniak (https://github.com/SteveJobzniak) + */ +class LazyClassDocumentor +{ + /** @var PropertyMapCache */ + private static $_propertyMapCache; + + /** @var array */ + private $_compiledPropertyMapLink; + + /** @var ReflectionClass */ + private $_reflector; + + /** @var array */ + private $_options; + + /** @var string Newline sequence. */ + private $_nl; + + /** + * Constructor. + * + * @param string $class + * @param array $options + * + * @throws ReflectionException + */ + public function __construct( + $class, + array $options) + { + if (self::$_propertyMapCache === null) { + self::$_propertyMapCache = new PropertyMapCache(); + } + $this->_reflector = new ReflectionClass($class); + $this->_options = $options; + $this->_nl = $options['windows'] ? "\r\n" : "\n"; + } + + /** + * Process the current class. + * + * @throws ReflectionException + * @throws LazyJsonMapperException + * + * @return bool `TRUE` if on-disk file has correct docs, otherwise `FALSE`. + */ + public function process() + { + // Only process user-defined classes (never any built-in PHP classes). + if (!$this->_reflector->isUserDefined()) { + return true; + } + + // There's nothing to do if this isn't a LazyJsonMapper subclass. + // NOTE: This properly skips "\LazyJsonMapper\LazyJsonMapper" itself. + if (!$this->_reflector->isSubclassOf('\LazyJsonMapper\LazyJsonMapper')) { + return true; + } + + // Compile this class property map if not yet built and cached. + $thisClassName = $this->_reflector->getName(); + if (!isset(self::$_propertyMapCache->classMaps[$thisClassName])) { + try { + PropertyMapCompiler::compileClassPropertyMap( // Throws. + self::$_propertyMapCache, + $thisClassName + ); + } catch (Exception $e) { + fwrite(STDERR, '> Unable to compile the class property map for "'.$thisClassName.'". Reason: '.$e->getMessage().PHP_EOL); + + return false; + } + } + + // Now link to the property map cache for our current class. + $this->_compiledPropertyMapLink = &self::$_propertyMapCache->classMaps[$thisClassName]; + + // Get the current class comment (string if ok, FALSE if none exists). + $currentDocComment = $this->_reflector->getDocComment(); + if (is_string($currentDocComment)) { + $currentDocComment = trim($currentDocComment); + } + + // Extract all relevant lines from the current comment. + $finalDocLines = $this->_extractRelevantLines($currentDocComment); + + // Generate the automatic summary line (classname followed by period). + $autoSummaryLine = $this->_reflector->getShortName().'.'; + + // If the 1st line is a classname followed by a period, update the name. + // NOTE: This ensures that we update all outdated auto-added classnames, + // and the risk of false positives is very low since we only document + // `LazyJsonMapper`-based classes with a `OneWord.`-style summary line. + // NOTE: Regex is from http://php.net/manual/en/language.oop5.basic.php, + // and yes we must run it in NON-UNICODE MODE, so that it parses on a + // byte by byte basis exactly like the real PHP classname interpreter. + if ( + isset($finalDocLines[0]) // The 1st line MUST exist to proceed. + && preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*\.$/', $finalDocLines[0]) + ) { + $finalDocLines[0] = $autoSummaryLine; + } + + // Generate the magic documentation lines for the current class. + $magicDocLines = $this->_generateMagicDocs(); + if (!empty($magicDocLines)) { + // If there are no lines already... add the automatic summary line. + if (empty($finalDocLines)) { + $finalDocLines[] = $autoSummaryLine; + } + + // Check the 1st char of the 1st line. If it's an @tag of any kind, + // insert automatic summary line at top and empty line after that. + elseif ($finalDocLines[0][0] === '@') { + array_unshift( + $finalDocLines, + $autoSummaryLine, + '' + ); + } + + $finalDocLines[] = ''; // Add empty line before our magic docs. + $finalDocLines = array_merge($finalDocLines, array_values($magicDocLines)); + } + unset($magicDocLines); + + // Generate the final doc-comment that this class is supposed to have. + if (!empty($finalDocLines)) { + // This will generate even if the class only contained an existing + // summary/tags and nothing was added by our magic handler. + foreach ($finalDocLines as &$line) { + $line = ($line === '' ? ' *' : " * {$line}"); + } + unset($line); + $finalDocComment = sprintf( + '/**%s%s%s */', + $this->_nl, + implode($this->_nl, $finalDocLines), + $this->_nl + ); + } else { + // The FALSE signifies that we want no class doc-block at all... + $finalDocComment = false; + } + unset($finalDocLines); + + // There's nothing to do if the doc-comment is already correct. + // NOTE: Both values are FALSE if no doc-comment exists and none wanted. + if ($currentDocComment === $finalDocComment) { + return true; + } + + // The docs mismatch. If this is a validate-run, just return false now. + if ($this->_options['validate-only']) { + fwrite(STDERR, '> Outdated class docs encountered in "'.$thisClassName.'". Aborting scan...'.PHP_EOL); + + return false; + } + + // Load the contents of the file... + $classFileName = $this->_reflector->getFileName(); + $fileLines = @file($classFileName); + if ($fileLines === false) { + fwrite(STDERR, '> Unable to read class file from disk: "'.$classFileName.'".'.PHP_EOL); + + return false; + } + + // Split the file into lines BEFORE the class and lines AFTER the class. + $classLine = $this->_reflector->getStartLine(); + $startLines = array_slice($fileLines, 0, $classLine - 1); + $endLines = array_slice($fileLines, $classLine - 1); + unset($fileLines); + + // Insert the new class documentation using a very careful algorithm. + if ($currentDocComment !== false) { + // Since the class already had PHPdoc, remove it and insert new doc. + // NOTE: A valid PHPdoc (getDocComment()) always starts with + // "/**[whitespace]". If it's just a "/*" or something like + // "/**Foo", then it's not detected by getDocComment(). However, the + // comment may be several lines above the class. So we'll have to do + // an intelligent search to find the old class-comment. As for the + // ending tag "*/", PHP doesn't care about whitespace around that. + // And it also doesn't let the user escape the "*/", which means + // that if we see that sequence we KNOW it's the end of a comment! + // NOTE: We'll search for the latest "/**[whitespace]" block and + // remove all lines from that until its closest "*/". + $deleteFrom = null; + $deleteTo = null; + for ($i = count($startLines) - 1; $i >= 0; --$i) { + if (strpos($startLines[$i], '*/') !== false) { + $deleteTo = $i; + } + if (preg_match('/^\s*\/\*\*\s/u', $startLines[$i])) { + $deleteFrom = $i; + break; + } + } + + // Ensure that we have found valid comment-offsets. + if ($deleteFrom === null || $deleteTo === null || $deleteTo < $deleteFrom) { + fwrite(STDERR, '> Unable to parse current class comment on disk: "'.$classFileName.'".'.PHP_EOL); + + return false; + } + + // Now update the startLines array to replace the doc-comment... + foreach ($startLines as $k => $v) { + if ($k === $deleteFrom && $finalDocComment !== false) { + // We've found the first line of the old comment, and we + // have a new comment. So replace that array entry. + $startLines[$k] = $finalDocComment.$this->_nl; + } elseif ($k >= $deleteFrom && $k <= $deleteTo) { + // Delete all other comment lines, including the first line + // if we had no new doc-comment. + unset($startLines[$k]); + } + + // Break if we've reached the final line to delete. + if ($k >= $deleteTo) { + break; + } + } + } elseif ($finalDocComment !== false) { + // There's no existing doc-comment. Just add ours above the class. + // NOTE: This only does something if we had a new comment to insert, + // which we SHOULD have since we came this far in this scenario... + $startLines[] = $finalDocComment.$this->_nl; + } + + // Generate the new file contents. + $newFileContent = implode($startLines).implode($endLines); + unset($startLines); + unset($endLines); + + // Perform an atomic file-write to disk, which ensures that we will + // never be able to corrupt the class-files on disk via partial writes. + $written = Utilities::atomicWrite($classFileName, $newFileContent); + if ($written !== false) { + echo '> Wrote updated class documentation to disk: "'.$classFileName.'".'.PHP_EOL; + + return true; + } else { + fwrite(STDERR, '> Unable to write new class documentation to disk: "'.$classFileName.'".'.PHP_EOL); + + return false; + } + } + + /** + * Extracts all relevant lines from a doc-comment. + * + * @param string $currentDocComment + * + * @return array + */ + private function _extractRelevantLines( + $currentDocComment) + { + if (!is_string($currentDocComment)) { + return []; + } + + // Remove the leading and trailing doc-comment tags (/** and */). + $currentDocComment = preg_replace('/(^\s*\/\*\*\s*|\s*\*\/$)/u', '', $currentDocComment); + + // Process all lines. Skip all @method and @property lines. + $relevantLines = []; + $lines = preg_split('/\r?\n|\r/u', $currentDocComment); + foreach ($lines as $line) { + // Remove leading and trailing whitespace, and leading asterisks. + $line = trim(preg_replace('/^\s*\*+/u', '', $line)); + + // Skip this line if it's a @method or @property line. + // NOTE: Removing them is totally safe, because the LazyJsonMapper + // class has marked all of its magic property/function handlers as + // final, which means that people's subclasses CANNOT override them + // to add their own magic methods/properties. So therefore we KNOW + // that ALL existing @method/@property class doc lines belong to us! + if (preg_match('/^@(?:method|property)/u', $line)) { + continue; + } + + $relevantLines[] = $line; + } + + // Remove trailing empty lines from the relevant lines. + for ($i = count($relevantLines) - 1; $i >= 0; --$i) { + if ($relevantLines[$i] === '') { + unset($relevantLines[$i]); + } else { + break; + } + } + + // Remove leading empty lines from the relevant lines. + foreach ($relevantLines as $k => $v) { + if ($v !== '') { + break; + } + + unset($relevantLines[$k]); + } + + // Return a re-indexed (properly 0-indexed) array. + return array_values($relevantLines); + } + + /** + * Generate PHPdoc lines for all magic properties and functions. + * + * @throws ReflectionException + * @throws LazyJsonMapperException + * + * @return array + */ + private function _generateMagicDocs() + { + // Check whether we should (and can) document properties and functions. + $documentProperties = $this->_options['properties'] && $this->_reflector->getConstant('ALLOW_VIRTUAL_PROPERTIES'); + $documentFunctions = $this->_options['functions'] && $this->_reflector->getConstant('ALLOW_VIRTUAL_FUNCTIONS'); + if (!$documentProperties && !$documentFunctions) { + return []; + } + + // Export all JSON properties, with RELATIVE class-paths when possible. + // NOTE: We will document ALL properties. Even ones inherited from + // parents/imported maps. This ensures that users who are manually + // reading the source code can see EVERYTHING without needing an IDE. + $properties = []; + $ownerClassName = $this->_reflector->getName(); + foreach ($this->_compiledPropertyMapLink as $propName => $propDef) { + $properties[$propName] = new PropertyDescription( // Throws. + $ownerClassName, + $propName, + $propDef, + true // Use relative class-paths when possible. + ); + } + + // Build the magic documentation... + $magicDocLines = []; + foreach (['functions', 'properties'] as $docType) { + if (($docType === 'functions' && !$documentFunctions) + || ($docType === 'properties' && !$documentProperties)) { + continue; + } + + // Generate all lines for the current magic tag type... + $lineStorage = []; + foreach ($properties as $property) { + if ($docType === 'functions') { + // We will only document useful functions (not the "has", + // since those are useless for properties that are fully + // defined in the class map). + foreach (['get', 'set', 'is', 'unset'] as $funcType) { + // Generate the function name, ie "getSomething", and + // skip this function if it's already defined as a REAL + // (overridden) function in this class or its parents. + $functionName = $funcType.$property->func_case; + if (!$this->_options['document-overridden'] && $this->_reflector->hasMethod($functionName)) { + continue; + } + + // Alright, the function doesn't exist as a real class + // function, or the user wants to document it anyway... + // Document it via its calculated signature. + // NOTE: Classtypes use paths relative to current class! + $functionSignature = $property->{'function_'.$funcType}; + $lineStorage[$functionName] = sprintf('@method %s', $functionSignature); + } + } elseif ($docType === 'properties') { + // Skip this property if it's already defined as a REAL + // (overridden) property in this class or its parents. + if (!$this->_options['document-overridden'] && $this->_reflector->hasProperty($property->name)) { + continue; + } + + // Alright, the property doesn't exist as a real class + // property, or the user wants to document it anyway... + // Document it via its calculated signature. + // NOTE: Classtypes use paths relative to current class! + $lineStorage[$property->name] = sprintf( + '@property %s $%s', + $property->type, + $property->name + ); + } + } + + // Skip this tag type if there was nothing to document... + if (empty($lineStorage)) { + continue; + } + + // Insert empty line separators between different magic tag types. + if (!empty($magicDocLines)) { + $magicDocLines[] = ''; + } + + // Reorder lines by name and add them to the magic doc lines. + // NOTE: We use case sensitivity so that "getComments" and + // "getCommentThreads" etc aren't placed next to each other. + ksort($lineStorage, SORT_NATURAL); // Case-sensitive natural order. + $magicDocLines = array_merge($magicDocLines, array_values($lineStorage)); + } + + return $magicDocLines; + } +} + +// Now process all PHP files under all of the project's namespace folders. +foreach ($namespaces as $realpath => $namespace) { + echo PHP_EOL.'Processing namespace "'.$namespace.'".'.PHP_EOL.'- Path: "'.$realpath.'".'.PHP_EOL; + $realpathlen = strlen($realpath); + + $iterator = new RegexIterator( + new RecursiveIteratorIterator(new RecursiveDirectoryIterator($realpath)), + '/\.php$/i', RecursiveRegexIterator::GET_MATCH + ); + foreach ($iterator as $file => $ext) { + // Determine the real path to the file (compatible with $realpath). + $realfile = realpath($file); + if ($realfile === false) { + fwrite(STDERR, 'Unable to determine real path to file "'.$file.'".'.PHP_EOL); + exit(1); + } + + // Now ensure that the file starts with the expected path... + if (strncmp($realpath, $realfile, $realpathlen) !== 0) { + fwrite(STDERR, 'Unexpected path to file "'.$realfile.'". Does not match project path.'.PHP_EOL); + exit(1); + } + $class = substr($realfile, $realpathlen); + + // Remove the leading slash for the folder... + if ($class[0] !== '/' && $class[0] !== '\\') { + fwrite(STDERR, 'Unexpected path to file "'.$realfile.'". Does not match project path.'.PHP_EOL); + exit(1); + } + $class = substr($class, 1); + + // And now just generate the final class name... + $class = sprintf( + '%s%s', + $namespace, + str_replace('/', '\\', preg_replace('/\.php$/ui', '', $class)) + ); + + // Some files may not contain classes. For example, some people have + // functions.php files with functions, etc. So before we proceed, just + // ensure that the generated class name actually exists. + // NOTE: class_exists() ignores interfaces. Only finds classes. Good. + if (!class_exists($class, true)) { // TRUE = Autoload. + continue; + } + + // Now process the current class. + $documentor = new LazyClassDocumentor($class, $options); + $result = $documentor->process(); + if (!$result) { + if ($options['validate-only']) { + fwrite(STDERR, '> One or more files need updated class documentation or contain other errors.'.PHP_EOL); + } else { + fwrite(STDERR, '> Error while processing class "'.$class.'". Aborting...'.PHP_EOL); + } + exit(1); + } + } +} diff --git a/instafeed/vendor/binsoul/net-mqtt-client-react/LICENSE.md b/instafeed/vendor/binsoul/net-mqtt-client-react/LICENSE.md new file mode 100755 index 0000000..6d19c5c --- /dev/null +++ b/instafeed/vendor/binsoul/net-mqtt-client-react/LICENSE.md @@ -0,0 +1,21 @@ +# The MIT License (MIT) + +Copyright (c) 2015 Sebastian Mößler + +> 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/instafeed/vendor/binsoul/net-mqtt-client-react/README.md b/instafeed/vendor/binsoul/net-mqtt-client-react/README.md new file mode 100755 index 0000000..ca3099d --- /dev/null +++ b/instafeed/vendor/binsoul/net-mqtt-client-react/README.md @@ -0,0 +1,145 @@ +# net-mqtt-client-react + +[![Latest Version on Packagist][ico-version]][link-packagist] +[![Software License][ico-license]](LICENSE.md) +[![Total Downloads][ico-downloads]][link-downloads] + +This package provides an asynchronous MQTT client built on the [React socket](https://github.com/reactphp/socket) library. All client methods return a promise which is fulfilled if the operation succeeded or rejected if the operation failed. Incoming messages of subscribed topics are delivered via the "message" event. + +## Install + +Via composer: + +``` bash +$ composer require binsoul/net-mqtt-client-react +``` + +## Example + +Connect to a public broker and run forever. + +``` php +createCached('8.8.8.8', $loop)); +$client = new ReactMqttClient($connector, $loop); + +// Bind to events +$client->on('open', function () use ($client) { + // Network connection established + echo sprintf("Open: %s:%s\n", $client->getHost(), $client->getPort()); +}); + +$client->on('close', function () use ($client, $loop) { + // Network connection closed + echo sprintf("Close: %s:%s\n", $client->getHost(), $client->getPort()); + + $loop->stop(); +}); + +$client->on('connect', function (Connection $connection) { + // Broker connected + echo sprintf("Connect: client=%s\n", $connection->getClientID()); +}); + +$client->on('disconnect', function (Connection $connection) { + // Broker disconnected + echo sprintf("Disconnect: client=%s\n", $connection->getClientID()); +}); + +$client->on('message', function (Message $message) { + // Incoming message + echo 'Message'; + + if ($message->isDuplicate()) { + echo ' (duplicate)'; + } + + if ($message->isRetained()) { + echo ' (retained)'; + } + + echo ': '.$message->getTopic().' => '.mb_strimwidth($message->getPayload(), 0, 50, '...'); + echo "\n"; +}); + +$client->on('warning', function (\Exception $e) { + echo sprintf("Warning: %s\n", $e->getMessage()); +}); + +$client->on('error', function (\Exception $e) use ($loop) { + echo sprintf("Error: %s\n", $e->getMessage()); + + $loop->stop(); +}); + +// Connect to broker +$client->connect('test.mosquitto.org')->then( + function () use ($client) { + // Subscribe to all topics + $client->subscribe(new DefaultSubscription('#')) + ->then(function (Subscription $subscription) { + echo sprintf("Subscribe: %s\n", $subscription->getFilter()); + }) + ->otherwise(function (\Exception $e) { + echo sprintf("Error: %s\n", $e->getMessage()); + }); + + // Publish humidity once + $client->publish(new DefaultMessage('sensors/humidity', '55%')) + ->then(function (Message $message) { + echo sprintf("Publish: %s => %s\n", $message->getTopic(), $message->getPayload()); + }) + ->otherwise(function (\Exception $e) { + echo sprintf("Error: %s\n", $e->getMessage()); + }); + + // Publish a random temperature every 10 seconds + $generator = function () { + return mt_rand(-20, 30); + }; + + $client->publishPeriodically(10, new DefaultMessage('sensors/temperature'), $generator) + ->progress(function (Message $message) { + echo sprintf("Publish: %s => %s\n", $message->getTopic(), $message->getPayload()); + }) + ->otherwise(function (\Exception $e) { + echo sprintf("Error: %s\n", $e->getMessage()); + }); + } +); + +$loop->run(); +``` + +## Testing + +``` bash +$ composer test +``` + +## License + +The MIT License (MIT). Please see [License File](LICENSE.md) for more information. + +[ico-version]: https://img.shields.io/packagist/v/binsoul/net-mqtt-client-react.svg?style=flat-square +[ico-license]: https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square +[ico-downloads]: https://img.shields.io/packagist/dt/binsoul/net-mqtt-client-react.svg?style=flat-square + +[link-packagist]: https://packagist.org/packages/binsoul/net-mqtt-client-react +[link-downloads]: https://packagist.org/packages/binsoul/net-mqtt-client-react +[link-author]: https://github.com/binsoul diff --git a/instafeed/vendor/binsoul/net-mqtt-client-react/composer.json b/instafeed/vendor/binsoul/net-mqtt-client-react/composer.json new file mode 100755 index 0000000..e9dcc25 --- /dev/null +++ b/instafeed/vendor/binsoul/net-mqtt-client-react/composer.json @@ -0,0 +1,51 @@ +{ + "name": "binsoul/net-mqtt-client-react", + "description": "Asynchronous MQTT client built on React", + "keywords": [ + "net", + "mqtt", + "client" + ], + "homepage": "https://github.com/binsoul/net-mqtt-client-react", + "license": "MIT", + "authors": [ + { + "name": "Sebastian Mößler", + "email": "code@binsoul.de", + "homepage": "https://github.com/binsoul", + "role": "Developer" + } + ], + "require": { + "php": "~5.6|~7.0", + "binsoul/net-mqtt": "~0.2", + "react/promise": "~2.0", + "react/socket": "~0.8" + }, + "require-dev": { + "phpunit/phpunit": "~4.0||~5.0", + "friendsofphp/php-cs-fixer": "~1.0" + }, + "autoload": { + "psr-4": { + "BinSoul\\Net\\Mqtt\\Client\\React\\": "src" + } + }, + "autoload-dev": { + "psr-4": { + "BinSoul\\Test\\Net\\Mqtt\\Client\\React\\": "tests" + } + }, + "scripts": { + "test": "phpunit", + "fix-style": [ + "php-cs-fixer fix src", + "php-cs-fixer fix tests" + ] + }, + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + } +} diff --git a/instafeed/vendor/binsoul/net-mqtt-client-react/src/ReactFlow.php b/instafeed/vendor/binsoul/net-mqtt-client-react/src/ReactFlow.php new file mode 100755 index 0000000..d404145 --- /dev/null +++ b/instafeed/vendor/binsoul/net-mqtt-client-react/src/ReactFlow.php @@ -0,0 +1,112 @@ +decorated = $decorated; + $this->deferred = $deferred; + $this->packet = $packet; + $this->isSilent = $isSilent; + } + + public function getCode() + { + return $this->decorated->getCode(); + } + + public function start() + { + $this->packet = $this->decorated->start(); + + return $this->packet; + } + + public function accept(Packet $packet) + { + return $this->decorated->accept($packet); + } + + public function next(Packet $packet) + { + $this->packet = $this->decorated->next($packet); + + return $this->packet; + } + + public function isFinished() + { + return $this->decorated->isFinished(); + } + + public function isSuccess() + { + return $this->decorated->isSuccess(); + } + + public function getResult() + { + return $this->decorated->getResult(); + } + + public function getErrorMessage() + { + return $this->decorated->getErrorMessage(); + } + + /** + * Returns the associated deferred. + * + * @return Deferred + */ + public function getDeferred() + { + return $this->deferred; + } + + /** + * Returns the current packet. + * + * @return Packet + */ + public function getPacket() + { + return $this->packet; + } + + /** + * Indicates if the flow should emit events. + * + * @return bool + */ + public function isSilent() + { + return $this->isSilent; + } +} diff --git a/instafeed/vendor/binsoul/net-mqtt-client-react/src/ReactMqttClient.php b/instafeed/vendor/binsoul/net-mqtt-client-react/src/ReactMqttClient.php new file mode 100755 index 0000000..f56b191 --- /dev/null +++ b/instafeed/vendor/binsoul/net-mqtt-client-react/src/ReactMqttClient.php @@ -0,0 +1,701 @@ +connector = $connector; + $this->loop = $loop; + + $this->parser = $parser; + if ($this->parser === null) { + $this->parser = new StreamParser(); + } + + $this->parser->onError(function (\Exception $e) { + $this->emitWarning($e); + }); + + $this->identifierGenerator = $identifierGenerator; + if ($this->identifierGenerator === null) { + $this->identifierGenerator = new DefaultIdentifierGenerator(); + } + } + + /** + * Return the host. + * + * @return string + */ + public function getHost() + { + return $this->host; + } + + /** + * Return the port. + * + * @return string + */ + public function getPort() + { + return $this->port; + } + + /** + * Indicates if the client is connected. + * + * @return bool + */ + public function isConnected() + { + return $this->isConnected; + } + + /** + * Returns the underlying stream or null if the client is not connected. + * + * @return DuplexStreamInterface|null + */ + public function getStream() + { + return $this->stream; + } + + /** + * Connects to a broker. + * + * @param string $host + * @param int $port + * @param Connection $connection + * @param int $timeout + * + * @return ExtendedPromiseInterface + */ + public function connect($host, $port = 1883, Connection $connection = null, $timeout = 5) + { + if ($this->isConnected || $this->isConnecting) { + return new RejectedPromise(new \LogicException('The client is already connected.')); + } + + $this->isConnecting = true; + $this->isConnected = false; + + $this->host = $host; + $this->port = $port; + + if ($connection === null) { + $connection = new DefaultConnection(); + } + + if ($connection->isCleanSession()) { + $this->cleanPreviousSession(); + } + + if ($connection->getClientID() === '') { + $connection = $connection->withClientID($this->identifierGenerator->generateClientID()); + } + + $deferred = new Deferred(); + + $this->establishConnection($this->host, $this->port, $timeout) + ->then(function (DuplexStreamInterface $stream) use ($connection, $deferred, $timeout) { + $this->stream = $stream; + + $this->emit('open', [$connection, $this]); + + $this->registerClient($connection, $timeout) + ->then(function (Connection $connection) use ($deferred) { + $this->isConnecting = false; + $this->isConnected = true; + $this->connection = $connection; + + $this->emit('connect', [$connection, $this]); + $deferred->resolve($this->connection); + }) + ->otherwise(function (\Exception $e) use ($deferred, $connection) { + $this->isConnecting = false; + + $this->emitError($e); + $deferred->reject($e); + + if ($this->stream !== null) { + $this->stream->close(); + } + + $this->emit('close', [$connection, $this]); + }); + }) + ->otherwise(function (\Exception $e) use ($deferred) { + $this->isConnecting = false; + + $this->emitError($e); + $deferred->reject($e); + }); + + return $deferred->promise(); + } + + /** + * Disconnects from a broker. + * + * @return ExtendedPromiseInterface + */ + public function disconnect() + { + if (!$this->isConnected || $this->isDisconnecting) { + return new RejectedPromise(new \LogicException('The client is not connected.')); + } + + $this->isDisconnecting = true; + + $deferred = new Deferred(); + + $this->startFlow(new OutgoingDisconnectFlow($this->connection), true) + ->then(function (Connection $connection) use ($deferred) { + $this->isDisconnecting = false; + $this->isConnected = false; + + $this->emit('disconnect', [$connection, $this]); + $deferred->resolve($connection); + + if ($this->stream !== null) { + $this->stream->close(); + } + }) + ->otherwise(function () use ($deferred) { + $this->isDisconnecting = false; + $deferred->reject($this->connection); + }); + + return $deferred->promise(); + } + + /** + * Subscribes to a topic filter. + * + * @param Subscription $subscription + * + * @return ExtendedPromiseInterface + */ + public function subscribe(Subscription $subscription) + { + if (!$this->isConnected) { + return new RejectedPromise(new \LogicException('The client is not connected.')); + } + + return $this->startFlow(new OutgoingSubscribeFlow([$subscription], $this->identifierGenerator)); + } + + /** + * Unsubscribes from a topic filter. + * + * @param Subscription $subscription + * + * @return ExtendedPromiseInterface + */ + public function unsubscribe(Subscription $subscription) + { + if (!$this->isConnected) { + return new RejectedPromise(new \LogicException('The client is not connected.')); + } + + return $this->startFlow(new OutgoingUnsubscribeFlow([$subscription], $this->identifierGenerator)); + } + + /** + * Publishes a message. + * + * @param Message $message + * + * @return ExtendedPromiseInterface + */ + public function publish(Message $message) + { + if (!$this->isConnected) { + return new RejectedPromise(new \LogicException('The client is not connected.')); + } + + return $this->startFlow(new OutgoingPublishFlow($message, $this->identifierGenerator)); + } + + /** + * Calls the given generator periodically and publishes the return value. + * + * @param int $interval + * @param Message $message + * @param callable $generator + * + * @return ExtendedPromiseInterface + */ + public function publishPeriodically($interval, Message $message, callable $generator) + { + if (!$this->isConnected) { + return new RejectedPromise(new \LogicException('The client is not connected.')); + } + + $deferred = new Deferred(); + + $this->timer[] = $this->loop->addPeriodicTimer( + $interval, + function () use ($message, $generator, $deferred) { + $this->publish($message->withPayload($generator($message->getTopic())))->then( + function ($value) use ($deferred) { + $deferred->notify($value); + }, + function (\Exception $e) use ($deferred) { + $deferred->reject($e); + } + ); + } + ); + + return $deferred->promise(); + } + + /** + * Emits warnings. + * + * @param \Exception $e + */ + private function emitWarning(\Exception $e) + { + $this->emit('warning', [$e, $this]); + } + + /** + * Emits errors. + * + * @param \Exception $e + */ + private function emitError(\Exception $e) + { + $this->emit('error', [$e, $this]); + } + + /** + * Establishes a network connection to a server. + * + * @param string $host + * @param int $port + * @param int $timeout + * + * @return ExtendedPromiseInterface + */ + private function establishConnection($host, $port, $timeout) + { + $deferred = new Deferred(); + + $timer = $this->loop->addTimer( + $timeout, + function () use ($deferred, $timeout) { + $exception = new \RuntimeException(sprintf('Connection timed out after %d seconds.', $timeout)); + $deferred->reject($exception); + } + ); + + $this->connector->connect($host.':'.$port) + ->always(function () use ($timer) { + $this->loop->cancelTimer($timer); + }) + ->then(function (DuplexStreamInterface $stream) use ($deferred) { + $stream->on('data', function ($data) { + $this->handleReceive($data); + }); + + $stream->on('close', function () { + $this->handleClose(); + }); + + $stream->on('error', function (\Exception $e) { + $this->handleError($e); + }); + + $deferred->resolve($stream); + }) + ->otherwise(function (\Exception $e) use ($deferred) { + $deferred->reject($e); + }); + + return $deferred->promise(); + } + + /** + * Registers a new client with the broker. + * + * @param Connection $connection + * @param int $timeout + * + * @return ExtendedPromiseInterface + */ + private function registerClient(Connection $connection, $timeout) + { + $deferred = new Deferred(); + + $responseTimer = $this->loop->addTimer( + $timeout, + function () use ($deferred, $timeout) { + $exception = new \RuntimeException(sprintf('No response after %d seconds.', $timeout)); + $deferred->reject($exception); + } + ); + + $this->startFlow(new OutgoingConnectFlow($connection, $this->identifierGenerator), true) + ->always(function () use ($responseTimer) { + $this->loop->cancelTimer($responseTimer); + })->then(function (Connection $connection) use ($deferred) { + $this->timer[] = $this->loop->addPeriodicTimer( + floor($connection->getKeepAlive() * 0.75), + function () { + $this->startFlow(new OutgoingPingFlow()); + } + ); + + $deferred->resolve($connection); + })->otherwise(function (\Exception $e) use ($deferred) { + $deferred->reject($e); + }); + + return $deferred->promise(); + } + + /** + * Handles incoming data. + * + * @param string $data + */ + private function handleReceive($data) + { + if (!$this->isConnected && !$this->isConnecting) { + return; + } + + $flowCount = count($this->receivingFlows); + + $packets = $this->parser->push($data); + foreach ($packets as $packet) { + $this->handlePacket($packet); + } + + if ($flowCount > count($this->receivingFlows)) { + $this->receivingFlows = array_values($this->receivingFlows); + } + + $this->handleSend(); + } + + /** + * Handles an incoming packet. + * + * @param Packet $packet + */ + private function handlePacket(Packet $packet) + { + switch ($packet->getPacketType()) { + case Packet::TYPE_PUBLISH: + /* @var PublishRequestPacket $packet */ + $message = new DefaultMessage( + $packet->getTopic(), + $packet->getPayload(), + $packet->getQosLevel(), + $packet->isRetained(), + $packet->isDuplicate() + ); + + $this->startFlow(new IncomingPublishFlow($message, $packet->getIdentifier())); + break; + case Packet::TYPE_CONNACK: + case Packet::TYPE_PINGRESP: + case Packet::TYPE_SUBACK: + case Packet::TYPE_UNSUBACK: + case Packet::TYPE_PUBREL: + case Packet::TYPE_PUBACK: + case Packet::TYPE_PUBREC: + case Packet::TYPE_PUBCOMP: + $flowFound = false; + foreach ($this->receivingFlows as $index => $flow) { + if ($flow->accept($packet)) { + $flowFound = true; + + unset($this->receivingFlows[$index]); + $this->continueFlow($flow, $packet); + + break; + } + } + + if (!$flowFound) { + $this->emitWarning( + new \LogicException(sprintf('Received unexpected packet of type %d.', $packet->getPacketType())) + ); + } + break; + default: + $this->emitWarning( + new \LogicException(sprintf('Cannot handle packet of type %d.', $packet->getPacketType())) + ); + } + } + + /** + * Handles outgoing packets. + */ + private function handleSend() + { + $flow = null; + if ($this->writtenFlow !== null) { + $flow = $this->writtenFlow; + $this->writtenFlow = null; + } + + if (count($this->sendingFlows) > 0) { + $this->writtenFlow = array_shift($this->sendingFlows); + $this->stream->write($this->writtenFlow->getPacket()); + } + + if ($flow !== null) { + if ($flow->isFinished()) { + $this->loop->nextTick(function () use ($flow) { + $this->finishFlow($flow); + }); + } else { + $this->receivingFlows[] = $flow; + } + } + } + + /** + * Handles closing of the stream. + */ + private function handleClose() + { + foreach ($this->timer as $timer) { + $this->loop->cancelTimer($timer); + } + + $this->timer = []; + + $connection = $this->connection; + + $this->isConnecting = false; + $this->isDisconnecting = false; + $this->isConnected = false; + $this->connection = null; + $this->stream = null; + + if ($connection !== null) { + $this->emit('close', [$connection, $this]); + } + } + + /** + * Handles errors of the stream. + * + * @param \Exception $e + */ + private function handleError(\Exception $e) + { + $this->emitError($e); + } + + /** + * Starts the given flow. + * + * @param Flow $flow + * @param bool $isSilent + * + * @return ExtendedPromiseInterface + */ + private function startFlow(Flow $flow, $isSilent = false) + { + try { + $packet = $flow->start(); + } catch (\Exception $e) { + $this->emitError($e); + + return new RejectedPromise($e); + } + + $deferred = new Deferred(); + $internalFlow = new ReactFlow($flow, $deferred, $packet, $isSilent); + + if ($packet !== null) { + if ($this->writtenFlow !== null) { + $this->sendingFlows[] = $internalFlow; + } else { + $this->stream->write($packet); + $this->writtenFlow = $internalFlow; + $this->handleSend(); + } + } else { + $this->loop->nextTick(function () use ($internalFlow) { + $this->finishFlow($internalFlow); + }); + } + + return $deferred->promise(); + } + + /** + * Continues the given flow. + * + * @param ReactFlow $flow + * @param Packet $packet + */ + private function continueFlow(ReactFlow $flow, Packet $packet) + { + try { + $response = $flow->next($packet); + } catch (\Exception $e) { + $this->emitError($e); + + return; + } + + if ($response !== null) { + if ($this->writtenFlow !== null) { + $this->sendingFlows[] = $flow; + } else { + $this->stream->write($response); + $this->writtenFlow = $flow; + $this->handleSend(); + } + } elseif ($flow->isFinished()) { + $this->loop->nextTick(function () use ($flow) { + $this->finishFlow($flow); + }); + } + } + + /** + * Finishes the given flow. + * + * @param ReactFlow $flow + */ + private function finishFlow(ReactFlow $flow) + { + if ($flow->isSuccess()) { + if (!$flow->isSilent()) { + $this->emit($flow->getCode(), [$flow->getResult(), $this]); + } + + $flow->getDeferred()->resolve($flow->getResult()); + } else { + $result = new \RuntimeException($flow->getErrorMessage()); + $this->emitWarning($result); + + $flow->getDeferred()->reject($result); + } + } + + /** + * Cleans previous session by rejecting all pending flows. + */ + private function cleanPreviousSession() + { + $error = new \RuntimeException('Connection has been closed.'); + + foreach ($this->receivingFlows as $receivingFlow) { + $receivingFlow->getDeferred()->reject($error); + } + + foreach ($this->sendingFlows as $sendingFlow) { + $sendingFlow->getDeferred()->reject($error); + } + + $this->receivingFlows = []; + $this->sendingFlows = []; + } +} diff --git a/instafeed/vendor/binsoul/net-mqtt/LICENSE.md b/instafeed/vendor/binsoul/net-mqtt/LICENSE.md new file mode 100755 index 0000000..6d19c5c --- /dev/null +++ b/instafeed/vendor/binsoul/net-mqtt/LICENSE.md @@ -0,0 +1,21 @@ +# The MIT License (MIT) + +Copyright (c) 2015 Sebastian Mößler + +> 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/instafeed/vendor/binsoul/net-mqtt/README.md b/instafeed/vendor/binsoul/net-mqtt/README.md new file mode 100755 index 0000000..9f341da --- /dev/null +++ b/instafeed/vendor/binsoul/net-mqtt/README.md @@ -0,0 +1,36 @@ +# net-mqtt + +[![Latest Version on Packagist][ico-version]][link-packagist] +[![Software License][ico-license]](LICENSE.md) +[![Total Downloads][ico-downloads]][link-downloads] + +MQTT is a machine-to-machine (M2M) / Internet of Things (IoT) connectivity protocol. It provides a lightweight method of carrying out messaging using a publish/subscribe model. + +This package implements the MQTT protocol versions 3.1 and 3.1.1. + + +## Install + +Via composer: + +``` bash +$ composer require binsoul/net-mqtt +``` + +## Testing + +``` bash +$ composer test +``` + +## License + +The MIT License (MIT). Please see [License File](LICENSE.md) for more information. + +[ico-version]: https://img.shields.io/packagist/v/binsoul/net-mqtt.svg?style=flat-square +[ico-license]: https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square +[ico-downloads]: https://img.shields.io/packagist/dt/binsoul/net-mqtt.svg?style=flat-square + +[link-packagist]: https://packagist.org/packages/binsoul/net-mqtt +[link-downloads]: https://packagist.org/packages/binsoul/net-mqtt +[link-author]: https://github.com/binsoul diff --git a/instafeed/vendor/binsoul/net-mqtt/composer.json b/instafeed/vendor/binsoul/net-mqtt/composer.json new file mode 100755 index 0000000..035ad9f --- /dev/null +++ b/instafeed/vendor/binsoul/net-mqtt/composer.json @@ -0,0 +1,47 @@ +{ + "name": "binsoul/net-mqtt", + "description": "MQTT protocol implementation", + "keywords": [ + "net", + "mqtt" + ], + "homepage": "https://github.com/binsoul/net-mqtt", + "license": "MIT", + "authors": [ + { + "name": "Sebastian Mößler", + "email": "code@binsoul.de", + "homepage": "https://github.com/binsoul", + "role": "Developer" + } + ], + "require": { + "php": "~5.6|~7.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0||~5.0", + "friendsofphp/php-cs-fixer": "~1.0" + }, + "autoload": { + "psr-4": { + "BinSoul\\Net\\Mqtt\\": "src" + } + }, + "autoload-dev": { + "psr-4": { + "BinSoul\\Test\\Net\\Mqtt\\": "tests" + } + }, + "scripts": { + "test": "phpunit", + "fix-style": [ + "php-cs-fixer fix src", + "php-cs-fixer fix tests" + ] + }, + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + } +} diff --git a/instafeed/vendor/binsoul/net-mqtt/src/Connection.php b/instafeed/vendor/binsoul/net-mqtt/src/Connection.php new file mode 100755 index 0000000..5297b74 --- /dev/null +++ b/instafeed/vendor/binsoul/net-mqtt/src/Connection.php @@ -0,0 +1,90 @@ +username = $username; + $this->password = $password; + $this->will = $will; + $this->clientID = $clientID; + $this->keepAlive = $keepAlive; + $this->protocol = $protocol; + $this->clean = $clean; + } + + public function getProtocol() + { + return $this->protocol; + } + + public function getClientID() + { + return $this->clientID; + } + + public function isCleanSession() + { + return $this->clean; + } + + public function getUsername() + { + return $this->username; + } + + public function getPassword() + { + return $this->password; + } + + public function getWill() + { + return $this->will; + } + + public function getKeepAlive() + { + return $this->keepAlive; + } + + public function withProtocol($protocol) + { + $result = clone $this; + $result->protocol = $protocol; + + return $result; + } + + public function withClientID($clientID) + { + $result = clone $this; + $result->clientID = $clientID; + + return $result; + } + + public function withCredentials($username, $password) + { + $result = clone $this; + $result->username = $username; + $result->password = $password; + + return $result; + } + + public function withWill(Message $will = null) + { + $result = clone $this; + $result->will = $will; + + return $result; + } + + public function withKeepAlive($timeout) + { + $result = clone $this; + $result->keepAlive = $timeout; + + return $result; + } +} diff --git a/instafeed/vendor/binsoul/net-mqtt/src/DefaultIdentifierGenerator.php b/instafeed/vendor/binsoul/net-mqtt/src/DefaultIdentifierGenerator.php new file mode 100755 index 0000000..4c21211 --- /dev/null +++ b/instafeed/vendor/binsoul/net-mqtt/src/DefaultIdentifierGenerator.php @@ -0,0 +1,38 @@ +currentIdentifier; + if ($this->currentIdentifier > 0xFFFF) { + $this->currentIdentifier = 1; + } + + return $this->currentIdentifier; + } + + public function generateClientID() + { + if (function_exists('random_bytes')) { + $data = random_bytes(9); + } elseif (function_exists('openssl_random_pseudo_bytes')) { + $data = openssl_random_pseudo_bytes(9); + } else { + $data = ''; + for ($i = 1; $i <= 8; ++$i) { + $data = chr(mt_rand(0, 255)).$data; + } + } + + return 'BNMCR'.bin2hex($data); + } +} diff --git a/instafeed/vendor/binsoul/net-mqtt/src/DefaultMessage.php b/instafeed/vendor/binsoul/net-mqtt/src/DefaultMessage.php new file mode 100755 index 0000000..bc659e9 --- /dev/null +++ b/instafeed/vendor/binsoul/net-mqtt/src/DefaultMessage.php @@ -0,0 +1,119 @@ +topic = $topic; + $this->payload = $payload; + $this->isRetained = $retain; + $this->qosLevel = $qosLevel; + $this->isDuplicate = $isDuplicate; + } + + public function getTopic() + { + return $this->topic; + } + + public function getPayload() + { + return $this->payload; + } + + public function getQosLevel() + { + return $this->qosLevel; + } + + public function isDuplicate() + { + return $this->isDuplicate; + } + + public function isRetained() + { + return $this->isRetained; + } + + public function withTopic($topic) + { + $result = clone $this; + $result->topic = $topic; + + return $result; + } + + public function withPayload($payload) + { + $result = clone $this; + $result->payload = $payload; + + return $result; + } + + public function withQosLevel($level) + { + $result = clone $this; + $result->qosLevel = $level; + + return $result; + } + + public function retain() + { + $result = clone $this; + $result->isRetained = true; + + return $result; + } + + public function release() + { + $result = clone $this; + $result->isRetained = false; + + return $result; + } + + public function duplicate() + { + $result = clone $this; + $result->isDuplicate = true; + + return $result; + } + + public function original() + { + $result = clone $this; + $result->isDuplicate = false; + + return $result; + } +} diff --git a/instafeed/vendor/binsoul/net-mqtt/src/DefaultSubscription.php b/instafeed/vendor/binsoul/net-mqtt/src/DefaultSubscription.php new file mode 100755 index 0000000..2fa71ab --- /dev/null +++ b/instafeed/vendor/binsoul/net-mqtt/src/DefaultSubscription.php @@ -0,0 +1,52 @@ +filter = $filter; + $this->qosLevel = $qosLevel; + } + + public function getFilter() + { + return $this->filter; + } + + public function getQosLevel() + { + return $this->qosLevel; + } + + public function withFilter($filter) + { + $result = clone $this; + $result->filter = $filter; + + return $result; + } + + public function withQosLevel($level) + { + $result = clone $this; + $result->qosLevel = $level; + + return $result; + } +} diff --git a/instafeed/vendor/binsoul/net-mqtt/src/Exception/EndOfStreamException.php b/instafeed/vendor/binsoul/net-mqtt/src/Exception/EndOfStreamException.php new file mode 100755 index 0000000..3ffc71c --- /dev/null +++ b/instafeed/vendor/binsoul/net-mqtt/src/Exception/EndOfStreamException.php @@ -0,0 +1,10 @@ +isFinished; + } + + public function isSuccess() + { + return $this->isFinished && $this->isSuccess; + } + + public function getResult() + { + return $this->result; + } + + public function getErrorMessage() + { + return $this->error; + } + + /** + * Marks the flow as successful and sets the result. + * + * @param mixed|null $result + */ + protected function succeed($result = null) + { + $this->isFinished = true; + $this->isSuccess = true; + $this->result = $result; + } + + /** + * Marks the flow as failed and sets the error message. + * + * @param string $error + */ + protected function fail($error = '') + { + $this->isFinished = true; + $this->isSuccess = false; + $this->error = $error; + } +} diff --git a/instafeed/vendor/binsoul/net-mqtt/src/Flow/IncomingPingFlow.php b/instafeed/vendor/binsoul/net-mqtt/src/Flow/IncomingPingFlow.php new file mode 100755 index 0000000..a19a1b0 --- /dev/null +++ b/instafeed/vendor/binsoul/net-mqtt/src/Flow/IncomingPingFlow.php @@ -0,0 +1,23 @@ +succeed(); + + return new PingResponsePacket(); + } +} diff --git a/instafeed/vendor/binsoul/net-mqtt/src/Flow/IncomingPublishFlow.php b/instafeed/vendor/binsoul/net-mqtt/src/Flow/IncomingPublishFlow.php new file mode 100755 index 0000000..b992c38 --- /dev/null +++ b/instafeed/vendor/binsoul/net-mqtt/src/Flow/IncomingPublishFlow.php @@ -0,0 +1,80 @@ +message = $message; + $this->identifier = $identifier; + } + + public function getCode() + { + return 'message'; + } + + public function start() + { + $packet = null; + $emit = true; + if ($this->message->getQosLevel() === 1) { + $packet = new PublishAckPacket(); + } elseif ($this->message->getQosLevel() === 2) { + $packet = new PublishReceivedPacket(); + $emit = false; + } + + if ($packet !== null) { + $packet->setIdentifier($this->identifier); + } + + if ($emit) { + $this->succeed($this->message); + } + + return $packet; + } + + public function accept(Packet $packet) + { + if ($this->message->getQosLevel() !== 2 || $packet->getPacketType() !== Packet::TYPE_PUBREL) { + return false; + } + + /* @var PublishReleasePacket $packet */ + return $packet->getIdentifier() === $this->identifier; + } + + public function next(Packet $packet) + { + $this->succeed($this->message); + + $response = new PublishCompletePacket(); + $response->setIdentifier($this->identifier); + + return $response; + } +} diff --git a/instafeed/vendor/binsoul/net-mqtt/src/Flow/OutgoingConnectFlow.php b/instafeed/vendor/binsoul/net-mqtt/src/Flow/OutgoingConnectFlow.php new file mode 100755 index 0000000..f439e79 --- /dev/null +++ b/instafeed/vendor/binsoul/net-mqtt/src/Flow/OutgoingConnectFlow.php @@ -0,0 +1,70 @@ +connection = $connection; + + if ($this->connection->getClientID() === '') { + $this->connection = $this->connection->withClientID($generator->generateClientID()); + } + } + + public function getCode() + { + return 'connect'; + } + + public function start() + { + $packet = new ConnectRequestPacket(); + $packet->setProtocolLevel($this->connection->getProtocol()); + $packet->setKeepAlive($this->connection->getKeepAlive()); + $packet->setClientID($this->connection->getClientID()); + $packet->setCleanSession($this->connection->isCleanSession()); + $packet->setUsername($this->connection->getUsername()); + $packet->setPassword($this->connection->getPassword()); + $will = $this->connection->getWill(); + if ($will !== null && $will->getTopic() !== '' && $will->getPayload() !== '') { + $packet->setWill($will->getTopic(), $will->getPayload(), $will->getQosLevel(), $will->isRetained()); + } + + return $packet; + } + + public function accept(Packet $packet) + { + return $packet->getPacketType() === Packet::TYPE_CONNACK; + } + + public function next(Packet $packet) + { + /** @var ConnectResponsePacket $packet */ + if ($packet->isSuccess()) { + $this->succeed($this->connection); + } else { + $this->fail($packet->getErrorName()); + } + } +} diff --git a/instafeed/vendor/binsoul/net-mqtt/src/Flow/OutgoingDisconnectFlow.php b/instafeed/vendor/binsoul/net-mqtt/src/Flow/OutgoingDisconnectFlow.php new file mode 100755 index 0000000..fe04c33 --- /dev/null +++ b/instafeed/vendor/binsoul/net-mqtt/src/Flow/OutgoingDisconnectFlow.php @@ -0,0 +1,37 @@ +connection = $connection; + } + + public function getCode() + { + return 'disconnect'; + } + + public function start() + { + $this->succeed($this->connection); + + return new DisconnectRequestPacket(); + } +} diff --git a/instafeed/vendor/binsoul/net-mqtt/src/Flow/OutgoingPingFlow.php b/instafeed/vendor/binsoul/net-mqtt/src/Flow/OutgoingPingFlow.php new file mode 100755 index 0000000..8cf79ab --- /dev/null +++ b/instafeed/vendor/binsoul/net-mqtt/src/Flow/OutgoingPingFlow.php @@ -0,0 +1,32 @@ +getPacketType() === Packet::TYPE_PINGRESP; + } + + public function next(Packet $packet) + { + $this->succeed(); + } +} diff --git a/instafeed/vendor/binsoul/net-mqtt/src/Flow/OutgoingPublishFlow.php b/instafeed/vendor/binsoul/net-mqtt/src/Flow/OutgoingPublishFlow.php new file mode 100755 index 0000000..63fe264 --- /dev/null +++ b/instafeed/vendor/binsoul/net-mqtt/src/Flow/OutgoingPublishFlow.php @@ -0,0 +1,102 @@ +message = $message; + if ($this->message->getQosLevel() > 0) { + $this->identifier = $generator->generatePacketID(); + } + } + + public function getCode() + { + return 'publish'; + } + + public function start() + { + $packet = new PublishRequestPacket(); + $packet->setTopic($this->message->getTopic()); + $packet->setPayload($this->message->getPayload()); + $packet->setRetained($this->message->isRetained()); + $packet->setDuplicate($this->message->isDuplicate()); + $packet->setQosLevel($this->message->getQosLevel()); + + if ($this->message->getQosLevel() === 0) { + $this->succeed($this->message); + } else { + $packet->setIdentifier($this->identifier); + } + + return $packet; + } + + public function accept(Packet $packet) + { + if ($this->message->getQosLevel() === 0) { + return false; + } + + $packetType = $packet->getPacketType(); + + if ($packetType === Packet::TYPE_PUBACK && $this->message->getQosLevel() === 1) { + /* @var PublishAckPacket $packet */ + return $packet->getIdentifier() === $this->identifier; + } elseif ($this->message->getQosLevel() === 2) { + if ($packetType === Packet::TYPE_PUBREC) { + /* @var PublishReceivedPacket $packet */ + return $packet->getIdentifier() === $this->identifier; + } elseif ($this->receivedPubRec && $packetType === Packet::TYPE_PUBCOMP) { + /* @var PublishCompletePacket $packet */ + return $packet->getIdentifier() === $this->identifier; + } + } + + return false; + } + + public function next(Packet $packet) + { + $packetType = $packet->getPacketType(); + + if ($packetType === Packet::TYPE_PUBACK || $packetType === Packet::TYPE_PUBCOMP) { + $this->succeed($this->message); + } elseif ($packetType === Packet::TYPE_PUBREC) { + $this->receivedPubRec = true; + + $response = new PublishReleasePacket(); + $response->setIdentifier($this->identifier); + + return $response; + } + } +} diff --git a/instafeed/vendor/binsoul/net-mqtt/src/Flow/OutgoingSubscribeFlow.php b/instafeed/vendor/binsoul/net-mqtt/src/Flow/OutgoingSubscribeFlow.php new file mode 100755 index 0000000..4a25517 --- /dev/null +++ b/instafeed/vendor/binsoul/net-mqtt/src/Flow/OutgoingSubscribeFlow.php @@ -0,0 +1,82 @@ +subscriptions = array_values($subscriptions); + $this->identifier = $generator->generatePacketID(); + } + + public function getCode() + { + return 'subscribe'; + } + + public function start() + { + $packet = new SubscribeRequestPacket(); + $packet->setTopic($this->subscriptions[0]->getFilter()); + $packet->setQosLevel($this->subscriptions[0]->getQosLevel()); + $packet->setIdentifier($this->identifier); + + return $packet; + } + + public function accept(Packet $packet) + { + if ($packet->getPacketType() !== Packet::TYPE_SUBACK) { + return false; + } + + /* @var SubscribeResponsePacket $packet */ + return $packet->getIdentifier() === $this->identifier; + } + + public function next(Packet $packet) + { + /* @var SubscribeResponsePacket $packet */ + $returnCodes = $packet->getReturnCodes(); + if (count($returnCodes) !== count($this->subscriptions)) { + throw new \LogicException( + sprintf( + 'SUBACK: Expected %d return codes but got %d.', + count($this->subscriptions), + count($returnCodes) + ) + ); + } + + foreach ($returnCodes as $index => $code) { + if ($packet->isError($code)) { + $this->fail(sprintf('Failed to subscribe to "%s".', $this->subscriptions[$index]->getFilter())); + + return; + } + } + + $this->succeed($this->subscriptions[0]); + } +} diff --git a/instafeed/vendor/binsoul/net-mqtt/src/Flow/OutgoingUnsubscribeFlow.php b/instafeed/vendor/binsoul/net-mqtt/src/Flow/OutgoingUnsubscribeFlow.php new file mode 100755 index 0000000..4949ff3 --- /dev/null +++ b/instafeed/vendor/binsoul/net-mqtt/src/Flow/OutgoingUnsubscribeFlow.php @@ -0,0 +1,61 @@ +subscriptions = array_values($subscriptions); + $this->identifier = $generator->generatePacketID(); + } + + public function getCode() + { + return 'unsubscribe'; + } + + public function start() + { + $packet = new UnsubscribeRequestPacket(); + $packet->setTopic($this->subscriptions[0]->getFilter()); + $packet->setIdentifier($this->identifier); + + return $packet; + } + + public function accept(Packet $packet) + { + if ($packet->getPacketType() !== Packet::TYPE_UNSUBACK) { + return false; + } + + /* @var UnsubscribeResponsePacket $packet */ + return $packet->getIdentifier() === $this->identifier; + } + + public function next(Packet $packet) + { + $this->succeed($this->subscriptions[0]); + } +} diff --git a/instafeed/vendor/binsoul/net-mqtt/src/IdentifierGenerator.php b/instafeed/vendor/binsoul/net-mqtt/src/IdentifierGenerator.php new file mode 100755 index 0000000..94c7c78 --- /dev/null +++ b/instafeed/vendor/binsoul/net-mqtt/src/IdentifierGenerator.php @@ -0,0 +1,23 @@ +write($output); + + return $output->getData(); + } + + public function read(PacketStream $stream) + { + $byte = $stream->readByte(); + + if ($byte >> 4 !== static::$packetType) { + throw new MalformedPacketException( + sprintf( + 'Expected packet type %02x but got %02x.', + $byte >> 4, + static::$packetType + ) + ); + } + + $this->packetFlags = $byte & 0x0F; + $this->readRemainingLength($stream); + } + + public function write(PacketStream $stream) + { + $stream->writeByte(((static::$packetType & 0x0F) << 4) + ($this->packetFlags & 0x0F)); + $this->writeRemainingLength($stream); + } + + /** + * Reads the remaining length from the given stream. + * + * @param PacketStream $stream + * + * @throws MalformedPacketException + */ + private function readRemainingLength(PacketStream $stream) + { + $this->remainingPacketLength = 0; + $multiplier = 1; + + do { + $encodedByte = $stream->readByte(); + + $this->remainingPacketLength += ($encodedByte & 127) * $multiplier; + $multiplier *= 128; + + if ($multiplier > 128 * 128 * 128 * 128) { + throw new MalformedPacketException('Malformed remaining length.'); + } + } while (($encodedByte & 128) !== 0); + } + + /** + * Writes the remaining length to the given stream. + * + * @param PacketStream $stream + */ + private function writeRemainingLength(PacketStream $stream) + { + $x = $this->remainingPacketLength; + do { + $encodedByte = $x % 128; + $x = (int) ($x / 128); + if ($x > 0) { + $encodedByte |= 128; + } + + $stream->writeByte($encodedByte); + } while ($x > 0); + } + + public function getPacketType() + { + return static::$packetType; + } + + /** + * Returns the packet flags. + * + * @return int + */ + public function getPacketFlags() + { + return $this->packetFlags; + } + + /** + * Returns the remaining length. + * + * @return int + */ + public function getRemainingPacketLength() + { + return $this->remainingPacketLength; + } + + /** + * Asserts that the packet flags have a specific value. + * + * @param int $value + * @param bool $fromPacket + * + * @throws MalformedPacketException + * @throws \InvalidArgumentException + */ + protected function assertPacketFlags($value, $fromPacket = true) + { + if ($this->packetFlags !== $value) { + $this->throwException( + sprintf( + 'Expected flags %02x but got %02x.', + $value, + $this->packetFlags + ), + $fromPacket + ); + } + } + + /** + * Asserts that the remaining length is greater than zero and has a specific value. + * + * @param int|null $value value to test or null if any value greater than zero is valid + * @param bool $fromPacket + * + * @throws MalformedPacketException + * @throws \InvalidArgumentException + */ + protected function assertRemainingPacketLength($value = null, $fromPacket = true) + { + if ($value === null && $this->remainingPacketLength === 0) { + $this->throwException('Expected payload but remaining packet length is zero.', $fromPacket); + } + + if ($value !== null && $this->remainingPacketLength !== $value) { + $this->throwException( + sprintf( + 'Expected remaining packet length of %d bytes but got %d.', + $value, + $this->remainingPacketLength + ), + $fromPacket + ); + } + } + + /** + * Asserts that the given string is a well-formed MQTT string. + * + * @param string $value + * @param bool $fromPacket + * + * @throws MalformedPacketException + * @throws \InvalidArgumentException + */ + protected function assertValidStringLength($value, $fromPacket = true) + { + if (strlen($value) > 0xFFFF) { + $this->throwException( + sprintf( + 'The string "%s" is longer than 65535 byte.', + substr($value, 0, 50) + ), + $fromPacket + ); + } + } + + /** + * Asserts that the given string is a well-formed MQTT string. + * + * @param string $value + * @param bool $fromPacket + * + * @throws MalformedPacketException + * @throws \InvalidArgumentException + */ + protected function assertValidString($value, $fromPacket = true) + { + $this->assertValidStringLength($value, $fromPacket); + + if (!mb_check_encoding($value, 'UTF-8')) { + $this->throwException( + sprintf( + 'The string "%s" is not well-formed UTF-8.', + substr($value, 0, 50) + ), + $fromPacket + ); + } + + if (preg_match('/[\xD8-\xDF][\x00-\xFF]|\x00\x00/x', $value)) { + $this->throwException( + sprintf( + 'The string "%s" contains invalid characters.', + substr($value, 0, 50) + ), + $fromPacket + ); + } + } + + /** + * Asserts that the given quality of service level is valid. + * + * @param int $level + * @param bool $fromPacket + * + * @throws MalformedPacketException + * @throws \InvalidArgumentException + */ + protected function assertValidQosLevel($level, $fromPacket = true) + { + if ($level < 0 || $level > 2) { + $this->throwException( + sprintf( + 'Expected a quality of service level between 0 and 2 but got %d.', + $level + ), + $fromPacket + ); + } + } + + /** + * Throws a MalformedPacketException for packet validation and an InvalidArgumentException otherwise. + * + * @param string $message + * @param bool $fromPacket + * + * @throws MalformedPacketException + * @throws \InvalidArgumentException + */ + protected function throwException($message, $fromPacket) + { + if ($fromPacket) { + throw new MalformedPacketException($message); + } + + throw new \InvalidArgumentException($message); + } +} diff --git a/instafeed/vendor/binsoul/net-mqtt/src/Packet/ConnectRequestPacket.php b/instafeed/vendor/binsoul/net-mqtt/src/Packet/ConnectRequestPacket.php new file mode 100755 index 0000000..7abce1f --- /dev/null +++ b/instafeed/vendor/binsoul/net-mqtt/src/Packet/ConnectRequestPacket.php @@ -0,0 +1,405 @@ +assertPacketFlags(0); + $this->assertRemainingPacketLength(); + + $this->protocolName = $stream->readString(); + $this->protocolLevel = $stream->readByte(); + $this->flags = $stream->readByte(); + $this->keepAlive = $stream->readWord(); + $this->clientID = $stream->readString(); + + if ($this->hasWill()) { + $this->willTopic = $stream->readString(); + $this->willMessage = $stream->readString(); + } + + if ($this->hasUsername()) { + $this->username = $stream->readString(); + } + + if ($this->hasPassword()) { + $this->password = $stream->readString(); + } + + $this->assertValidWill(); + $this->assertValidString($this->clientID); + $this->assertValidString($this->willTopic); + $this->assertValidString($this->username); + } + + public function write(PacketStream $stream) + { + if ($this->clientID === '') { + $this->clientID = 'BinSoul'.mt_rand(100000, 999999); + } + + $data = new PacketStream(); + + $data->writeString($this->protocolName); + $data->writeByte($this->protocolLevel); + $data->writeByte($this->flags); + $data->writeWord($this->keepAlive); + $data->writeString($this->clientID); + + if ($this->hasWill()) { + $data->writeString($this->willTopic); + $data->writeString($this->willMessage); + } + + if ($this->hasUsername()) { + $data->writeString($this->username); + } + + if ($this->hasPassword()) { + $data->writeString($this->password); + } + + $this->remainingPacketLength = $data->length(); + + parent::write($stream); + $stream->write($data->getData()); + } + + /** + * Returns the protocol level. + * + * @return int + */ + public function getProtocolLevel() + { + return $this->protocolLevel; + } + + /** + * Sets the protocol level. + * + * @param int $value + * + * @throws \InvalidArgumentException + */ + public function setProtocolLevel($value) + { + if ($value < 3 || $value > 4) { + throw new \InvalidArgumentException(sprintf('Unknown protocol level %d.', $value)); + } + + $this->protocolLevel = $value; + if ($this->protocolLevel === 3) { + $this->protocolName = 'MQIsdp'; + } elseif ($this->protocolLevel === 4) { + $this->protocolName = 'MQTT'; + } + } + + /** + * Returns the client id. + * + * @return string + */ + public function getClientID() + { + return $this->clientID; + } + + /** + * Sets the client id. + * + * @param string $value + */ + public function setClientID($value) + { + $this->clientID = $value; + } + + /** + * Returns the keep alive time in seconds. + * + * @return int + */ + public function getKeepAlive() + { + return $this->keepAlive; + } + + /** + * Sets the keep alive time in seconds. + * + * @param int $value + * + * @throws \InvalidArgumentException + */ + public function setKeepAlive($value) + { + if ($value > 65535) { + throw new \InvalidArgumentException( + sprintf( + 'Expected a keep alive time lower than 65535 but got %d.', + $value + ) + ); + } + + $this->keepAlive = $value; + } + + /** + * Indicates if the clean session flag is set. + * + * @return bool + */ + public function isCleanSession() + { + return ($this->flags & 2) === 2; + } + + /** + * Changes the clean session flag. + * + * @param bool $value + */ + public function setCleanSession($value) + { + if ($value) { + $this->flags |= 2; + } else { + $this->flags &= ~2; + } + } + + /** + * Indicates if a will is set. + * + * @return bool + */ + public function hasWill() + { + return ($this->flags & 4) === 4; + } + + /** + * Returns the desired quality of service level of the will. + * + * @return int + */ + public function getWillQosLevel() + { + return ($this->flags & 24) >> 3; + } + + /** + * Indicates if the will should be retained. + * + * @return bool + */ + public function isWillRetained() + { + return ($this->flags & 32) === 32; + } + + /** + * Returns the will topic. + * + * @return string + */ + public function getWillTopic() + { + return $this->willTopic; + } + + /** + * Returns the will message. + * + * @return string + */ + public function getWillMessage() + { + return $this->willMessage; + } + + /** + * Sets the will. + * + * @param string $topic + * @param string $message + * @param int $qosLevel + * @param bool $isRetained + * + * @throws \InvalidArgumentException + */ + public function setWill($topic, $message, $qosLevel = 0, $isRetained = false) + { + $this->assertValidString($topic, false); + if ($topic === '') { + throw new \InvalidArgumentException('The topic must not be empty.'); + } + + $this->assertValidStringLength($message, false); + if ($message === '') { + throw new \InvalidArgumentException('The message must not be empty.'); + } + + $this->assertValidQosLevel($qosLevel, false); + + $this->willTopic = $topic; + $this->willMessage = $message; + + $this->flags |= 4; + $this->flags |= ($qosLevel << 3); + + if ($isRetained) { + $this->flags |= 32; + } else { + $this->flags &= ~32; + } + } + + /** + * Removes the will. + */ + public function removeWill() + { + $this->flags &= ~60; + $this->willTopic = ''; + $this->willMessage = ''; + } + + /** + * Indicates if a username is set. + * + * @return bool + */ + public function hasUsername() + { + return $this->flags & 64; + } + + /** + * Returns the username. + * + * @return string + */ + public function getUsername() + { + return $this->username; + } + + /** + * Sets the username. + * + * @param string $value + * + * @throws \InvalidArgumentException + */ + public function setUsername($value) + { + $this->assertValidString($value, false); + + $this->username = $value; + if ($this->username !== '') { + $this->flags |= 64; + } else { + $this->flags &= ~64; + } + } + + /** + * Indicates if a password is set. + * + * @return bool + */ + public function hasPassword() + { + return $this->flags & 128; + } + + /** + * Returns the password. + * + * @return string + */ + public function getPassword() + { + return $this->password; + } + + /** + * Sets the password. + * + * @param string $value + * + * @throws \InvalidArgumentException + */ + public function setPassword($value) + { + $this->assertValidStringLength($value, false); + + $this->password = $value; + if ($this->password !== '') { + $this->flags |= 128; + } else { + $this->flags &= ~128; + } + } + + /** + * Asserts that all will flags and quality of service are correct. + * + * @throws MalformedPacketException + */ + private function assertValidWill() + { + if ($this->hasWill()) { + $this->assertValidQosLevel($this->getWillQosLevel(), true); + } else { + if ($this->getWillQosLevel() > 0) { + $this->throwException( + sprintf( + 'Expected a will quality of service level of zero but got %d.', + $this->getWillQosLevel() + ), + true + ); + } + + if ($this->isWillRetained()) { + $this->throwException('There is not will but the will retain flag is set.', true); + } + } + } +} diff --git a/instafeed/vendor/binsoul/net-mqtt/src/Packet/ConnectResponsePacket.php b/instafeed/vendor/binsoul/net-mqtt/src/Packet/ConnectResponsePacket.php new file mode 100755 index 0000000..f896d22 --- /dev/null +++ b/instafeed/vendor/binsoul/net-mqtt/src/Packet/ConnectResponsePacket.php @@ -0,0 +1,111 @@ + [ + 'Connection accepted', + '', + ], + 1 => [ + 'Unacceptable protocol version', + 'The Server does not support the level of the MQTT protocol requested by the client.', + ], + 2 => [ + 'Identifier rejected', + 'The client identifier is correct UTF-8 but not allowed by the server.', + ], + 3 => [ + 'Server unavailable', + 'The network connection has been made but the MQTT service is unavailable', + ], + 4 => [ + 'Bad user name or password', + 'The data in the user name or password is malformed.', + ], + 5 => [ + 'Not authorized', + 'The client is not authorized to connect.', + ], + ]; + + /** @var int */ + private $flags = 0; + /** @var int */ + private $returnCode; + + protected static $packetType = Packet::TYPE_CONNACK; + protected $remainingPacketLength = 2; + + public function read(PacketStream $stream) + { + parent::read($stream); + $this->assertPacketFlags(0); + $this->assertRemainingPacketLength(2); + + $this->flags = $stream->readByte(); + $this->returnCode = $stream->readByte(); + } + + public function write(PacketStream $stream) + { + $this->remainingPacketLength = 2; + parent::write($stream); + + $stream->writeByte($this->flags); + $stream->writeByte($this->returnCode); + } + + /** + * Returns the return code. + * + * @return int + */ + public function getReturnCode() + { + return $this->returnCode; + } + + /** + * Indicates if the connection was successful. + * + * @return bool + */ + public function isSuccess() + { + return $this->returnCode === 0; + } + + /** + * Indicates if the connection failed. + * + * @return bool + */ + public function isError() + { + return $this->returnCode > 0; + } + + /** + * Returns a string representation of the returned error code. + * + * @return int + */ + public function getErrorName() + { + if (isset(self::$returnCodes[$this->returnCode])) { + return self::$returnCodes[$this->returnCode][0]; + } + + return 'Error '.$this->returnCode; + } +} diff --git a/instafeed/vendor/binsoul/net-mqtt/src/Packet/DisconnectRequestPacket.php b/instafeed/vendor/binsoul/net-mqtt/src/Packet/DisconnectRequestPacket.php new file mode 100755 index 0000000..0cda785 --- /dev/null +++ b/instafeed/vendor/binsoul/net-mqtt/src/Packet/DisconnectRequestPacket.php @@ -0,0 +1,22 @@ +assertPacketFlags(0); + $this->assertRemainingPacketLength(0); + } +} diff --git a/instafeed/vendor/binsoul/net-mqtt/src/Packet/IdentifiablePacket.php b/instafeed/vendor/binsoul/net-mqtt/src/Packet/IdentifiablePacket.php new file mode 100755 index 0000000..a44ae1c --- /dev/null +++ b/instafeed/vendor/binsoul/net-mqtt/src/Packet/IdentifiablePacket.php @@ -0,0 +1,62 @@ +identifier === null) { + ++self::$nextIdentifier; + self::$nextIdentifier &= 0xFFFF; + + $this->identifier = self::$nextIdentifier; + } + + return $this->identifier; + } + + /** + * Returns the identifier. + * + * @return int|null + */ + public function getIdentifier() + { + return $this->identifier; + } + + /** + * Sets the identifier. + * + * @param int|null $value + * + * @throws \InvalidArgumentException + */ + public function setIdentifier($value) + { + if ($value !== null && ($value < 0 || $value > 0xFFFF)) { + throw new \InvalidArgumentException( + sprintf( + 'Expected an identifier between 0x0000 and 0xFFFF but got %x', + $value + ) + ); + } + + $this->identifier = $value; + } +} diff --git a/instafeed/vendor/binsoul/net-mqtt/src/Packet/IdentifierOnlyPacket.php b/instafeed/vendor/binsoul/net-mqtt/src/Packet/IdentifierOnlyPacket.php new file mode 100755 index 0000000..01dffa5 --- /dev/null +++ b/instafeed/vendor/binsoul/net-mqtt/src/Packet/IdentifierOnlyPacket.php @@ -0,0 +1,42 @@ +assertPacketFlags($this->getExpectedPacketFlags()); + $this->assertRemainingPacketLength(2); + + $this->identifier = $stream->readWord(); + } + + public function write(PacketStream $stream) + { + $this->remainingPacketLength = 2; + parent::write($stream); + + $stream->writeWord($this->generateIdentifier()); + } + + /** + * Returns the expected packet flags. + * + * @return int + */ + protected function getExpectedPacketFlags() + { + return 0; + } +} diff --git a/instafeed/vendor/binsoul/net-mqtt/src/Packet/PingRequestPacket.php b/instafeed/vendor/binsoul/net-mqtt/src/Packet/PingRequestPacket.php new file mode 100755 index 0000000..cb0fc58 --- /dev/null +++ b/instafeed/vendor/binsoul/net-mqtt/src/Packet/PingRequestPacket.php @@ -0,0 +1,22 @@ +assertPacketFlags(0); + $this->assertRemainingPacketLength(0); + } +} diff --git a/instafeed/vendor/binsoul/net-mqtt/src/Packet/PingResponsePacket.php b/instafeed/vendor/binsoul/net-mqtt/src/Packet/PingResponsePacket.php new file mode 100755 index 0000000..79eaeb3 --- /dev/null +++ b/instafeed/vendor/binsoul/net-mqtt/src/Packet/PingResponsePacket.php @@ -0,0 +1,22 @@ +assertPacketFlags(0); + $this->assertRemainingPacketLength(0); + } +} diff --git a/instafeed/vendor/binsoul/net-mqtt/src/Packet/PublishAckPacket.php b/instafeed/vendor/binsoul/net-mqtt/src/Packet/PublishAckPacket.php new file mode 100755 index 0000000..fc4937e --- /dev/null +++ b/instafeed/vendor/binsoul/net-mqtt/src/Packet/PublishAckPacket.php @@ -0,0 +1,13 @@ +assertRemainingPacketLength(); + + $originalPosition = $stream->getPosition(); + $this->topic = $stream->readString(); + $this->identifier = null; + if ($this->getQosLevel() > 0) { + $this->identifier = $stream->readWord(); + } + + $payloadLength = $this->remainingPacketLength - ($stream->getPosition() - $originalPosition); + $this->payload = $stream->read($payloadLength); + + $this->assertValidQosLevel($this->getQosLevel()); + $this->assertValidString($this->topic); + } + + public function write(PacketStream $stream) + { + $data = new PacketStream(); + + $data->writeString($this->topic); + if ($this->getQosLevel() > 0) { + $data->writeWord($this->generateIdentifier()); + } + + $data->write($this->payload); + + $this->remainingPacketLength = $data->length(); + + parent::write($stream); + $stream->write($data->getData()); + } + + /** + * Returns the topic. + * + * @return string + */ + public function getTopic() + { + return $this->topic; + } + + /** + * Sets the topic. + * + * @param string $value + * + * @throws \InvalidArgumentException + */ + public function setTopic($value) + { + $this->assertValidString($value, false); + if ($value === '') { + throw new \InvalidArgumentException('The topic must not be empty.'); + } + + $this->topic = $value; + } + + /** + * Returns the payload. + * + * @return string + */ + public function getPayload() + { + return $this->payload; + } + + /** + * Sets the payload. + * + * @param string $value + */ + public function setPayload($value) + { + $this->payload = $value; + } + + /** + * Indicates if the packet is a duplicate. + * + * @return bool + */ + public function isDuplicate() + { + return ($this->packetFlags & 8) === 8; + } + + /** + * Marks the packet as duplicate. + * + * @param bool $value + */ + public function setDuplicate($value) + { + if ($value) { + $this->packetFlags |= 8; + } else { + $this->packetFlags &= ~8; + } + } + + /** + * Indicates if the packet is retained. + * + * @return bool + */ + public function isRetained() + { + return ($this->packetFlags & 1) === 1; + } + + /** + * Marks the packet as retained. + * + * @param bool $value + */ + public function setRetained($value) + { + if ($value) { + $this->packetFlags |= 1; + } else { + $this->packetFlags &= ~1; + } + } + + /** + * Returns the quality of service level. + * + * @return int + */ + public function getQosLevel() + { + return ($this->packetFlags & 6) >> 1; + } + + /** + * Sets the quality of service level. + * + * @param int $value + * + * @throws \InvalidArgumentException + */ + public function setQosLevel($value) + { + $this->assertValidQosLevel($value, false); + + $this->packetFlags |= ($value & 3) << 1; + } +} diff --git a/instafeed/vendor/binsoul/net-mqtt/src/Packet/StrictConnectRequestPacket.php b/instafeed/vendor/binsoul/net-mqtt/src/Packet/StrictConnectRequestPacket.php new file mode 100755 index 0000000..71e8c78 --- /dev/null +++ b/instafeed/vendor/binsoul/net-mqtt/src/Packet/StrictConnectRequestPacket.php @@ -0,0 +1,66 @@ +assertValidClientID($this->clientID, true); + } + + /** + * Sets the client id. + * + * @param string $value + * + * @throws \InvalidArgumentException + */ + public function setClientID($value) + { + $this->assertValidClientID($value, false); + + $this->clientID = $value; + } + + /** + * Asserts that a client id is shorter than 24 bytes and only contains characters 0-9, a-z or A-Z. + * + * @param string $value + * @param bool $fromPacket + * + * @throws MalformedPacketException + * @throws \InvalidArgumentException + */ + private function assertValidClientID($value, $fromPacket) + { + + if (strlen($value) > 23) { + $this->throwException( + sprintf( + 'Expected client id shorter than 24 bytes but got "%s".', + $value + ), + $fromPacket + ); + } + + if ($value !== '' && !ctype_alnum($value)) { + $this->throwException( + sprintf( + 'Expected a client id containing characters 0-9, a-z or A-Z but got "%s".', + $value + ), + $fromPacket + ); + } + } +} diff --git a/instafeed/vendor/binsoul/net-mqtt/src/Packet/SubscribeRequestPacket.php b/instafeed/vendor/binsoul/net-mqtt/src/Packet/SubscribeRequestPacket.php new file mode 100755 index 0000000..55aa831 --- /dev/null +++ b/instafeed/vendor/binsoul/net-mqtt/src/Packet/SubscribeRequestPacket.php @@ -0,0 +1,101 @@ +assertPacketFlags(2); + $this->assertRemainingPacketLength(); + + $this->identifier = $stream->readWord(); + $this->topic = $stream->readString(); + $this->qosLevel = $stream->readByte(); + + $this->assertValidQosLevel($this->qosLevel); + $this->assertValidString($this->topic); + } + + public function write(PacketStream $stream) + { + $data = new PacketStream(); + + $data->writeWord($this->generateIdentifier()); + $data->writeString($this->topic); + $data->writeByte($this->qosLevel); + + $this->remainingPacketLength = $data->length(); + + parent::write($stream); + $stream->write($data->getData()); + } + + /** + * Returns the topic. + * + * @return string + */ + public function getTopic() + { + return $this->topic; + } + + /** + * Sets the topic. + * + * @param string $value + * + * @throws \InvalidArgumentException + */ + public function setTopic($value) + { + $this->assertValidString($value, false); + if ($value === '') { + throw new \InvalidArgumentException('The topic must not be empty.'); + } + + $this->topic = $value; + } + + /** + * Returns the quality of service level. + * + * @return int + */ + public function getQosLevel() + { + return $this->qosLevel; + } + + /** + * Sets the quality of service level. + * + * @param int $value + * + * @throws \InvalidArgumentException + */ + public function setQosLevel($value) + { + $this->assertValidQosLevel($value, false); + + $this->qosLevel = $value; + } +} diff --git a/instafeed/vendor/binsoul/net-mqtt/src/Packet/SubscribeResponsePacket.php b/instafeed/vendor/binsoul/net-mqtt/src/Packet/SubscribeResponsePacket.php new file mode 100755 index 0000000..35c78e4 --- /dev/null +++ b/instafeed/vendor/binsoul/net-mqtt/src/Packet/SubscribeResponsePacket.php @@ -0,0 +1,132 @@ + ['Maximum QoS 0'], + 1 => ['Maximum QoS 1'], + 2 => ['Maximum QoS 2'], + 128 => ['Failure'], + ]; + + /** @var int[] */ + private $returnCodes; + + protected static $packetType = Packet::TYPE_SUBACK; + + public function read(PacketStream $stream) + { + parent::read($stream); + $this->assertPacketFlags(0); + $this->assertRemainingPacketLength(); + + $this->identifier = $stream->readWord(); + + $returnCodeLength = $this->remainingPacketLength - 2; + for ($n = 0; $n < $returnCodeLength; ++$n) { + $returnCode = $stream->readByte(); + $this->assertValidReturnCode($returnCode); + + $this->returnCodes[] = $returnCode; + } + } + + public function write(PacketStream $stream) + { + $data = new PacketStream(); + + $data->writeWord($this->generateIdentifier()); + foreach ($this->returnCodes as $returnCode) { + $data->writeByte($returnCode); + } + + $this->remainingPacketLength = $data->length(); + + parent::write($stream); + $stream->write($data->getData()); + } + + /** + * Indicates if the given return code is an error. + * + * @param int $returnCode + * + * @return bool + */ + public function isError($returnCode) + { + return $returnCode === 128; + } + + /** + * Indicates if the given return code is an error. + * + * @param int $returnCode + * + * @return bool + */ + public function getReturnCodeName($returnCode) + { + if (isset(self::$qosLevels[$returnCode])) { + return self::$qosLevels[$returnCode][0]; + } + + return 'Unknown '.$returnCode; + } + + /** + * Returns the return codes. + * + * @return int[] + */ + public function getReturnCodes() + { + return $this->returnCodes; + } + + /** + * Sets the return codes. + * + * @param int[] $value + * + * @throws \InvalidArgumentException + */ + public function setReturnCodes(array $value) + { + foreach ($value as $returnCode) { + $this->assertValidReturnCode($returnCode, false); + } + + $this->returnCodes = $value; + } + + /** + * Asserts that a return code is valid. + * + * @param int $returnCode + * @param bool $fromPacket + * + * @throws MalformedPacketException + * @throws \InvalidArgumentException + */ + private function assertValidReturnCode($returnCode, $fromPacket = true) + { + if (!in_array($returnCode, [0, 1, 2, 128])) { + $this->throwException( + sprintf('Malformed return code %02x.', $returnCode), + $fromPacket + ); + } + } +} diff --git a/instafeed/vendor/binsoul/net-mqtt/src/Packet/UnsubscribeRequestPacket.php b/instafeed/vendor/binsoul/net-mqtt/src/Packet/UnsubscribeRequestPacket.php new file mode 100755 index 0000000..4bce38c --- /dev/null +++ b/instafeed/vendor/binsoul/net-mqtt/src/Packet/UnsubscribeRequestPacket.php @@ -0,0 +1,68 @@ +assertPacketFlags(2); + $this->assertRemainingPacketLength(); + + $originalPosition = $stream->getPosition(); + + do { + $this->identifier = $stream->readWord(); + $this->topic = $stream->readString(); + } while (($stream->getPosition() - $originalPosition) <= $this->remainingPacketLength); + } + + public function write(PacketStream $stream) + { + $data = new PacketStream(); + + $data->writeWord($this->generateIdentifier()); + $data->writeString($this->topic); + + $this->remainingPacketLength = $data->length(); + + parent::write($stream); + $stream->write($data->getData()); + } + + /** + * Returns the topic. + * + * @return string + */ + public function getTopic() + { + return $this->topic; + } + + /** + * Sets the topic. + * + * @param string $value + */ + public function setTopic($value) + { + $this->topic = $value; + } +} diff --git a/instafeed/vendor/binsoul/net-mqtt/src/Packet/UnsubscribeResponsePacket.php b/instafeed/vendor/binsoul/net-mqtt/src/Packet/UnsubscribeResponsePacket.php new file mode 100755 index 0000000..385b91d --- /dev/null +++ b/instafeed/vendor/binsoul/net-mqtt/src/Packet/UnsubscribeResponsePacket.php @@ -0,0 +1,13 @@ + ConnectRequestPacket::class, + Packet::TYPE_CONNACK => ConnectResponsePacket::class, + Packet::TYPE_PUBLISH => PublishRequestPacket::class, + Packet::TYPE_PUBACK => PublishAckPacket::class, + Packet::TYPE_PUBREC => PublishReceivedPacket::class, + Packet::TYPE_PUBREL => PublishReleasePacket::class, + Packet::TYPE_PUBCOMP => PublishCompletePacket::class, + Packet::TYPE_SUBSCRIBE => SubscribeRequestPacket::class, + Packet::TYPE_SUBACK => SubscribeResponsePacket::class, + Packet::TYPE_UNSUBSCRIBE => UnsubscribeRequestPacket::class, + Packet::TYPE_UNSUBACK => UnsubscribeResponsePacket::class, + Packet::TYPE_PINGREQ => PingRequestPacket::class, + Packet::TYPE_PINGRESP => PingResponsePacket::class, + Packet::TYPE_DISCONNECT => DisconnectRequestPacket::class, + ]; + + /** + * Builds a packet object for the given type. + * + * @param int $type + * + * @throws UnknownPacketTypeException + * + * @return Packet + */ + public function build($type) + { + if (!isset(self::$mapping[$type])) { + throw new UnknownPacketTypeException(sprintf('Unknown packet type %d.', $type)); + } + + $class = self::$mapping[$type]; + + return new $class(); + } +} diff --git a/instafeed/vendor/binsoul/net-mqtt/src/PacketStream.php b/instafeed/vendor/binsoul/net-mqtt/src/PacketStream.php new file mode 100755 index 0000000..eb241d6 --- /dev/null +++ b/instafeed/vendor/binsoul/net-mqtt/src/PacketStream.php @@ -0,0 +1,216 @@ +data = $data; + $this->position = 0; + } + + /** + * @return string + */ + public function __toString() + { + return $this->data; + } + + /** + * Returns the desired number of bytes. + * + * @param int $count + * + * @throws EndOfStreamException + * + * @return string + */ + public function read($count) + { + $contentLength = strlen($this->data); + if ($this->position > $contentLength || $count > $contentLength - $this->position) { + throw new EndOfStreamException( + sprintf( + 'End of stream reached when trying to read %d bytes. content length=%d, position=%d', + $count, + $contentLength, + $this->position + ) + ); + } + + $chunk = substr($this->data, $this->position, $count); + if ($chunk === false) { + $chunk = ''; + } + + $readBytes = strlen($chunk); + $this->position += $readBytes; + + return $chunk; + } + + /** + * Returns a single byte. + * + * @return int + */ + public function readByte() + { + return ord($this->read(1)); + } + + /** + * Returns a single word. + * + * @return int + */ + public function readWord() + { + return ($this->readByte() << 8) + $this->readByte(); + } + + /** + * Returns a length prefixed string. + * + * @return string + */ + public function readString() + { + $length = $this->readWord(); + + return $this->read($length); + } + + /** + * Appends the given value. + * + * @param string $value + */ + public function write($value) + { + $this->data .= $value; + } + + /** + * Appends a single byte. + * + * @param int $value + */ + public function writeByte($value) + { + $this->write(chr($value)); + } + + /** + * Appends a single word. + * + * @param int $value + */ + public function writeWord($value) + { + $this->write(chr(($value & 0xFFFF) >> 8)); + $this->write(chr($value & 0xFF)); + } + + /** + * Appends a length prefixed string. + * + * @param string $string + */ + public function writeString($string) + { + $this->writeWord(strlen($string)); + $this->write($string); + } + + /** + * Returns the length of the stream. + * + * @return int + */ + public function length() + { + return strlen($this->data); + } + + /** + * Returns the number of bytes until the end of the stream. + * + * @return int + */ + public function getRemainingBytes() + { + return $this->length() - $this->position; + } + + /** + * Returns the whole content of the stream. + * + * @return string + */ + public function getData() + { + return $this->data; + } + + /** + * Changes the internal position of the stream relative to the current position. + * + * @param int $offset + */ + public function seek($offset) + { + $this->position += $offset; + } + + /** + * Returns the internal position of the stream. + * + * @return int + */ + public function getPosition() + { + return $this->position; + } + + /** + * Sets the internal position of the stream. + * + * @param int $value + */ + public function setPosition($value) + { + $this->position = $value; + } + + /** + * Removes all bytes from the beginning to the current position. + */ + public function cut() + { + $this->data = substr($this->data, $this->position); + if ($this->data === false) { + $this->data = ''; + } + + $this->position = 0; + } +} diff --git a/instafeed/vendor/binsoul/net-mqtt/src/StreamParser.php b/instafeed/vendor/binsoul/net-mqtt/src/StreamParser.php new file mode 100755 index 0000000..398f3e6 --- /dev/null +++ b/instafeed/vendor/binsoul/net-mqtt/src/StreamParser.php @@ -0,0 +1,90 @@ +buffer = new PacketStream(); + $this->factory = new PacketFactory(); + } + + /** + * Registers an error callback. + * + * @param callable $callback + */ + public function onError($callback) + { + $this->errorCallback = $callback; + } + + /** + * Appends the given data to the internal buffer and parses it. + * + * @param string $data + * + * @return Packet[] + */ + public function push($data) + { + $this->buffer->write($data); + + $result = []; + while ($this->buffer->getRemainingBytes() > 0) { + $type = $this->buffer->readByte() >> 4; + try { + $packet = $this->factory->build($type); + } catch (UnknownPacketTypeException $e) { + $this->handleError($e); + continue; + } + + $this->buffer->seek(-1); + $position = $this->buffer->getPosition(); + try { + $packet->read($this->buffer); + $result[] = $packet; + $this->buffer->cut(); + } catch (EndOfStreamException $e) { + $this->buffer->setPosition($position); + break; + } catch (MalformedPacketException $e) { + $this->handleError($e); + } + } + + return $result; + } + + /** + * Executes the registered error callback. + * + * @param \Throwable $exception + */ + private function handleError($exception) + { + if ($this->errorCallback !== null) { + $callback = $this->errorCallback; + $callback($exception); + } + } +} diff --git a/instafeed/vendor/binsoul/net-mqtt/src/Subscription.php b/instafeed/vendor/binsoul/net-mqtt/src/Subscription.php new file mode 100755 index 0000000..b870533 --- /dev/null +++ b/instafeed/vendor/binsoul/net-mqtt/src/Subscription.php @@ -0,0 +1,41 @@ + + */ +class TopicMatcher +{ + /** + * Check if the given topic matches the filter. + * + * @param string $filter e.g. A/B/+, A/B/# + * @param string $topic e.g. A/B/C, A/B/foo/bar/baz + * + * @return bool true if topic matches the pattern + */ + public function matches($filter, $topic) + { + // Created by Steffen (https://github.com/kernelguy) + $tokens = explode('/', $filter); + $parts = []; + for ($i = 0, $count = count($tokens); $i < $count; ++$i) { + $token = $tokens[$i]; + switch ($token) { + case '+': + $parts[] = '[^/#\+]*'; + + break; + case '#': + if ($i === 0) { + $parts[] = '[^\+\$]*'; + } else { + $parts[] = '[^\+]*'; + } + + break; + default: + $parts[] = str_replace('+', '\+', $token); + + break; + } + } + + $regex = implode('/', $parts); + $regex = str_replace('$', '\$', $regex); + $regex = ';^'.$regex.'$;'; + + return preg_match($regex, $topic) === 1; + } +} diff --git a/instafeed/vendor/clue/http-proxy-react/.gitignore b/instafeed/vendor/clue/http-proxy-react/.gitignore new file mode 100755 index 0000000..4fbb073 --- /dev/null +++ b/instafeed/vendor/clue/http-proxy-react/.gitignore @@ -0,0 +1,2 @@ +/vendor/ +/composer.lock diff --git a/instafeed/vendor/clue/http-proxy-react/.travis.yml b/instafeed/vendor/clue/http-proxy-react/.travis.yml new file mode 100755 index 0000000..04f51ad --- /dev/null +++ b/instafeed/vendor/clue/http-proxy-react/.travis.yml @@ -0,0 +1,27 @@ +language: php + +php: +# - 5.3 # requires old distro, see below + - 5.4 + - 5.5 + - 5.6 + - 7 + - hhvm # ignore errors, see below + +# lock distro so new future defaults will not break the build +dist: trusty + +matrix: + include: + - php: 5.3 + dist: precise + allow_failures: + - php: hhvm + +sudo: false + +install: + - composer install --no-interaction + +script: + - vendor/bin/phpunit --coverage-text diff --git a/instafeed/vendor/clue/http-proxy-react/CHANGELOG.md b/instafeed/vendor/clue/http-proxy-react/CHANGELOG.md new file mode 100755 index 0000000..3d25812 --- /dev/null +++ b/instafeed/vendor/clue/http-proxy-react/CHANGELOG.md @@ -0,0 +1,103 @@ +# Changelog + +## 1.3.0 (2018-02-13) + +* Feature: Support communication over Unix domain sockets (UDS) + (#20 by @clue) + + ```php + // new: now supports communication over Unix domain sockets (UDS) + $proxy = new ProxyConnector('http+unix:///tmp/proxy.sock', $connector); + ``` + +* Reduce memory consumption by avoiding circular reference from stream reader + (#18 by @valga) + +* Improve documentation + (#19 by @clue) + +## 1.2.0 (2017-08-30) + +* Feature: Use socket error codes for connection rejections + (#17 by @clue) + + ```php + $promise = $proxy->connect('imap.example.com:143'); + $promise->then(null, function (Exeption $e) { + if ($e->getCode() === SOCKET_EACCES) { + echo 'Failed to authenticate with proxy!'; + } + throw $e; + }); + ``` + +* Improve test suite by locking Travis distro so new defaults will not break the build and + optionally exclude tests that rely on working internet connection + (#15 and #16 by @clue) + +## 1.1.0 (2017-06-11) + +* Feature: Support proxy authentication if proxy URL contains username/password + (#14 by @clue) + + ```php + // new: username/password will now be passed to HTTP proxy server + $proxy = new ProxyConnector('user:pass@127.0.0.1:8080', $connector); + ``` + +## 1.0.0 (2017-06-10) + +* First stable release, now following SemVer + +> Contains no other changes, so it's actually fully compatible with the v0.3.2 release. + +## 0.3.2 (2017-06-10) + +* Fix: Fix rejecting invalid URIs and unexpected URI schemes + (#13 by @clue) + +* Fix HHVM build for now again and ignore future HHVM build errors + (#12 by @clue) + +* Documentation for Connector concepts (TCP/TLS, timeouts, DNS resolution) + (#11 by @clue) + +## 0.3.1 (2017-05-10) + +* Feature: Forward compatibility with upcoming Socket v1.0 and v0.8 + (#10 by @clue) + +## 0.3.0 (2017-04-10) + +* Feature / BC break: Replace deprecated SocketClient with new Socket component + (#9 by @clue) + + This implies that the `ProxyConnector` from this package now implements the + `React\Socket\ConnectorInterface` instead of the legacy + `React\SocketClient\ConnectorInterface`. + +## 0.2.0 (2017-04-10) + +* Feature / BC break: Update SocketClient to v0.7 or v0.6 and + use `connect($uri)` instead of `create($host, $port)` + (#8 by @clue) + + ```php + // old + $connector->create($host, $port)->then(function (Stream $conn) { + $conn->write("…"); + }); + + // new + $connector->connect($uri)->then(function (ConnectionInterface $conn) { + $conn->write("…"); + }); + ``` + +* Improve test suite by adding PHPUnit to require-dev + (#7 by @clue) + + +## 0.1.0 (2016-11-01) + +* First tagged release diff --git a/instafeed/vendor/clue/http-proxy-react/LICENSE b/instafeed/vendor/clue/http-proxy-react/LICENSE new file mode 100755 index 0000000..7baae8e --- /dev/null +++ b/instafeed/vendor/clue/http-proxy-react/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Christian Lück + +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/instafeed/vendor/clue/http-proxy-react/README.md b/instafeed/vendor/clue/http-proxy-react/README.md new file mode 100755 index 0000000..442b7f9 --- /dev/null +++ b/instafeed/vendor/clue/http-proxy-react/README.md @@ -0,0 +1,422 @@ +# clue/http-proxy-react [![Build Status](https://travis-ci.org/clue/php-http-proxy-react.svg?branch=master)](https://travis-ci.org/clue/php-http-proxy-react) + +Async HTTP proxy connector, use any TCP/IP-based protocol through an HTTP +CONNECT proxy server, built on top of [ReactPHP](https://reactphp.org). + +HTTP CONNECT proxy servers (also commonly known as "HTTPS proxy" or "SSL proxy") +are commonly used to tunnel HTTPS traffic through an intermediary ("proxy"), to +conceal the origin address (anonymity) or to circumvent address blocking +(geoblocking). While many (public) HTTP CONNECT proxy servers often limit this +to HTTPS port `443` only, this can technically be used to tunnel any +TCP/IP-based protocol (HTTP, SMTP, IMAP etc.). +This library provides a simple API to create these tunneled connection for you. +Because it implements ReactPHP's standard +[`ConnectorInterface`](https://github.com/reactphp/socket#connectorinterface), +it can simply be used in place of a normal connector. +This makes it fairly simple to add HTTP CONNECT proxy support to pretty much any +existing higher-level protocol implementation. + +* **Async execution of connections** - + Send any number of HTTP CONNECT requests in parallel and process their + responses as soon as results come in. + The Promise-based design provides a *sane* interface to working with out of + bound responses and possible connection errors. +* **Standard interfaces** - + Allows easy integration with existing higher-level components by implementing + ReactPHP's standard + [`ConnectorInterface`](https://github.com/reactphp/socket#connectorinterface). +* **Lightweight, SOLID design** - + Provides a thin abstraction that is [*just good enough*](http://en.wikipedia.org/wiki/Principle_of_good_enough) + and does not get in your way. + Builds on top of well-tested components and well-established concepts instead of reinventing the wheel. +* **Good test coverage** - + Comes with an automated tests suite and is regularly tested against actual proxy servers in the wild + +**Table of contents** + +* [Quickstart example](#quickstart-example) +* [Usage](#usage) + * [ProxyConnector](#proxyconnector) + * [Plain TCP connections](#plain-tcp-connections) + * [Secure TLS connections](#secure-tls-connections) + * [Connection timeout](#connection-timeout) + * [DNS resolution](#dns-resolution) + * [Authentication](#authentication) + * [Advanced secure proxy connections](#advanced-secure-proxy-connections) + * [Advanced Unix domain sockets](#advanced-unix-domain-sockets) +* [Install](#install) +* [Tests](#tests) +* [License](#license) +* [More](#more) + +### Quickstart example + +The following example code demonstrates how this library can be used to send a +secure HTTPS request to google.com through a local HTTP proxy server: + +```php +$loop = React\EventLoop\Factory::create(); + +$proxy = new ProxyConnector('127.0.0.1:8080', new Connector($loop)); +$connector = new Connector($loop, array( + 'tcp' => $proxy, + 'timeout' => 3.0, + 'dns' => false +)); + +$connector->connect('tls://google.com:443')->then(function (ConnectionInterface $stream) { + $stream->write("GET / HTTP/1.1\r\nHost: google.com\r\nConnection: close\r\n\r\n"); + $stream->on('data', function ($chunk) { + echo $chunk; + }); +}, 'printf'); + +$loop->run(); +``` + +See also the [examples](examples). + +## Usage + +### ProxyConnector + +The `ProxyConnector` is responsible for creating plain TCP/IP connections to +any destination by using an intermediary HTTP CONNECT proxy. + +``` +[you] -> [proxy] -> [destination] +``` + +Its constructor simply accepts an HTTP proxy URL and a connector used to connect +to the proxy server address: + +```php +$connector = new Connector($loop); +$proxy = new ProxyConnector('http://127.0.0.1:8080', $connector); +``` + +The proxy URL may or may not contain a scheme and port definition. The default +port will be `80` for HTTP (or `443` for HTTPS), but many common HTTP proxy +servers use custom ports (often the alternative HTTP port `8080`). +In its most simple form, the given connector will be a +[`\React\Socket\Connector`](https://github.com/reactphp/socket#connector) if you +want to connect to a given IP address as above. + +This is the main class in this package. +Because it implements ReactPHP's standard +[`ConnectorInterface`](https://github.com/reactphp/socket#connectorinterface), +it can simply be used in place of a normal connector. +Accordingly, it provides only a single public method, the +[`connect()`](https://github.com/reactphp/socket#connect) method. +The `connect(string $uri): PromiseInterface` +method can be used to establish a streaming connection. +It returns a [Promise](https://github.com/reactphp/promise) which either +fulfills with a [ConnectionInterface](https://github.com/reactphp/socket#connectioninterface) +on success or rejects with an `Exception` on error. + +This makes it fairly simple to add HTTP CONNECT proxy support to pretty much any +higher-level component: + +```diff +- $client = new SomeClient($connector); ++ $proxy = new ProxyConnector('http://127.0.0.1:8080', $connector); ++ $client = new SomeClient($proxy); +``` + +#### Plain TCP connections + +HTTP CONNECT proxies are most frequently used to issue HTTPS requests to your destination. +However, this is actually performed on a higher protocol layer and this +connector is actually inherently a general-purpose plain TCP/IP connector. +As documented above, you can simply invoke its `connect()` method to establish +a streaming plain TCP/IP connection and use any higher level protocol like so: + +```php +$proxy = new ProxyConnector('http://127.0.0.1:8080', $connector); + +$proxy->connect('tcp://smtp.googlemail.com:587')->then(function (ConnectionInterface $stream) { + $stream->write("EHLO local\r\n"); + $stream->on('data', function ($chunk) use ($stream) { + echo $chunk; + }); +}); +``` + +You can either use the `ProxyConnector` directly or you may want to wrap this connector +in ReactPHP's [`Connector`](https://github.com/reactphp/socket#connector): + +```php +$connector = new Connector($loop, array( + 'tcp' => $proxy, + 'dns' => false +)); + +$connector->connect('tcp://smtp.googlemail.com:587')->then(function (ConnectionInterface $stream) { + $stream->write("EHLO local\r\n"); + $stream->on('data', function ($chunk) use ($stream) { + echo $chunk; + }); +}); +``` + +Note that HTTP CONNECT proxies often restrict which ports one may connect to. +Many (public) proxy servers do in fact limit this to HTTPS (443) only. + +#### Secure TLS connections + +This class can also be used if you want to establish a secure TLS connection +(formerly known as SSL) between you and your destination, such as when using +secure HTTPS to your destination site. You can simply wrap this connector in +ReactPHP's [`Connector`](https://github.com/reactphp/socket#connector) or the +low-level [`SecureConnector`](https://github.com/reactphp/socket#secureconnector): + +```php +$proxy = new ProxyConnector('http://127.0.0.1:8080', $connector); +$connector = new Connector($loop, array( + 'tcp' => $proxy, + 'dns' => false +)); + +$connector->connect('tls://smtp.googlemail.com:465')->then(function (ConnectionInterface $stream) { + $stream->write("EHLO local\r\n"); + $stream->on('data', function ($chunk) use ($stream) { + echo $chunk; + }); +}); +``` + +> Note how secure TLS connections are in fact entirely handled outside of + this HTTP CONNECT client implementation. + +#### Connection timeout + +By default, the `ProxyConnector` does not implement any timeouts for establishing remote +connections. +Your underlying operating system may impose limits on pending and/or idle TCP/IP +connections, anywhere in a range of a few minutes to several hours. + +Many use cases require more control over the timeout and likely values much +smaller, usually in the range of a few seconds only. + +You can use ReactPHP's [`Connector`](https://github.com/reactphp/socket#connector) +or the low-level +[`TimeoutConnector`](https://github.com/reactphp/socket#timeoutconnector) +to decorate any given `ConnectorInterface` instance. +It provides the same `connect()` method, but will automatically reject the +underlying connection attempt if it takes too long: + +```php +$connector = new Connector($loop, array( + 'tcp' => $proxy, + 'dns' => false, + 'timeout' => 3.0 +)); + +$connector->connect('tcp://google.com:80')->then(function ($stream) { + // connection succeeded within 3.0 seconds +}); +``` + +See also any of the [examples](examples). + +> Note how connection timeout is in fact entirely handled outside of this + HTTP CONNECT client implementation. + +#### DNS resolution + +By default, the `ProxyConnector` does not perform any DNS resolution at all and simply +forwards any hostname you're trying to connect to the remote proxy server. +The remote proxy server is thus responsible for looking up any hostnames via DNS +(this default mode is thus called *remote DNS resolution*). + +As an alternative, you can also send the destination IP to the remote proxy +server. +In this mode you either have to stick to using IPs only (which is ofen unfeasable) +or perform any DNS lookups locally and only transmit the resolved destination IPs +(this mode is thus called *local DNS resolution*). + +The default *remote DNS resolution* is useful if your local `ProxyConnector` either can +not resolve target hostnames because it has no direct access to the internet or +if it should not resolve target hostnames because its outgoing DNS traffic might +be intercepted. + +As noted above, the `ProxyConnector` defaults to using remote DNS resolution. +However, wrapping the `ProxyConnector` in ReactPHP's +[`Connector`](https://github.com/reactphp/socket#connector) actually +performs local DNS resolution unless explicitly defined otherwise. +Given that remote DNS resolution is assumed to be the preferred mode, all +other examples explicitly disable DNS resoltion like this: + +```php +$connector = new Connector($loop, array( + 'tcp' => $proxy, + 'dns' => false +)); +``` + +If you want to explicitly use *local DNS resolution*, you can use the following code: + +```php +// set up Connector which uses Google's public DNS (8.8.8.8) +$connector = Connector($loop, array( + 'tcp' => $proxy, + 'dns' => '8.8.8.8' +)); +``` + +> Note how local DNS resolution is in fact entirely handled outside of this + HTTP CONNECT client implementation. + +#### Authentication + +If your HTTP proxy server requires authentication, you may pass the username and +password as part of the HTTP proxy URL like this: + +```php +$proxy = new ProxyConnector('http://user:pass@127.0.0.1:8080', $connector); +``` + +Note that both the username and password must be percent-encoded if they contain +special characters: + +```php +$user = 'he:llo'; +$pass = 'p@ss'; + +$proxy = new ProxyConnector( + rawurlencode($user) . ':' . rawurlencode($pass) . '@127.0.0.1:8080', + $connector +); +``` + +> The authentication details will be used for basic authentication and will be + transferred in the `Proxy-Authorization` HTTP request header for each + connection attempt. + If the authentication details are missing or not accepted by the remote HTTP + proxy server, it is expected to reject each connection attempt with a + `407` (Proxy Authentication Required) response status code and an exception + error code of `SOCKET_EACCES` (13). + +#### Advanced secure proxy connections + +Note that communication between the client and the proxy is usually via an +unencrypted, plain TCP/IP HTTP connection. Note that this is the most common +setup, because you can still establish a TLS connection between you and the +destination host as above. + +If you want to connect to a (rather rare) HTTPS proxy, you may want use the +`https://` scheme (HTTPS default port 443) and use ReactPHP's +[`Connector`](https://github.com/reactphp/socket#connector) or the low-level +[`SecureConnector`](https://github.com/reactphp/socket#secureconnector) +instance to create a secure connection to the proxy: + +```php +$connector = new Connector($loop); +$proxy = new ProxyConnector('https://127.0.0.1:443', $connector); + +$proxy->connect('tcp://smtp.googlemail.com:587'); +``` + +#### Advanced Unix domain sockets + +HTTP CONNECT proxy servers support forwarding TCP/IP based connections and +higher level protocols. +In some advanced cases, it may be useful to let your HTTP CONNECT proxy server +listen on a Unix domain socket (UDS) path instead of a IP:port combination. +For example, this allows you to rely on file system permissions instead of +having to rely on explicit [authentication](#authentication). + +You can simply use the `http+unix://` URI scheme like this: + +```php +$proxy = new ProxyConnector('http+unix:///tmp/proxy.sock', $connector); + +$proxy->connect('tcp://google.com:80')->then(function (ConnectionInterface $stream) { + // connected… +}); +``` + +Similarly, you can also combine this with [authentication](#authentication) +like this: + +```php +$proxy = new ProxyConnector('http+unix://user:pass@/tmp/proxy.sock', $connector); +``` + +> Note that Unix domain sockets (UDS) are considered advanced usage and PHP only + has limited support for this. + In particular, enabling [secure TLS](#secure-tls-connections) may not be + supported. + +> Note that the HTTP CONNECT protocol does not support the notion of UDS paths. + The above works reasonably well because UDS is only used for the connection between + client and proxy server and the path will not actually passed over the protocol. + This implies that this does not support connecting to UDS destination paths. + +## Install + +The recommended way to install this library is [through Composer](https://getcomposer.org). +[New to Composer?](https://getcomposer.org/doc/00-intro.md) + +This project follows [SemVer](http://semver.org/). +This will install the latest supported version: + +```bash +$ composer require clue/http-proxy-react:^1.3 +``` + +See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades. + +This project aims to run on any platform and thus does not require any PHP +extensions and supports running on legacy PHP 5.3 through current PHP 7+ and +HHVM. +It's *highly recommended to use PHP 7+* for this project. + +## Tests + +To run the test suite, you first need to clone this repo and then install all +dependencies [through Composer](https://getcomposer.org): + +```bash +$ composer install +``` + +To run the test suite, go to the project root and run: + +```bash +$ php vendor/bin/phpunit +``` + +The test suite contains tests that rely on a working internet connection, +alternatively you can also run it like this: + +```bash +$ php vendor/bin/phpunit --exclude-group internet +``` + +## License + +MIT + +## More + +* If you want to learn more about how the + [`ConnectorInterface`](https://github.com/reactphp/socket#connectorinterface) + and its usual implementations look like, refer to the documentation of the underlying + [react/socket](https://github.com/reactphp/socket) component. +* If you want to learn more about processing streams of data, refer to the + documentation of the underlying + [react/stream](https://github.com/reactphp/stream) component. +* As an alternative to an HTTP CONNECT proxy, you may also want to look into + using a SOCKS (SOCKS4/SOCKS5) proxy instead. + You may want to use [clue/socks-react](https://github.com/clue/php-socks-react) + which also provides an implementation of the same + [`ConnectorInterface`](https://github.com/reactphp/socket#connectorinterface) + so that supporting either proxy protocol should be fairly trivial. +* If you're dealing with public proxies, you'll likely have to work with mixed + quality and unreliable proxies. You may want to look into using + [clue/connection-manager-extra](https://github.com/clue/php-connection-manager-extra) + which allows retrying unreliable ones, implying connection timeouts, + concurrently working with multiple connectors and more. +* If you're looking for an end-user HTTP CONNECT proxy server daemon, you may + want to use [LeProxy](https://leproxy.org/). diff --git a/instafeed/vendor/clue/http-proxy-react/composer.json b/instafeed/vendor/clue/http-proxy-react/composer.json new file mode 100755 index 0000000..23801c9 --- /dev/null +++ b/instafeed/vendor/clue/http-proxy-react/composer.json @@ -0,0 +1,30 @@ +{ + "name": "clue/http-proxy-react", + "description": "Async HTTP proxy connector, use any TCP/IP-based protocol through an HTTP CONNECT proxy server, built on top of ReactPHP", + "keywords": ["HTTP", "CONNECT", "proxy", "ReactPHP", "async"], + "homepage": "https://github.com/clue/php-http-proxy-react", + "license": "MIT", + "authors": [ + { + "name": "Christian Lück", + "email": "christian@lueck.tv" + } + ], + "autoload": { + "psr-4": { "Clue\\React\\HttpProxy\\": "src/" } + }, + "autoload-dev": { + "psr-4": { "Tests\\Clue\\React\\HttpProxy\\": "tests/" } + }, + "require": { + "php": ">=5.3", + "react/promise": " ^2.1 || ^1.2.1", + "react/socket": "^1.0 || ^0.8.4", + "ringcentral/psr7": "^1.2" + }, + "require-dev": { + "phpunit/phpunit": "^5.0 || ^4.8", + "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3", + "clue/block-react": "^1.1" + } +} diff --git a/instafeed/vendor/clue/http-proxy-react/examples/01-proxy-https.php b/instafeed/vendor/clue/http-proxy-react/examples/01-proxy-https.php new file mode 100755 index 0000000..c07ea0d --- /dev/null +++ b/instafeed/vendor/clue/http-proxy-react/examples/01-proxy-https.php @@ -0,0 +1,30 @@ + $proxy, + 'timeout' => 3.0, + 'dns' => false +)); + +$connector->connect('tls://google.com:443')->then(function (ConnectionInterface $stream) { + $stream->write("GET / HTTP/1.1\r\nHost: google.com\r\nConnection: close\r\n\r\n"); + $stream->on('data', function ($chunk) { + echo $chunk; + }); +}, 'printf'); + +$loop->run(); diff --git a/instafeed/vendor/clue/http-proxy-react/examples/02-optional-proxy-https.php b/instafeed/vendor/clue/http-proxy-react/examples/02-optional-proxy-https.php new file mode 100755 index 0000000..c65e69a --- /dev/null +++ b/instafeed/vendor/clue/http-proxy-react/examples/02-optional-proxy-https.php @@ -0,0 +1,37 @@ + $proxy, + 'timeout' => 3.0, + 'dns' => false + )); +} + +$connector->connect('tls://google.com:443')->then(function (ConnectionInterface $stream) { + $stream->write("GET / HTTP/1.1\r\nHost: google.com\r\nConnection: close\r\n\r\n"); + $stream->on('data', function ($chunk) { + echo $chunk; + }); +}, 'printf'); + +$loop->run(); diff --git a/instafeed/vendor/clue/http-proxy-react/examples/11-proxy-smtp.php b/instafeed/vendor/clue/http-proxy-react/examples/11-proxy-smtp.php new file mode 100755 index 0000000..3225491 --- /dev/null +++ b/instafeed/vendor/clue/http-proxy-react/examples/11-proxy-smtp.php @@ -0,0 +1,32 @@ + $proxy, + 'timeout' => 3.0, + 'dns' => false +)); + +$connector->connect('tcp://smtp.googlemail.com:587')->then(function (ConnectionInterface $stream) { + $stream->write("EHLO local\r\n"); + $stream->on('data', function ($chunk) use ($stream) { + echo $chunk; + $stream->write("QUIT\r\n"); + }); +}, 'printf'); + +$loop->run(); diff --git a/instafeed/vendor/clue/http-proxy-react/examples/12-proxy-smtps.php b/instafeed/vendor/clue/http-proxy-react/examples/12-proxy-smtps.php new file mode 100755 index 0000000..462dba3 --- /dev/null +++ b/instafeed/vendor/clue/http-proxy-react/examples/12-proxy-smtps.php @@ -0,0 +1,35 @@ + $proxy, + 'timeout' => 3.0, + 'dns' => false +)); + +$connector->connect('tls://smtp.googlemail.com:465')->then(function (ConnectionInterface $stream) { + $stream->write("EHLO local\r\n"); + $stream->on('data', function ($chunk) use ($stream) { + echo $chunk; + $stream->write("QUIT\r\n"); + }); +}, 'printf'); + +$loop->run(); diff --git a/instafeed/vendor/clue/http-proxy-react/phpunit.xml.dist b/instafeed/vendor/clue/http-proxy-react/phpunit.xml.dist new file mode 100755 index 0000000..a6e2430 --- /dev/null +++ b/instafeed/vendor/clue/http-proxy-react/phpunit.xml.dist @@ -0,0 +1,14 @@ + + + + + + ./tests/ + + + + + ./src/ + + + diff --git a/instafeed/vendor/clue/http-proxy-react/src/ProxyConnector.php b/instafeed/vendor/clue/http-proxy-react/src/ProxyConnector.php new file mode 100755 index 0000000..69eb5ee --- /dev/null +++ b/instafeed/vendor/clue/http-proxy-react/src/ProxyConnector.php @@ -0,0 +1,213 @@ + [proxy] -> [destination] + * + * This is most frequently used to issue HTTPS requests to your destination. + * However, this is actually performed on a higher protocol layer and this + * connector is actually inherently a general-purpose plain TCP/IP connector. + * + * Note that HTTP CONNECT proxies often restrict which ports one may connect to. + * Many (public) proxy servers do in fact limit this to HTTPS (443) only. + * + * If you want to establish a TLS connection (such as HTTPS) between you and + * your destination, you may want to wrap this connector in a SecureConnector + * instance. + * + * Note that communication between the client and the proxy is usually via an + * unencrypted, plain TCP/IP HTTP connection. Note that this is the most common + * setup, because you can still establish a TLS connection between you and the + * destination host as above. + * + * If you want to connect to a (rather rare) HTTPS proxy, you may want use its + * HTTPS port (443) and use a SecureConnector instance to create a secure + * connection to the proxy. + * + * @link https://tools.ietf.org/html/rfc7231#section-4.3.6 + */ +class ProxyConnector implements ConnectorInterface +{ + private $connector; + private $proxyUri; + private $proxyAuth = ''; + + /** + * Instantiate a new ProxyConnector which uses the given $proxyUrl + * + * @param string $proxyUrl The proxy URL may or may not contain a scheme and + * port definition. The default port will be `80` for HTTP (or `443` for + * HTTPS), but many common HTTP proxy servers use custom ports. + * @param ConnectorInterface $connector In its most simple form, the given + * connector will be a \React\Socket\Connector if you want to connect to + * a given IP address. + * @throws InvalidArgumentException if the proxy URL is invalid + */ + public function __construct($proxyUrl, ConnectorInterface $connector) + { + // support `http+unix://` scheme for Unix domain socket (UDS) paths + if (preg_match('/^http\+unix:\/\/(.*?@)?(.+?)$/', $proxyUrl, $match)) { + // rewrite URI to parse authentication from dummy host + $proxyUrl = 'http://' . $match[1] . 'localhost'; + + // connector uses Unix transport scheme and explicit path given + $connector = new FixedUriConnector( + 'unix://' . $match[2], + $connector + ); + } + + if (strpos($proxyUrl, '://') === false) { + $proxyUrl = 'http://' . $proxyUrl; + } + + $parts = parse_url($proxyUrl); + if (!$parts || !isset($parts['scheme'], $parts['host']) || ($parts['scheme'] !== 'http' && $parts['scheme'] !== 'https')) { + throw new InvalidArgumentException('Invalid proxy URL "' . $proxyUrl . '"'); + } + + // apply default port and TCP/TLS transport for given scheme + if (!isset($parts['port'])) { + $parts['port'] = $parts['scheme'] === 'https' ? 443 : 80; + } + $parts['scheme'] = $parts['scheme'] === 'https' ? 'tls' : 'tcp'; + + $this->connector = $connector; + $this->proxyUri = $parts['scheme'] . '://' . $parts['host'] . ':' . $parts['port']; + + // prepare Proxy-Authorization header if URI contains username/password + if (isset($parts['user']) || isset($parts['pass'])) { + $this->proxyAuth = 'Proxy-Authorization: Basic ' . base64_encode( + rawurldecode($parts['user'] . ':' . (isset($parts['pass']) ? $parts['pass'] : '')) + ) . "\r\n"; + } + } + + public function connect($uri) + { + if (strpos($uri, '://') === false) { + $uri = 'tcp://' . $uri; + } + + $parts = parse_url($uri); + if (!$parts || !isset($parts['scheme'], $parts['host'], $parts['port']) || $parts['scheme'] !== 'tcp') { + return Promise\reject(new InvalidArgumentException('Invalid target URI specified')); + } + + $host = trim($parts['host'], '[]'); + $port = $parts['port']; + + // construct URI to HTTP CONNECT proxy server to connect to + $proxyUri = $this->proxyUri; + + // append path from URI if given + if (isset($parts['path'])) { + $proxyUri .= $parts['path']; + } + + // parse query args + $args = array(); + if (isset($parts['query'])) { + parse_str($parts['query'], $args); + } + + // append hostname from URI to query string unless explicitly given + if (!isset($args['hostname'])) { + $args['hostname'] = $parts['host']; + } + + // append query string + $proxyUri .= '?' . http_build_query($args, '', '&');; + + // append fragment from URI if given + if (isset($parts['fragment'])) { + $proxyUri .= '#' . $parts['fragment']; + } + + $auth = $this->proxyAuth; + + return $this->connector->connect($proxyUri)->then(function (ConnectionInterface $stream) use ($host, $port, $auth) { + $deferred = new Deferred(function ($_, $reject) use ($stream) { + $reject(new RuntimeException('Connection canceled while waiting for response from proxy (ECONNABORTED)', defined('SOCKET_ECONNABORTED') ? SOCKET_ECONNABORTED : 103)); + $stream->close(); + }); + + // keep buffering data until headers are complete + $buffer = ''; + $fn = function ($chunk) use (&$buffer, $deferred, $stream) { + $buffer .= $chunk; + + $pos = strpos($buffer, "\r\n\r\n"); + if ($pos !== false) { + // try to parse headers as response message + try { + $response = Psr7\parse_response(substr($buffer, 0, $pos)); + } catch (Exception $e) { + $deferred->reject(new RuntimeException('Invalid response received from proxy (EBADMSG)', defined('SOCKET_EBADMSG') ? SOCKET_EBADMSG: 71, $e)); + $stream->close(); + return; + } + + if ($response->getStatusCode() === 407) { + // map status code 407 (Proxy Authentication Required) to EACCES + $deferred->reject(new RuntimeException('Proxy denied connection due to invalid authentication ' . $response->getStatusCode() . ' (' . $response->getReasonPhrase() . ') (EACCES)', defined('SOCKET_EACCES') ? SOCKET_EACCES : 13)); + return $stream->close(); + } elseif ($response->getStatusCode() < 200 || $response->getStatusCode() >= 300) { + // map non-2xx status code to ECONNREFUSED + $deferred->reject(new RuntimeException('Proxy refused connection with HTTP error code ' . $response->getStatusCode() . ' (' . $response->getReasonPhrase() . ') (ECONNREFUSED)', defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111)); + return $stream->close(); + } + + // all okay, resolve with stream instance + $deferred->resolve($stream); + + // emit remaining incoming as data event + $buffer = (string)substr($buffer, $pos + 4); + if ($buffer !== '') { + $stream->emit('data', array($buffer)); + $buffer = ''; + } + return; + } + + // stop buffering when 8 KiB have been read + if (isset($buffer[8192])) { + $deferred->reject(new RuntimeException('Proxy must not send more than 8 KiB of headers (EMSGSIZE)', defined('SOCKET_EMSGSIZE') ? SOCKET_EMSGSIZE : 90)); + $stream->close(); + } + }; + $stream->on('data', $fn); + + $stream->on('error', function (Exception $e) use ($deferred) { + $deferred->reject(new RuntimeException('Stream error while waiting for response from proxy (EIO)', defined('SOCKET_EIO') ? SOCKET_EIO : 5, $e)); + }); + + $stream->on('close', function () use ($deferred) { + $deferred->reject(new RuntimeException('Connection to proxy lost while waiting for response (ECONNRESET)', defined('SOCKET_ECONNRESET') ? SOCKET_ECONNRESET : 104)); + }); + + $stream->write("CONNECT " . $host . ":" . $port . " HTTP/1.1\r\nHost: " . $host . ":" . $port . "\r\n" . $auth . "\r\n"); + + return $deferred->promise()->then(function (ConnectionInterface $stream) use ($fn) { + // Stop buffering when connection has been established. + $stream->removeListener('data', $fn); + return new Promise\FulfilledPromise($stream); + }); + }, function (Exception $e) use ($proxyUri) { + throw new RuntimeException('Unable to connect to proxy (ECONNREFUSED)', defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111, $e); + }); + } +} diff --git a/instafeed/vendor/clue/http-proxy-react/tests/AbstractTestCase.php b/instafeed/vendor/clue/http-proxy-react/tests/AbstractTestCase.php new file mode 100755 index 0000000..632b314 --- /dev/null +++ b/instafeed/vendor/clue/http-proxy-react/tests/AbstractTestCase.php @@ -0,0 +1,80 @@ +createCallableMock(); + $mock + ->expects($this->never()) + ->method('__invoke'); + + return $mock; + } + + protected function expectCallableOnce() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke'); + + return $mock; + } + + protected function expectCallableOnceWith($value) + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->equalTo($value)); + + return $mock; + } + + protected function expectCallableOnceWithExceptionCode($code) + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->callback(function ($e) use ($code) { + return $e->getCode() === $code; + })); + + return $mock; + } + + + protected function expectCallableOnceParameter($type) + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->isInstanceOf($type)); + + return $mock; + } + + /** + * @link https://github.com/reactphp/react/blob/master/tests/React/Tests/Socket/TestCase.php (taken from reactphp/react) + */ + protected function createCallableMock() + { + return $this->getMockBuilder('Tests\\Clue\\React\\HttpProxy\\CallableStub')->getMock(); + } +} + +class CallableStub +{ + public function __invoke() + { + } +} + diff --git a/instafeed/vendor/clue/http-proxy-react/tests/FunctionalTest.php b/instafeed/vendor/clue/http-proxy-react/tests/FunctionalTest.php new file mode 100755 index 0000000..23273cf --- /dev/null +++ b/instafeed/vendor/clue/http-proxy-react/tests/FunctionalTest.php @@ -0,0 +1,75 @@ +loop = Factory::create(); + + $this->tcpConnector = new TcpConnector($this->loop); + + $f = new \React\Dns\Resolver\Factory(); + $resolver = $f->create('8.8.8.8', $this->loop); + + $this->dnsConnector = new DnsConnector($this->tcpConnector, $resolver); + } + + public function testNonListeningSocketRejectsConnection() + { + $proxy = new ProxyConnector('127.0.0.1:9999', $this->dnsConnector); + + $promise = $proxy->connect('google.com:80'); + + $this->setExpectedException('RuntimeException', 'Unable to connect to proxy', SOCKET_ECONNREFUSED); + Block\await($promise, $this->loop, 3.0); + } + + public function testPlainGoogleDoesNotAcceptConnectMethod() + { + $proxy = new ProxyConnector('google.com', $this->dnsConnector); + + $promise = $proxy->connect('google.com:80'); + + $this->setExpectedException('RuntimeException', '405 (Method Not Allowed)', SOCKET_ECONNREFUSED); + Block\await($promise, $this->loop, 3.0); + } + + public function testSecureGoogleDoesNotAcceptConnectMethod() + { + if (!function_exists('stream_socket_enable_crypto')) { + $this->markTestSkipped('TLS not supported on really old platforms (HHVM < 3.8)'); + } + + $secure = new SecureConnector($this->dnsConnector, $this->loop); + $proxy = new ProxyConnector('https://google.com:443', $secure); + + $promise = $proxy->connect('google.com:80'); + + $this->setExpectedException('RuntimeException', '405 (Method Not Allowed)', SOCKET_ECONNREFUSED); + Block\await($promise, $this->loop, 3.0); + } + + public function testSecureGoogleDoesNotAcceptPlainStream() + { + $proxy = new ProxyConnector('google.com:443', $this->dnsConnector); + + $promise = $proxy->connect('google.com:80'); + + $this->setExpectedException('RuntimeException', 'Connection to proxy lost', SOCKET_ECONNRESET); + Block\await($promise, $this->loop, 3.0); + } +} diff --git a/instafeed/vendor/clue/http-proxy-react/tests/ProxyConnectorTest.php b/instafeed/vendor/clue/http-proxy-react/tests/ProxyConnectorTest.php new file mode 100755 index 0000000..13d6e42 --- /dev/null +++ b/instafeed/vendor/clue/http-proxy-react/tests/ProxyConnectorTest.php @@ -0,0 +1,333 @@ +connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock(); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testInvalidProxy() + { + new ProxyConnector('///', $this->connector); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testInvalidProxyScheme() + { + new ProxyConnector('ftp://example.com', $this->connector); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testInvalidHttpsUnixScheme() + { + new ProxyConnector('https+unix:///tmp/proxy.sock', $this->connector); + } + + public function testCreatesConnectionToHttpPort() + { + $promise = new Promise(function () { }); + $this->connector->expects($this->once())->method('connect')->with('tcp://proxy.example.com:80?hostname=google.com')->willReturn($promise); + + $proxy = new ProxyConnector('proxy.example.com', $this->connector); + + $proxy->connect('google.com:80'); + } + + public function testCreatesConnectionToHttpPortAndPassesThroughUriComponents() + { + $promise = new Promise(function () { }); + $this->connector->expects($this->once())->method('connect')->with('tcp://proxy.example.com:80/path?foo=bar&hostname=google.com#segment')->willReturn($promise); + + $proxy = new ProxyConnector('proxy.example.com', $this->connector); + + $proxy->connect('google.com:80/path?foo=bar#segment'); + } + + public function testCreatesConnectionToHttpPortAndObeysExplicitHostname() + { + $promise = new Promise(function () { }); + $this->connector->expects($this->once())->method('connect')->with('tcp://proxy.example.com:80?hostname=www.google.com')->willReturn($promise); + + $proxy = new ProxyConnector('proxy.example.com', $this->connector); + + $proxy->connect('google.com:80?hostname=www.google.com'); + } + + public function testCreatesConnectionToHttpsPort() + { + $promise = new Promise(function () { }); + $this->connector->expects($this->once())->method('connect')->with('tls://proxy.example.com:443?hostname=google.com')->willReturn($promise); + + $proxy = new ProxyConnector('https://proxy.example.com', $this->connector); + + $proxy->connect('google.com:80'); + } + + public function testCreatesConnectionToUnixPath() + { + $promise = new Promise(function () { }); + $this->connector->expects($this->once())->method('connect')->with('unix:///tmp/proxy.sock')->willReturn($promise); + + $proxy = new ProxyConnector('http+unix:///tmp/proxy.sock', $this->connector); + + $proxy->connect('google.com:80'); + } + + public function testCancelPromiseWillCancelPendingConnection() + { + $promise = new Promise(function () { }, $this->expectCallableOnce()); + $this->connector->expects($this->once())->method('connect')->willReturn($promise); + + $proxy = new ProxyConnector('proxy.example.com', $this->connector); + + $promise = $proxy->connect('google.com:80'); + + $this->assertInstanceOf('React\Promise\CancellablePromiseInterface', $promise); + + $promise->cancel(); + } + + public function testWillWriteToOpenConnection() + { + $stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('close', 'write'))->getMock(); + $stream->expects($this->once())->method('write')->with("CONNECT google.com:80 HTTP/1.1\r\nHost: google.com:80\r\n\r\n"); + + $promise = \React\Promise\resolve($stream); + $this->connector->expects($this->once())->method('connect')->willReturn($promise); + + $proxy = new ProxyConnector('proxy.example.com', $this->connector); + + $proxy->connect('google.com:80'); + } + + public function testWillProxyAuthorizationHeaderIfProxyUriContainsAuthentication() + { + $stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('close', 'write'))->getMock(); + $stream->expects($this->once())->method('write')->with("CONNECT google.com:80 HTTP/1.1\r\nHost: google.com:80\r\nProxy-Authorization: Basic dXNlcjpwYXNz\r\n\r\n"); + + $promise = \React\Promise\resolve($stream); + $this->connector->expects($this->once())->method('connect')->willReturn($promise); + + $proxy = new ProxyConnector('user:pass@proxy.example.com', $this->connector); + + $proxy->connect('google.com:80'); + } + + public function testWillProxyAuthorizationHeaderIfProxyUriContainsOnlyUsernameWithoutPassword() + { + $stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('close', 'write'))->getMock(); + $stream->expects($this->once())->method('write')->with("CONNECT google.com:80 HTTP/1.1\r\nHost: google.com:80\r\nProxy-Authorization: Basic dXNlcjo=\r\n\r\n"); + + $promise = \React\Promise\resolve($stream); + $this->connector->expects($this->once())->method('connect')->willReturn($promise); + + $proxy = new ProxyConnector('user@proxy.example.com', $this->connector); + + $proxy->connect('google.com:80'); + } + + public function testWillProxyAuthorizationHeaderIfProxyUriContainsAuthenticationWithPercentEncoding() + { + $user = 'h@llÖ'; + $pass = '%secret?'; + + $stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('close', 'write'))->getMock(); + $stream->expects($this->once())->method('write')->with("CONNECT google.com:80 HTTP/1.1\r\nHost: google.com:80\r\nProxy-Authorization: Basic " . base64_encode($user . ':' . $pass) . "\r\n\r\n"); + + $promise = \React\Promise\resolve($stream); + $this->connector->expects($this->once())->method('connect')->willReturn($promise); + + $proxy = new ProxyConnector(rawurlencode($user) . ':' . rawurlencode($pass) . '@proxy.example.com', $this->connector); + + $proxy->connect('google.com:80'); + } + + public function testWillProxyAuthorizationHeaderIfUnixProxyUriContainsAuthentication() + { + $stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('close', 'write'))->getMock(); + $stream->expects($this->once())->method('write')->with("CONNECT google.com:80 HTTP/1.1\r\nHost: google.com:80\r\nProxy-Authorization: Basic dXNlcjpwYXNz\r\n\r\n"); + + $promise = \React\Promise\resolve($stream); + $this->connector->expects($this->once())->method('connect')->with('unix:///tmp/proxy.sock')->willReturn($promise); + + $proxy = new ProxyConnector('http+unix://user:pass@/tmp/proxy.sock', $this->connector); + + $proxy->connect('google.com:80'); + } + + public function testRejectsInvalidUri() + { + $this->connector->expects($this->never())->method('connect'); + + $proxy = new ProxyConnector('proxy.example.com', $this->connector); + + $promise = $proxy->connect('///'); + + $promise->then(null, $this->expectCallableOnce()); + } + + public function testRejectsUriWithNonTcpScheme() + { + $this->connector->expects($this->never())->method('connect'); + + $proxy = new ProxyConnector('proxy.example.com', $this->connector); + + $promise = $proxy->connect('tls://google.com:80'); + + $promise->then(null, $this->expectCallableOnce()); + } + + public function testRejectsIfConnectorRejects() + { + $promise = \React\Promise\reject(new \RuntimeException()); + $this->connector->expects($this->once())->method('connect')->willReturn($promise); + + $proxy = new ProxyConnector('proxy.example.com', $this->connector); + + $promise = $proxy->connect('google.com:80'); + + $promise->then(null, $this->expectCallableOnce()); + } + + public function testRejectsAndClosesIfStreamWritesNonHttp() + { + $stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('close', 'write'))->getMock(); + + $promise = \React\Promise\resolve($stream); + $this->connector->expects($this->once())->method('connect')->willReturn($promise); + + $proxy = new ProxyConnector('proxy.example.com', $this->connector); + + $promise = $proxy->connect('google.com:80'); + + $stream->expects($this->once())->method('close'); + $stream->emit('data', array("invalid\r\n\r\n")); + + $promise->then(null, $this->expectCallableOnceWithExceptionCode(SOCKET_EBADMSG)); + } + + public function testRejectsAndClosesIfStreamWritesTooMuchData() + { + $stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('close', 'write'))->getMock(); + + $promise = \React\Promise\resolve($stream); + $this->connector->expects($this->once())->method('connect')->willReturn($promise); + + $proxy = new ProxyConnector('proxy.example.com', $this->connector); + + $promise = $proxy->connect('google.com:80'); + + $stream->expects($this->once())->method('close'); + $stream->emit('data', array(str_repeat('*', 100000))); + + $promise->then(null, $this->expectCallableOnceWithExceptionCode(SOCKET_EMSGSIZE)); + } + + public function testRejectsAndClosesIfStreamReturnsProyAuthenticationRequired() + { + $stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('close', 'write'))->getMock(); + + $promise = \React\Promise\resolve($stream); + $this->connector->expects($this->once())->method('connect')->willReturn($promise); + + $proxy = new ProxyConnector('proxy.example.com', $this->connector); + + $promise = $proxy->connect('google.com:80'); + + $stream->expects($this->once())->method('close'); + $stream->emit('data', array("HTTP/1.1 407 Proxy Authentication Required\r\n\r\n")); + + $promise->then(null, $this->expectCallableOnceWithExceptionCode(SOCKET_EACCES)); + } + + public function testRejectsAndClosesIfStreamReturnsNonSuccess() + { + $stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('close', 'write'))->getMock(); + + $promise = \React\Promise\resolve($stream); + $this->connector->expects($this->once())->method('connect')->willReturn($promise); + + $proxy = new ProxyConnector('proxy.example.com', $this->connector); + + $promise = $proxy->connect('google.com:80'); + + $stream->expects($this->once())->method('close'); + $stream->emit('data', array("HTTP/1.1 403 Not allowed\r\n\r\n")); + + $promise->then(null, $this->expectCallableOnceWithExceptionCode(SOCKET_ECONNREFUSED)); + } + + public function testResolvesIfStreamReturnsSuccess() + { + $stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('close', 'write'))->getMock(); + + $promise = \React\Promise\resolve($stream); + $this->connector->expects($this->once())->method('connect')->willReturn($promise); + + $proxy = new ProxyConnector('proxy.example.com', $this->connector); + + $promise = $proxy->connect('google.com:80'); + + $promise->then($this->expectCallableOnce('React\Stream\Stream')); + $never = $this->expectCallableNever(); + $promise->then(function (ConnectionInterface $stream) use ($never) { + $stream->on('data', $never); + }); + + $stream->emit('data', array("HTTP/1.1 200 OK\r\n\r\n")); + } + + public function testResolvesIfStreamReturnsSuccessAndEmitsExcessiveData() + { + $stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('close', 'write'))->getMock(); + + $promise = \React\Promise\resolve($stream); + $this->connector->expects($this->once())->method('connect')->willReturn($promise); + + $proxy = new ProxyConnector('proxy.example.com', $this->connector); + + $promise = $proxy->connect('google.com:80'); + + $once = $this->expectCallableOnceWith('hello!'); + $promise->then(function (ConnectionInterface $stream) use ($once) { + $stream->on('data', $once); + }); + + $stream->emit('data', array("HTTP/1.1 200 OK\r\n\r\nhello!")); + } + + public function testCancelPromiseWillCloseOpenConnectionAndReject() + { + $stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('close', 'write'))->getMock(); + $stream->expects($this->once())->method('close'); + + $promise = \React\Promise\resolve($stream); + $this->connector->expects($this->once())->method('connect')->willReturn($promise); + + $proxy = new ProxyConnector('proxy.example.com', $this->connector); + + $promise = $proxy->connect('google.com:80'); + + $this->assertInstanceOf('React\Promise\CancellablePromiseInterface', $promise); + + $promise->cancel(); + + $promise->then(null, $this->expectCallableOnceWithExceptionCode(SOCKET_ECONNABORTED)); + } +} diff --git a/instafeed/vendor/clue/socks-react/.gitignore b/instafeed/vendor/clue/socks-react/.gitignore new file mode 100755 index 0000000..de4a392 --- /dev/null +++ b/instafeed/vendor/clue/socks-react/.gitignore @@ -0,0 +1,2 @@ +/vendor +/composer.lock diff --git a/instafeed/vendor/clue/socks-react/.travis.yml b/instafeed/vendor/clue/socks-react/.travis.yml new file mode 100755 index 0000000..04f51ad --- /dev/null +++ b/instafeed/vendor/clue/socks-react/.travis.yml @@ -0,0 +1,27 @@ +language: php + +php: +# - 5.3 # requires old distro, see below + - 5.4 + - 5.5 + - 5.6 + - 7 + - hhvm # ignore errors, see below + +# lock distro so new future defaults will not break the build +dist: trusty + +matrix: + include: + - php: 5.3 + dist: precise + allow_failures: + - php: hhvm + +sudo: false + +install: + - composer install --no-interaction + +script: + - vendor/bin/phpunit --coverage-text diff --git a/instafeed/vendor/clue/socks-react/CHANGELOG.md b/instafeed/vendor/clue/socks-react/CHANGELOG.md new file mode 100755 index 0000000..c1eddb3 --- /dev/null +++ b/instafeed/vendor/clue/socks-react/CHANGELOG.md @@ -0,0 +1,338 @@ +# Changelog + +## 0.8.7 (2017-12-17) + +* Feature: Support SOCKS over TLS (`sockss://` URI scheme) + (#70 and #71 by @clue) + + ```php + // new: now supports SOCKS over TLS + $client = new Client('socks5s://localhost', $connector); + ``` + +* Feature: Support communication over Unix domain sockets (UDS) + (#69 by @clue) + + ```php + // new: now supports SOCKS over Unix domain sockets (UDS) + $client = new Client('socks5+unix:///tmp/proxy.sock', $connector); + ``` + +* Improve test suite by adding forward compatibility with PHPUnit 6 + (#68 by @clue) + +## 0.8.6 (2017-09-17) + +* Feature: Forward compatibility with Evenement v3.0 + (#67 by @WyriHaximus) + +## 0.8.5 (2017-09-01) + +* Feature: Use socket error codes for connection rejections + (#63 by @clue) + + ```php + $promise = $proxy->connect('imap.example.com:143'); + $promise->then(null, function (Exeption $e) { + if ($e->getCode() === SOCKET_EACCES) { + echo 'Failed to authenticate with proxy!'; + } + throw $e; + }); + ``` + +* Feature: Report matching SOCKS5 error codes for server side connection errors + (#62 by @clue) + +* Fix: Fix SOCKS5 client receiving destination hostnames and + fix IPv6 addresses as hostnames for TLS certificates + (#64 and #65 by @clue) + +* Improve test suite by locking Travis distro so new defaults will not break the build and + optionally exclude tests that rely on working internet connection + (#61 and #66 by @clue) + +## 0.8.4 (2017-07-27) + +* Feature: Server now passes client source address to Connector + (#60 by @clue) + +## 0.8.3 (2017-07-18) + +* Feature: Pass full remote URI as parameter to authentication callback + (#58 by @clue) + + ```php + // new third parameter passed to authentication callback + $server->setAuth(function ($user, $pass, $remote) { + $ip = parse_url($remote, PHP_URL_HOST); + + return ($ip === '127.0.0.1'); + }); + ``` + +* Fix: Fix connecting to IPv6 address via SOCKS5 server and validate target + URI so hostname can not contain excessive URI components + (#59 by @clue) + +* Improve test suite by fixing HHVM build for now again and ignore future HHVM build errors + (#57 by @clue) + +## 0.8.2 (2017-05-09) + +* Feature: Forward compatibility with upcoming Socket v1.0 and v0.8 + (#56 by @clue) + +## 0.8.1 (2017-04-21) + +* Update examples to use URIs with default port 1080 and accept proxy URI arguments + (#54 by @clue) + +* Remove now unneeded dependency on `react/stream` + (#55 by @clue) + +## 0.8.0 (2017-04-18) + +* Feature: Merge `Server` class from clue/socks-server + (#52 by @clue) + + ```php + $socket = new React\Socket\Server(1080, $loop); + $server = new Clue\React\Socks\Server($loop, $socket); + ``` + + > Upgrading from [clue/socks-server](https://github.com/clue/php-socks-server)? + The classes have been moved as-is, so you can simply start using the new + class name `Clue\React\Socks\Server` with no other changes required. + +## 0.7.0 (2017-04-14) + +* Feature / BC break: Replace depreacted SocketClient with Socket v0.7 and + use `connect($uri)` instead of `create($host, $port)` + (#51 by @clue) + + ```php + // old + $connector = new React\SocketClient\TcpConnector($loop); + $client = new Client(1080, $connector); + $client->create('google.com', 80)->then(function (Stream $conn) { + $conn->write("…"); + }); + + // new + $connector = new React\Socket\TcpConnector($loop); + $client = new Client(1080, $connector); + $client->connect('google.com:80')->then(function (ConnectionInterface $conn) { + $conn->write("…"); + }); + ``` + +* Improve test suite by adding PHPUnit to require-dev + (#50 by @clue) + +## 0.6.0 (2016-11-29) + +* Feature / BC break: Pass connector into `Client` instead of loop, remove unneeded deps + (#49 by @clue) + + ```php + // old (connector is create implicitly) + $client = new Client('127.0.0.1', $loop); + + // old (connector can optionally be passed) + $client = new Client('127.0.0.1', $loop, $connector); + + // new (connector is now mandatory) + $connector = new React\SocketClient\TcpConnector($loop); + $client = new Client('127.0.0.1', $connector); + ``` + +* Feature / BC break: `Client` now implements `ConnectorInterface`, remove `Connector` adapter + (#47 by @clue) + + ```php + // old (explicit connector functions as an adapter) + $connector = $client->createConnector(); + $promise = $connector->create('google.com', 80); + + // new (client can be used as connector right away) + $promise = $client->create('google.com', 80); + ``` + +* Feature / BC break: Remove `createSecureConnector()`, use `SecureConnector` instead + (#47 by @clue) + + ```php + // old (tight coupling and hidden dependency) + $tls = $client->createSecureConnector(); + $promise = $tls->create('google.com', 443); + + // new (more explicit, loose coupling) + $tls = new React\SocketClient\SecureConnector($client, $loop); + $promise = $tls->create('google.com', 443); + ``` + +* Feature / BC break: Remove `setResolveLocal()` and local DNS resolution and default to remote DNS resolution, use `DnsConnector` instead + (#44 by @clue) + + ```php + // old (implicitly defaults to true, can be disabled) + $client->setResolveLocal(false); + $tcp = $client->createConnector(); + $promise = $tcp->create('google.com', 80); + + // new (always disabled, can be re-enabled like this) + $factory = new React\Dns\Resolver\Factory(); + $resolver = $factory->createCached('8.8.8.8', $loop); + $tcp = new React\SocketClient\DnsConnector($client, $resolver); + $promise = $tcp->create('google.com', 80); + ``` + +* Feature / BC break: Remove `setTimeout()`, use `TimeoutConnector` instead + (#45 by @clue) + + ```php + // old (timeout only applies to TCP/IP connection) + $client = new Client('127.0.0.1', …); + $client->setTimeout(3.0); + $tcp = $client->createConnector(); + $promise = $tcp->create('google.com', 80); + + // new (timeout can be added to any layer) + $client = new Client('127.0.0.1', …); + $tcp = new React\SocketClient\TimeoutConnector($client, 3.0, $loop); + $promise = $tcp->create('google.com', 80); + ``` + +* Feature / BC break: Remove `setProtocolVersion()` and `setAuth()` mutators, only support SOCKS URI for protocol version and authentication (immutable API) + (#46 by @clue) + + ```php + // old (state can be mutated after instantiation) + $client = new Client('127.0.0.1', …); + $client->setProtocolVersion('5'); + $client->setAuth('user', 'pass'); + + // new (immutable after construction, already supported as of v0.5.2 - now mandatory) + $client = new Client('socks5://user:pass@127.0.0.1', …); + ``` + +## 0.5.2 (2016-11-25) + +* Feature: Apply protocol version and username/password auth from SOCKS URI + (#43 by @clue) + + ```php + // explicitly use SOCKS5 + $client = new Client('socks5://127.0.0.1', $loop); + + // use authentication (automatically SOCKS5) + $client = new Client('user:pass@127.0.0.1', $loop); + ``` + +* More explicit client examples, including proxy chaining + (#42 by @clue) + +## 0.5.1 (2016-11-21) + +* Feature: Support Promise cancellation + (#39 by @clue) + + ```php + $promise = $connector->create($host, $port); + + $promise->cancel(); + ``` + +* Feature: Timeout now cancels pending connection attempt + (#39, #22 by @clue) + +## 0.5.0 (2016-11-07) + +* Remove / BC break: Split off Server to clue/socks-server + (#35 by @clue) + + > Upgrading? Check [clue/socks-server](https://github.com/clue/php-socks-server) for details. + +* Improve documentation and project structure + +## 0.4.0 (2016-03-19) + +* Feature: Support proper SSL/TLS connections with additional SSL context options + (#31, #33 by @clue) + +* Documentation for advanced Connector setups (bindto, multihop) + (#32 by @clue) + +## 0.3.0 (2015-06-20) + +* BC break / Feature: Client ctor now accepts a SOCKS server URI + ([#24](https://github.com/clue/php-socks-react/pull/24)) + + ```php +// old +$client = new Client($loop, 'localhost', 9050); + +// new +$client = new Client('localhost:9050', $loop); +``` + +* Feature: Automatically assume default SOCKS port (1080) if not given explicitly + ([#26](https://github.com/clue/php-socks-react/pull/26)) + +* Improve documentation and test suite + +## 0.2.1 (2014-11-13) + +* Support React PHP v0.4 (while preserving BC with React PHP v0.3) + ([#16](https://github.com/clue/php-socks-react/pull/16)) + +* Improve examples and add first class support for HHVM + ([#15](https://github.com/clue/php-socks-react/pull/15) and [#17](https://github.com/clue/php-socks-react/pull/17)) + +## 0.2.0 (2014-09-27) + +* BC break / Feature: Simplify constructors by making parameters optional. + ([#10](https://github.com/clue/php-socks-react/pull/10)) + + The `Factory` has been removed, you can now create instances of the `Client` + and `Server` yourself: + + ```php + // old + $factory = new Factory($loop, $dns); + $client = $factory->createClient('localhost', 9050); + $server = $factory->createSever($socket); + + // new + $client = new Client($loop, 'localhost', 9050); + $server = new Server($loop, $socket); + ``` + +* BC break: Remove HTTP support and link to [clue/buzz-react](https://github.com/clue/php-buzz-react) instead. + ([#9](https://github.com/clue/php-socks-react/pull/9)) + + HTTP operates on a different layer than this low-level SOCKS library. + Removing this reduces the footprint of this library. + + > Upgrading? Check the [README](https://github.com/clue/php-socks-react#http-requests) for details. + +* Fix: Refactored to support other, faster loops (libev/libevent) + ([#12](https://github.com/clue/php-socks-react/pull/12)) + +* Explicitly list dependencies, clean up examples and extend test suite significantly + +## 0.1.0 (2014-05-19) + +* First stable release +* Async SOCKS `Client` and `Server` implementation +* Project was originally part of [clue/socks](https://github.com/clue/php-socks) + and was split off from its latest releave v0.4.0 + ([#1](https://github.com/clue/reactphp-socks/issues/1)) + +> Upgrading from clue/socks v0.4.0? Use namespace `Clue\React\Socks` instead of `Socks` and you're ready to go! + +## 0.0.0 (2011-04-26) + +* Initial concept, originally tracked as part of + [clue/socks](https://github.com/clue/php-socks) diff --git a/instafeed/vendor/clue/socks-react/LICENSE b/instafeed/vendor/clue/socks-react/LICENSE new file mode 100755 index 0000000..8efa9aa --- /dev/null +++ b/instafeed/vendor/clue/socks-react/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2011 Christian Lück + +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/instafeed/vendor/clue/socks-react/README.md b/instafeed/vendor/clue/socks-react/README.md new file mode 100755 index 0000000..a18d797 --- /dev/null +++ b/instafeed/vendor/clue/socks-react/README.md @@ -0,0 +1,1017 @@ +# clue/socks-react [![Build Status](https://travis-ci.org/clue/php-socks-react.svg?branch=master)](https://travis-ci.org/clue/php-socks-react) + +Async SOCKS4, SOCKS4a and SOCKS5 proxy client and server implementation, built on top of [ReactPHP](http://reactphp.org). + +The SOCKS protocol family can be used to easily tunnel TCP connections independent +of the actual application level protocol, such as HTTP, SMTP, IMAP, Telnet etc. + +**Table of contents** + +* [Quickstart example](#quickstart-example) +* [Usage](#usage) + * [ConnectorInterface](#connectorinterface) + * [connect()](#connect) + * [Client](#client) + * [Plain TCP connections](#plain-tcp-connections) + * [Secure TLS connections](#secure-tls-connections) + * [HTTP requests](#http-requests) + * [Protocol version](#protocol-version) + * [DNS resolution](#dns-resolution) + * [Authentication](#authentication) + * [Proxy chaining](#proxy-chaining) + * [Connection timeout](#connection-timeout) + * [SOCKS over TLS](#socks-over-tls) + * [Unix domain sockets](#unix-domain-sockets) + * [Server](#server) + * [Server connector](#server-connector) + * [Protocol version](#server-protocol-version) + * [Authentication](#server-authentication) + * [Proxy chaining](#server-proxy-chaining) + * [SOCKS over TLS](#server-socks-over-tls) + * [Unix domain sockets](#server-unix-domain-sockets) +* [Servers](#servers) + * [Using a PHP SOCKS server](#using-a-php-socks-server) + * [Using SSH as a SOCKS server](#using-ssh-as-a-socks-server) + * [Using the Tor (anonymity network) to tunnel SOCKS connections](#using-the-tor-anonymity-network-to-tunnel-socks-connections) +* [Install](#install) +* [Tests](#tests) +* [License](#license) +* [More](#more) + +## Quickstart example + +Once [installed](#install), you can use the following code to create a connection +to google.com via a local SOCKS proxy server: + +```php +$loop = React\EventLoop\Factory::create(); +$client = new Client('127.0.0.1:1080', new Connector($loop)); + +$client->connect('tcp://www.google.com:80')->then(function (ConnectionInterface $stream) { + $stream->write("GET / HTTP/1.0\r\n\r\n"); +}); + +$loop->run(); +``` + +If you're not already running any other [SOCKS proxy server](#servers), +you can use the following code to create a SOCKS +proxy server listening for connections on `localhost:1080`: + +```php +$loop = React\EventLoop\Factory::create(); + +// listen on localhost:1080 +$socket = new Socket('127.0.0.1:1080', $loop); + +// start a new server listening for incoming connection on the given socket +$server = new Server($loop, $socket); + +$loop->run(); +``` + +See also the [examples](examples). + +## Usage + +### ConnectorInterface + +The `ConnectorInterface` is responsible for providing an interface for +establishing streaming connections, such as a normal TCP/IP connection. + +In order to use this library, you should understand how this integrates with its +ecosystem. +This base interface is actually defined in React's +[Socket component](https://github.com/reactphp/socket) and used +throughout React's ecosystem. + +Most higher-level components (such as HTTP, database or other networking +service clients) accept an instance implementing this interface to create their +TCP/IP connection to the underlying networking service. +This is usually done via dependency injection, so it's fairly simple to actually +swap this implementation against this library in order to connect through a +SOCKS proxy server. + +The interface only offers a single method: + +#### connect() + +The `connect(string $uri): PromiseInterface` method +can be used to establish a streaming connection. +It returns a [Promise](https://github.com/reactphp/promise) which either +fulfills with a [ConnectionInterface](https://github.com/reactphp/socket#connectioninterface) or +rejects with an `Exception`: + +```php +$connector->connect('tcp://google.com:80')->then( + function (ConnectionInterface $stream) { + // connection successfully established + }, + function (Exception $error) { + // failed to connect due to $error + } +); +``` + +### Client + +The `Client` is responsible for communication with your SOCKS server instance. +Its constructor simply accepts an SOCKS proxy URI and a connector used to +connect to the SOCKS proxy server address. + +In its most simple form, you can simply pass React's +[`Connector`](https://github.com/reactphp/socket#connector) +like this: + +```php +$connector = new React\Socket\Connector($loop); +$client = new Client('127.0.0.1:1080', $connector); +``` + +You can omit the port if you're using the default SOCKS port 1080: + +```php +$client = new Client('127.0.0.1', $connector); +``` + +If you need custom connector settings (DNS resolution, timeouts etc.), you can explicitly pass a +custom instance of the [`ConnectorInterface`](https://github.com/reactphp/socket#connectorinterface): + +```php +// use local DNS server +$dnsResolverFactory = new DnsFactory(); +$resolver = $dnsResolverFactory->createCached('127.0.0.1', $loop); + +// outgoing connections to SOCKS server via interface 192.168.10.1 +// this is not to be confused with local DNS resolution (see further below) +$connector = new DnsConnector( + new TcpConnector($loop, array('bindto' => '192.168.10.1:0')), + $resolver +); + +$client = new Client('my-socks-server.local:1080', $connector); +``` + +This is the main class in this package. +Because it implements the the [`ConnectorInterface`](#connectorinterface), it +can simply be used in place of a normal connector. +This makes it fairly simple to add SOCKS proxy support to pretty much any +higher-level component: + +```diff +- $client = new SomeClient($connector); ++ $proxy = new Client('127.0.0.1:1080', $connector); ++ $client = new SomeClient($proxy); +``` + +#### Plain TCP connections + +The `Client` implements the [`ConnectorInterface`](#connectorinterface) and +hence provides a single public method, the [`connect()`](#connect) method. +Let's open up a streaming TCP/IP connection and write some data: + +```php +$client->connect('tcp://www.google.com:80')->then(function (ConnectonInterface $stream) { + echo 'connected to www.google.com:80'; + $stream->write("GET / HTTP/1.0\r\n\r\n"); + // ... +}); +``` + +You can either use the `Client` directly or you may want to wrap this connector +in React's [`Connector`](https://github.com/reactphp/socket#connector): + +```php +$connector = new React\Socket\Connector($loop, array( + 'tcp' => $client, + 'dns' => false +)); + +$connector->connect('tcp://www.google.com:80')->then(function (ConnectonInterface $stream) { + echo 'connected to www.google.com:80'; + $stream->write("GET / HTTP/1.0\r\n\r\n"); + // ... +}); + +``` + +See also the [first example](examples). + +The `tcp://` scheme can also be omitted. +Passing any other scheme will reject the promise. + +Pending connection attempts can be canceled by canceling its pending promise like so: + +```php +$promise = $connector->connect($uri); + +$promise->cancel(); +``` + +Calling `cancel()` on a pending promise will cancel the underlying TCP/IP +connection to the SOCKS server and/or the SOCKS protocol negotiation and reject +the resulting promise. + +#### Secure TLS connections + +If you want to establish a secure TLS connection (such as HTTPS) between you and +your destination, you may want to wrap this connector in React's +[`Connector`](https://github.com/reactphp/socket#connector) or the low-level +[`SecureConnector`](https://github.com/reactphp/socket#secureconnector): + +```php +$connector = new React\Socket\Connector($loop, array( + 'tcp' => $client, + 'dns' => false +)); + +// now create an SSL encrypted connection (notice the $ssl instead of $tcp) +$connector->connect('tls://www.google.com:443')->then(function (ConnectionInterface $stream) { + // proceed with just the plain text data + // everything is encrypted/decrypted automatically + echo 'connected to SSL encrypted www.google.com'; + $stream->write("GET / HTTP/1.0\r\n\r\n"); + // ... +}); +``` + +See also the [second example](examples). + +If you use the low-level `SecureConnector`, then the `tls://` scheme can also +be omitted. +Passing any other scheme will reject the promise. + +Pending connection attempts can be canceled by canceling its pending promise +as usual. + +> Also note how secure TLS connections are in fact entirely handled outside of + this SOCKS client implementation. + +You can optionally pass additional +[SSL context options](http://php.net/manual/en/context.ssl.php) +to the constructor like this: + +```php +$connector = new React\Socket\Connector($loop, array( + 'tcp' => $client, + 'tls' => array( + 'verify_peer' => false, + 'verify_peer_name' => false + ), + 'dns' => false +)); +``` + +#### HTTP requests + +HTTP operates on a higher layer than this low-level SOCKS implementation. +If you want to issue HTTP requests, you can add a dependency for +[clue/buzz-react](https://github.com/clue/php-buzz-react). +It can interact with this library by issuing all +[http requests through a SOCKS server](https://github.com/clue/php-buzz-react#socks-proxy). +This works for both plain HTTP and SSL encrypted HTTPS requests. + +#### Protocol version + +This library supports the SOCKS4, SOCKS4a and SOCKS5 protocol versions. + +While SOCKS4 already had (a somewhat limited) support for `SOCKS BIND` requests +and SOCKS5 added generic UDP support (`SOCKS UDPASSOCIATE`), this library +focuses on the most commonly used core feature of `SOCKS CONNECT`. +In this mode, a SOCKS server acts as a generic proxy allowing higher level +application protocols to work through it. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
SOCKS4SOCKS4aSOCKS5
Protocol specificationSOCKS4.protocolSOCKS4A.protocolRFC 1928
Tunnel outgoing TCP connections
Remote DNS resolving
IPv6 addresses
Username/Password authentication✓ (as per RFC 1929)
Handshake # roundtrips112 (3 with authentication)
Handshake traffic
+ remote DNS
17 bytes
17 bytes
+ hostname + 1
variable (+ auth + IPv6)
+ hostname - 3
+ +Note, this is __not__ a full SOCKS5 implementation due to missing GSSAPI +authentication (but it's unlikely you're going to miss it anyway). + +By default, the `Client` communicates via SOCKS4a with the SOCKS server +– unless you enable [authentication](#authentication), in which case it will +default to SOCKS5. +This is done because SOCKS4a incurs less overhead than SOCKS5 (see above) and +is equivalent with SOCKS4 if you use [local DNS resolution](#dns-resolution). + +If want to explicitly set the protocol version, use the supported values URI +schemes `socks4`, `socks4a` or `socks5` as part of the SOCKS URI: + +```php +$client = new Client('socks5://127.0.0.1', $connector); +``` + +As seen above, both SOCKS5 and SOCKS4a support remote and local DNS resolution. +If you've explicitly set this to SOCKS4, then you may want to check the following +chapter about local DNS resolution or you may only connect to IPv4 addresses. + +#### DNS resolution + +By default, the `Client` does not perform any DNS resolution at all and simply +forwards any hostname you're trying to connect to to the SOCKS server. +The remote SOCKS server is thus responsible for looking up any hostnames via DNS +(this default mode is thus called *remote DNS resolution*). +As seen above, this mode is supported by the SOCKS5 and SOCKS4a protocols, but +not the SOCKS4 protocol, as the protocol lacks a way to communicate hostnames. + +On the other hand, all SOCKS protocol versions support sending destination IP +addresses to the SOCKS server. +In this mode you either have to stick to using IPs only (which is ofen unfeasable) +or perform any DNS lookups locally and only transmit the resolved destination IPs +(this mode is thus called *local DNS resolution*). + +The default *remote DNS resolution* is useful if your local `Client` either can +not resolve target hostnames because it has no direct access to the internet or +if it should not resolve target hostnames because its outgoing DNS traffic might +be intercepted (in particular when using the +[Tor network](#using-the-tor-anonymity-network-to-tunnel-socks-connections)). + +As noted above, the `Client` defaults to using remote DNS resolution. +However, wrapping the `Client` in React's +[`Connector`](https://github.com/reactphp/socket#connector) actually +performs local DNS resolution unless explicitly defined otherwise. +Given that remote DNS resolution is assumed to be the preferred mode, all +other examples explicitly disable DNS resoltion like this: + +```php +$connector = new React\Socket\Connector($loop, array( + 'tcp' => $client, + 'dns' => false +)); +``` + +If you want to explicitly use *local DNS resolution* (such as when explicitly +using SOCKS4), you can use the following code: + +```php +// set up Connector which uses Google's public DNS (8.8.8.8) +$connector = new React\Socket\Connector($loop, array( + 'tcp' => $client, + 'dns' => '8.8.8.8' +)); +``` + +See also the [fourth example](examples). + +Pending connection attempts can be canceled by canceling its pending promise +as usual. + +> Also note how local DNS resolution is in fact entirely handled outside of this + SOCKS client implementation. + +If you've explicitly set the client to SOCKS4 and stick to the default +*remote DNS resolution*, then you may only connect to IPv4 addresses because +the protocol lacks a way to communicate hostnames. +If you try to connect to a hostname despite, the resulting promise will be +rejected right away. + +#### Authentication + +This library supports username/password authentication for SOCKS5 servers as +defined in [RFC 1929](http://tools.ietf.org/html/rfc1929). + +On the client side, simply pass your username and password to use for +authentication (see below). +For each further connection the client will merely send a flag to the server +indicating authentication information is available. +Only if the server requests authentication during the initial handshake, +the actual authentication credentials will be transmitted to the server. + +Note that the password is transmitted in cleartext to the SOCKS proxy server, +so this methods should not be used on a network where you have to worry about eavesdropping. + +You can simply pass the authentication information as part of the SOCKS URI: + +```php +$client = new Client('username:password@127.0.0.1', $connector); +``` + +Note that both the username and password must be percent-encoded if they contain +special characters: + +```php +$user = 'he:llo'; +$pass = 'p@ss'; + +$client = new Client( + rawurlencode($user) . ':' . rawurlencode($pass) . '@127.0.0.1', + $connector +); +``` + +> The authentication details will be transmitted in cleartext to the SOCKS proxy + server only if it requires username/password authentication. + If the authentication details are missing or not accepted by the remote SOCKS + proxy server, it is expected to reject each connection attempt with an + exception error code of `SOCKET_EACCES` (13). + +Authentication is only supported by protocol version 5 (SOCKS5), +so passing authentication to the `Client` enforces communication with protocol +version 5 and complains if you have explicitly set anything else: + +```php +// throws InvalidArgumentException +new Client('socks4://user:pass@127.0.0.1', $connector); +``` + +#### Proxy chaining + +The `Client` is responsible for creating connections to the SOCKS server which +then connects to the target host. + +``` +Client -> SocksServer -> TargetHost +``` + +Sometimes it may be required to establish outgoing connections via another SOCKS +server. +For example, this can be useful if you want to conceal your origin address. + +``` +Client -> MiddlemanSocksServer -> TargetSocksServer -> TargetHost +``` + +The `Client` uses any instance of the `ConnectorInterface` to establish +outgoing connections. +In order to connect through another SOCKS server, you can simply use another +SOCKS connector from another SOCKS client like this: + +```php +// https via the proxy chain "MiddlemanSocksServer -> TargetSocksServer -> TargetHost" +// please note how the client uses TargetSocksServer (not MiddlemanSocksServer!), +// which in turn then uses MiddlemanSocksServer. +// this creates a TCP/IP connection to MiddlemanSocksServer, which then connects +// to TargetSocksServer, which then connects to the TargetHost +$middle = new Client('127.0.0.1:1080', new Connector($loop)); +$target = new Client('example.com:1080', $middle); + +$connector = new React\Socket\Connector($loop, array( + 'tcp' => $target, + 'dns' => false +)); + +$connector->connect('tls://www.google.com:443')->then(function ($stream) { + // … +}); +``` + +See also the [third example](examples). + +Pending connection attempts can be canceled by canceling its pending promise +as usual. + +Proxy chaining can happen on the server side and/or the client side: + +* If you ask your client to chain through multiple proxies, then each proxy + server does not really know anything about chaining at all. + This means that this is a client-only property. + +* If you ask your server to chain through another proxy, then your client does + not really know anything about chaining at all. + This means that this is a server-only property and not part of this class. + For example, you can find this in the below [`Server`](#server-proxy-chaining) + class or somewhat similar when you're using the + [Tor network](#using-the-tor-anonymity-network-to-tunnel-socks-connections). + +#### Connection timeout + +By default, the `Client` does not implement any timeouts for establishing remote +connections. +Your underlying operating system may impose limits on pending and/or idle TCP/IP +connections, anywhere in a range of a few minutes to several hours. + +Many use cases require more control over the timeout and likely values much +smaller, usually in the range of a few seconds only. + +You can use React's [`Connector`](https://github.com/reactphp/socket#connector) +or the low-level +[`TimeoutConnector`](https://github.com/reactphp/socket#timeoutconnector) +to decorate any given `ConnectorInterface` instance. +It provides the same `connect()` method, but will automatically reject the +underlying connection attempt if it takes too long: + +```php +$connector = new Connector($loop, array( + 'tcp' => $client, + 'dns' => false, + 'timeout' => 3.0 +)); + +$connector->connect('tcp://google.com:80')->then(function ($stream) { + // connection succeeded within 3.0 seconds +}); +``` + +See also any of the [examples](examples). + +Pending connection attempts can be canceled by canceling its pending promise +as usual. + +> Also note how connection timeout is in fact entirely handled outside of this + SOCKS client implementation. + +#### SOCKS over TLS + +All [SOCKS protocol versions](#protocol-version) support forwarding TCP/IP +based connections and higher level protocols. +This implies that you can also use [secure TLS connections](#secure-tls-connections) +to transfer sensitive data across SOCKS proxy servers. +This means that no eavesdropper nor the proxy server will be able to decrypt +your data. + +However, the initial SOCKS communication between the client and the proxy is +usually via an unencrypted, plain TCP/IP connection. +This means that an eavesdropper may be able to see *where* you connect to and +may also be able to see your [SOCKS authentication](#authentication) details +in cleartext. + +As an alternative, you may establish a secure TLS connection to your SOCKS +proxy before starting the initial SOCKS communication. +This means that no eavesdroppper will be able to see the destination address +you want to connect to or your [SOCKS authentication](#authentication) details. + +You can use the `sockss://` URI scheme or use an explicit +[SOCKS protocol version](#protocol-version) like this: + +```php +$client = new Client('sockss://127.0.0.1:1080', new Connector($loop)); + +$client = new Client('socks5s://127.0.0.1:1080', new Connector($loop)); +``` + +See also [example 32](examples). + +Simiarly, you can also combine this with [authentication](#authentication) +like this: + +```php +$client = new Client('sockss://user:pass@127.0.0.1:1080', new Connector($loop)); +``` + +> Note that for most use cases, [secure TLS connections](#secure-tls-connections) + should be used instead. SOCKS over TLS is considered advanced usage and is + used very rarely in practice. + In particular, the SOCKS server has to accept secure TLS connections, see + also [Server SOCKS over TLS](#server-socks-over-tls) for more details. + Also, PHP does not support "double encryption" over a single connection. + This means that enabling [secure TLS connections](#secure-tls-connections) + over a communication channel that has been opened with SOCKS over TLS + may not be supported. + +> Note that the SOCKS protocol does not support the notion of TLS. The above + works reasonably well because TLS is only used for the connection between + client and proxy server and the SOCKS protocol data is otherwise identical. + This implies that this may also have only limited support for + [proxy chaining](#proxy-chaining) over multiple TLS paths. + +#### Unix domain sockets + +All [SOCKS protocol versions](#protocol-version) support forwarding TCP/IP +based connections and higher level protocols. +In some advanced cases, it may be useful to let your SOCKS server listen on a +Unix domain socket (UDS) path instead of a IP:port combination. +For example, this allows you to rely on file system permissions instead of +having to rely on explicit [authentication](#authentication). + +You can use the `socks+unix://` URI scheme or use an explicit +[SOCKS protocol version](#protocol-version) like this: + +```php +$client = new Client('socks+unix:///tmp/proxy.sock', new Connector($loop)); + +$client = new Client('socks5+unix:///tmp/proxy.sock', new Connector($loop)); +``` + +Simiarly, you can also combine this with [authentication](#authentication) +like this: + +```php +$client = new Client('socks+unix://user:pass@/tmp/proxy.sock', new Connector($loop)); +``` + +> Note that Unix domain sockets (UDS) are considered advanced usage and PHP only + has limited support for this. + In particular, enabling [secure TLS](#secure-tls-connections) may not be + supported. + +> Note that SOCKS protocol does not support the notion of UDS paths. The above + works reasonably well because UDS is only used for the connection between + client and proxy server and the path will not actually passed over the protocol. + This implies that this does also not support [proxy chaining](#proxy-chaining) + over multiple UDS paths. + +### Server + +The `Server` is responsible for accepting incoming communication from SOCKS clients +and forwarding the requested connection to the target host. +It also registers everything with the main [`EventLoop`](https://github.com/reactphp/event-loop#usage) +and an underlying TCP/IP socket server like this: + +```php +$loop = \React\EventLoop\Factory::create(); + +// listen on localhost:$port +$socket = new Socket($port, $loop); + +$server = new Server($loop, $socket); +``` + +#### Server connector + +The `Server` uses an instance of the [`ConnectorInterface`](#connectorinterface) +to establish outgoing connections for each incoming connection request. + +If you need custom connector settings (DNS resolution, timeouts etc.), you can explicitly pass a +custom instance of the [`ConnectorInterface`](https://github.com/reactphp/socket#connectorinterface): + +```php +// use local DNS server +$dnsResolverFactory = new DnsFactory(); +$resolver = $dnsResolverFactory->createCached('127.0.0.1', $loop); + +// outgoing connections to target host via interface 192.168.10.1 +$connector = new DnsConnector( + new TcpConnector($loop, array('bindto' => '192.168.10.1:0')), + $resolver +); + +$server = new Server($loop, $socket, $connector); +``` + +If you want to forward the outgoing connection through another SOCKS proxy, you +may also pass a [`Client`](#client) instance as a connector, see also +[server proxy chaining](#server-proxy-chaining) for more details. + +Internally, the `Server` uses the normal [`connect()`](#connect) method, but +it also passes the original client IP as the `?source={remote}` parameter. +The `source` parameter contains the full remote URI, including the protocol +and any authentication details, for example `socks5://user:pass@1.2.3.4:5678`. +You can use this parameter for logging purposes or to restrict connection +requests for certain clients by providing a custom implementation of the +[`ConnectorInterface`](#connectorinterface). + +#### Server protocol version + +The `Server` supports all protocol versions (SOCKS4, SOCKS4a and SOCKS5) by default. + +If want to explicitly set the protocol version, use the supported values `4`, `4a` or `5`: + +```PHP +$server->setProtocolVersion(5); +``` + +In order to reset the protocol version to its default (i.e. automatic detection), +use `null` as protocol version. + +```PHP +$server->setProtocolVersion(null); +``` + +#### Server authentication + +By default, the `Server` does not require any authentication from the clients. +You can enable authentication support so that clients need to pass a valid +username and password before forwarding any connections. + +Setting authentication on the `Server` enforces each further connected client +to use protocol version 5 (SOCKS5). +If a client tries to use any other protocol version, does not send along +authentication details or if authentication details can not be verified, +the connection will be rejected. + +Because your authentication mechanism might take some time to actually check +the provided authentication credentials (like querying a remote database or webservice), +the server side uses a [Promise](https://github.com/reactphp/promise) based interface. +While this might seem complex at first, it actually provides a very simple way +to handle simultanous connections in a non-blocking fashion and increases overall performance. + +```PHP +$server->setAuth(function ($username, $password, $remote) { + // either return a boolean success value right away + // or use promises for delayed authentication + + // $remote is a full URI à la socks5://user:pass@192.168.1.1:1234 + // or socks5s://user:pass@192.168.1.1:1234 for SOCKS over TLS + // useful for logging or extracting parts, such as the remote IP + $ip = parse_url($remote, PHP_URL_HOST); + + return ($username === 'root' && $ip === '127.0.0.1'); +}); +``` + +Or if you only accept static authentication details, you can use the simple +array-based authentication method as a shortcut: + +```PHP +$server->setAuthArray(array( + 'tom' => 'password', + 'admin' => 'root' +)); +``` + +See also [example #12](examples). + +If you do not want to use authentication anymore: + +```PHP +$server->unsetAuth(); +``` + +#### Server proxy chaining + +The `Server` is responsible for creating connections to the target host. + +``` +Client -> SocksServer -> TargetHost +``` + +Sometimes it may be required to establish outgoing connections via another SOCKS +server. +For example, this can be useful if your target SOCKS server requires +authentication, but your client does not support sending authentication +information (e.g. like most webbrowser). + +``` +Client -> MiddlemanSocksServer -> TargetSocksServer -> TargetHost +``` + +The `Server` uses any instance of the `ConnectorInterface` to establish outgoing +connections. +In order to connect through another SOCKS server, you can simply use the +[`Client`](#client) SOCKS connector from above. +You can create a SOCKS `Client` instance like this: + +```php +// set next SOCKS server example.com:1080 as target +$connector = new React\Socket\Connector($loop); +$client = new Client('user:pass@example.com:1080', $connector); + +// listen on localhost:1080 +$socket = new Socket('127.0.0.1:1080', $loop); + +// start a new server which forwards all connections to the other SOCKS server +$server = new Server($loop, $socket, $client); +``` + +See also [example #21](examples). + +Proxy chaining can happen on the server side and/or the client side: + +* If you ask your client to chain through multiple proxies, then each proxy + server does not really know anything about chaining at all. + This means that this is a client-only property and not part of this class. + For example, you can find this in the above [`Client`](#proxy-chaining) class. + +* If you ask your server to chain through another proxy, then your client does + not really know anything about chaining at all. + This means that this is a server-only property and can be implemented as above. + +#### Server SOCKS over TLS + +All [SOCKS protocol versions](#server-protocol-version) support forwarding TCP/IP +based connections and higher level protocols. +This implies that you can also use [secure TLS connections](#secure-tls-connections) +to transfer sensitive data across SOCKS proxy servers. +This means that no eavesdropper nor the proxy server will be able to decrypt +your data. + +However, the initial SOCKS communication between the client and the proxy is +usually via an unencrypted, plain TCP/IP connection. +This means that an eavesdropper may be able to see *where* the client connects +to and may also be able to see the [SOCKS authentication](#authentication) +details in cleartext. + +As an alternative, you may listen for SOCKS over TLS connections so +that the client has to establish a secure TLS connection to your SOCKS +proxy before starting the initial SOCKS communication. +This means that no eavesdroppper will be able to see the destination address +the client wants to connect to or their [SOCKS authentication](#authentication) +details. + +You can simply start your listening socket on the `tls://` URI scheme like this: + +```php +$loop = \React\EventLoop\Factory::create(); + +// listen on tls://127.0.0.1:1080 with the given server certificate +$socket = new React\Socket\Server('tls://127.0.0.1:1080', $loop, array( + 'tls' => array( + 'local_cert' => __DIR__ . '/localhost.pem', + ) +)); +$server = new Server($loop, $socket); +``` + +See also [example 31](examples). + +> Note that for most use cases, [secure TLS connections](#secure-tls-connections) + should be used instead. SOCKS over TLS is considered advanced usage and is + used very rarely in practice. + +> Note that the SOCKS protocol does not support the notion of TLS. The above + works reasonably well because TLS is only used for the connection between + client and proxy server and the SOCKS protocol data is otherwise identical. + This implies that this does also not support [proxy chaining](#server-proxy-chaining) + over multiple TLS paths. + +#### Server Unix domain sockets + +All [SOCKS protocol versions](#server-protocol-version) support forwarding TCP/IP +based connections and higher level protocols. +In some advanced cases, it may be useful to let your SOCKS server listen on a +Unix domain socket (UDS) path instead of a IP:port combination. +For example, this allows you to rely on file system permissions instead of +having to rely on explicit [authentication](#server-authentication). + +You can simply start your listening socket on the `unix://` URI scheme like this: + +```php +$loop = \React\EventLoop\Factory::create(); + +// listen on /tmp/proxy.sock +$socket = new React\Socket\Server('unix:///tmp/proxy.sock', $loop); +$server = new Server($loop, $socket); +``` + +> Note that Unix domain sockets (UDS) are considered advanced usage and that + the SOCKS protocol does not support the notion of UDS paths. The above + works reasonably well because UDS is only used for the connection between + client and proxy server and the path will not actually passed over the protocol. + This implies that this does also not support [proxy chaining](#server-proxy-chaining) + over multiple UDS paths. + +## Servers + +### Using a PHP SOCKS server + +* If you're looking for an end-user SOCKS server daemon, you may want to use + [LeProxy](https://leproxy.org/) or [clue/psocksd](https://github.com/clue/psocksd). +* If you're looking for a SOCKS server implementation, consider using + the above [`Server`](#server) class. + +### Using SSH as a SOCKS server + +If you already have an SSH server set up, you can easily use it as a SOCKS +tunnel end point. On your client, simply start your SSH client and use +the `-D ` option to start a local SOCKS server (quoting the man page: +a `local "dynamic" application-level port forwarding`). + +You can start a local SOCKS server by creating a loopback connection to your +local system if you already run an SSH daemon: + +```bash +$ ssh -D 1080 localhost +``` + +Alternatively, you can start a local SOCKS server tunneling through a given +remote host that runs an SSH daemon: + +```bash +$ ssh -D 1080 example.com +``` + +Now you can simply use this SSH SOCKS server like this: + +```PHP +$client = new Client('127.0.0.1:1080', $connector); +``` + +Note that the above will allow all users on the local system to connect over +your SOCKS server without authentication which may or may not be what you need. +As an alternative, recent OpenSSH client versions also support +[Unix domain sockets](#unix-domain-sockets) (UDS) paths so that you can rely +on Unix file system permissions instead: + +```bash +$ ssh -D/tmp/proxy.sock example.com +``` + +Now you can simply use this SSH SOCKS server like this: + +```PHP +$client = new Client('socks+unix:///tmp/proxy.sock', $connector); +``` + +### Using the Tor (anonymity network) to tunnel SOCKS connections + +The [Tor anonymity network](http://www.torproject.org) client software is designed +to encrypt your traffic and route it over a network of several nodes to conceal its origin. +It presents a SOCKS4 and SOCKS5 interface on TCP port 9050 by default +which allows you to tunnel any traffic through the anonymity network. +In most scenarios you probably don't want your client to resolve the target hostnames, +because you would leak DNS information to anybody observing your local traffic. +Also, Tor provides hidden services through an `.onion` pseudo top-level domain +which have to be resolved by Tor. + +```PHP +$client = new Client('127.0.0.1:9050', $connector); +``` + +## Install + +The recommended way to install this library is [through Composer](https://getcomposer.org). +[New to Composer?](https://getcomposer.org/doc/00-intro.md) + +This will install the latest supported version: + +```bash +$ composer require clue/socks-react:^0.8.7 +``` + +See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades. + +## Tests + +To run the test suite, you first need to clone this repo and then install all +dependencies [through Composer](https://getcomposer.org): + +```bash +$ composer install +``` + +To run the test suite, go to the project root and run: + +```bash +$ php vendor/bin/phpunit +``` + +The test suite contains a number of tests that rely on a working internet +connection, alternatively you can also run it like this: + +```bash +$ php vendor/bin/phpunit --exclude-group internet +``` + +## License + +MIT, see LICENSE + +## More + +* If you want to learn more about processing streams of data, refer to the + documentation of the underlying + [react/stream](https://github.com/reactphp/stream) component. +* If you want to learn more about how the + [`ConnectorInterface`](#connectorinterface) and its usual implementations look + like, refer to the documentation of the underlying + [react/socket component](https://github.com/reactphp/socket). +* As an alternative to a SOCKS (SOCKS4/SOCKS5) proxy, you may also want to look into + using an HTTP CONNECT proxy instead. + You may want to use [clue/http-proxy-react](https://github.com/clue/php-http-proxy-react) + which also provides an implementation of the + [`ConnectorInterface`](#connectorinterface) so that supporting either proxy + protocol should be fairly trivial. +* If you're dealing with public proxies, you'll likely have to work with mixed + quality and unreliable proxies. You may want to look into using + [clue/connection-manager-extra](https://github.com/clue/php-connection-manager-extra) + which allows retrying unreliable ones, implying connection timeouts, + concurrently working with multiple connectors and more. +* If you're looking for an end-user SOCKS server daemon, you may want to use + [LeProxy](https://leproxy.org/) or [clue/psocksd](https://github.com/clue/psocksd). diff --git a/instafeed/vendor/clue/socks-react/composer.json b/instafeed/vendor/clue/socks-react/composer.json new file mode 100755 index 0000000..50ef83a --- /dev/null +++ b/instafeed/vendor/clue/socks-react/composer.json @@ -0,0 +1,28 @@ +{ + "name": "clue/socks-react", + "description": "Async SOCKS4, SOCKS4a and SOCKS5 proxy client and server implementation, built on top of ReactPHP", + "keywords": ["socks client", "socks server", "proxy", "tcp tunnel", "socks protocol", "async", "ReactPHP"], + "homepage": "https://github.com/clue/php-socks-react", + "license": "MIT", + "authors": [ + { + "name": "Christian Lück", + "email": "christian@lueck.tv" + } + ], + "autoload": { + "psr-4": {"Clue\\React\\Socks\\": "src/"} + }, + "require": { + "php": ">=5.3", + "react/socket": "^1.0 || ^0.8.6", + "react/promise": "^2.1 || ^1.2", + "evenement/evenement": "~3.0|~1.0|~2.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0 || ^5.7 || ^4.8.35", + "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3", + "clue/connection-manager-extra": "^1.0 || ^0.7", + "clue/block-react": "^1.1" + } +} diff --git a/instafeed/vendor/clue/socks-react/examples/01-http.php b/instafeed/vendor/clue/socks-react/examples/01-http.php new file mode 100755 index 0000000..584b6c4 --- /dev/null +++ b/instafeed/vendor/clue/socks-react/examples/01-http.php @@ -0,0 +1,30 @@ + $client, + 'timeout' => 3.0, + 'dns' => false +)); + +echo 'Demo SOCKS client connecting to SOCKS server ' . $proxy . PHP_EOL; + +$connector->connect('tcp://www.google.com:80')->then(function (ConnectionInterface $stream) { + echo 'connected' . PHP_EOL; + $stream->write("GET / HTTP/1.0\r\n\r\n"); + $stream->on('data', function ($data) { + echo $data; + }); +}, 'printf'); + +$loop->run(); diff --git a/instafeed/vendor/clue/socks-react/examples/02-https.php b/instafeed/vendor/clue/socks-react/examples/02-https.php new file mode 100755 index 0000000..f763fc2 --- /dev/null +++ b/instafeed/vendor/clue/socks-react/examples/02-https.php @@ -0,0 +1,30 @@ + $client, + 'timeout' => 3.0, + 'dns' => false +)); + +echo 'Demo SOCKS client connecting to SOCKS server ' . $proxy . PHP_EOL; + +$connector->connect('tls://www.google.com:443')->then(function (ConnectionInterface $stream) { + echo 'connected' . PHP_EOL; + $stream->write("GET / HTTP/1.0\r\n\r\n"); + $stream->on('data', function ($data) { + echo $data; + }); +}, 'printf'); + +$loop->run(); diff --git a/instafeed/vendor/clue/socks-react/examples/03-proxy-chaining.php b/instafeed/vendor/clue/socks-react/examples/03-proxy-chaining.php new file mode 100755 index 0000000..5278d2c --- /dev/null +++ b/instafeed/vendor/clue/socks-react/examples/03-proxy-chaining.php @@ -0,0 +1,46 @@ + [...]' . PHP_EOL; + echo 'You can add 1..n proxies in the path' . PHP_EOL; + exit(1); +} + +$path = array_slice($argv, 1); + +// Alternatively, you can also hard-code this value like this: +//$path = array('127.0.0.1:9051', '127.0.0.1:9052', '127.0.0.1:9053'); + +$loop = React\EventLoop\Factory::create(); + +// set next SOCKS server chain via p1 -> p2 -> p3 -> destination +$connector = new Connector($loop); +foreach ($path as $proxy) { + $connector = new Client($proxy, $connector); +} + +// please note how the client uses p3 (not p1!), which in turn then uses the complete chain +// this creates a TCP/IP connection to p1, which then connects to p2, then to p3, which then connects to the target +$connector = new Connector($loop, array( + 'tcp' => $connector, + 'timeout' => 3.0, + 'dns' => false +)); + +echo 'Demo SOCKS client connecting to SOCKS proxy server chain ' . implode(' -> ', $path) . PHP_EOL; + +$connector->connect('tls://www.google.com:443')->then(function (ConnectionInterface $stream) { + echo 'connected' . PHP_EOL; + $stream->write("GET / HTTP/1.0\r\n\r\n"); + $stream->on('data', function ($data) { + echo $data; + }); +}, 'printf'); + +$loop->run(); diff --git a/instafeed/vendor/clue/socks-react/examples/04-local-dns.php b/instafeed/vendor/clue/socks-react/examples/04-local-dns.php new file mode 100755 index 0000000..2ea39dd --- /dev/null +++ b/instafeed/vendor/clue/socks-react/examples/04-local-dns.php @@ -0,0 +1,31 @@ + $client, + 'timeout' => 3.0, + 'dns' => '8.8.8.8' +)); + +echo 'Demo SOCKS client connecting to SOCKS server ' . $proxy . PHP_EOL; + +$connector->connect('tls://www.google.com:443')->then(function (ConnectionInterface $stream) { + echo 'connected' . PHP_EOL; + $stream->write("GET / HTTP/1.0\r\n\r\n"); + $stream->on('data', function ($data) { + echo $data; + }); +}, 'printf'); + +$loop->run(); diff --git a/instafeed/vendor/clue/socks-react/examples/11-server.php b/instafeed/vendor/clue/socks-react/examples/11-server.php new file mode 100755 index 0000000..da0a1a1 --- /dev/null +++ b/instafeed/vendor/clue/socks-react/examples/11-server.php @@ -0,0 +1,19 @@ +getAddress() . PHP_EOL; + +$loop->run(); diff --git a/instafeed/vendor/clue/socks-react/examples/12-server-with-password.php b/instafeed/vendor/clue/socks-react/examples/12-server-with-password.php new file mode 100755 index 0000000..55cc30b --- /dev/null +++ b/instafeed/vendor/clue/socks-react/examples/12-server-with-password.php @@ -0,0 +1,24 @@ +setAuthArray(array( + 'tom' => 'god', + 'user' => 'p@ssw0rd' +)); + +echo 'SOCKS5 server requiring authentication listening on ' . $socket->getAddress() . PHP_EOL; + +$loop->run(); diff --git a/instafeed/vendor/clue/socks-react/examples/13-server-blacklist.php b/instafeed/vendor/clue/socks-react/examples/13-server-blacklist.php new file mode 100755 index 0000000..c153049 --- /dev/null +++ b/instafeed/vendor/clue/socks-react/examples/13-server-blacklist.php @@ -0,0 +1,40 @@ + $reject, + 'www.google.com:80' => $reject, + '*' => $permit +)); + +// listen on 127.0.0.1:1080 or first argument +$listen = isset($argv[1]) ? $argv[1] : '127.0.0.1:1080'; +$socket = new Socket($listen, $loop); + +// start the actual socks server on the given server socket and using our connection manager for outgoing connections +$server = new Server($loop, $socket, $connector); + +echo 'SOCKS server listening on ' . $socket->getAddress() . PHP_EOL; + +$loop->run(); diff --git a/instafeed/vendor/clue/socks-react/examples/21-server-proxy-chaining.php b/instafeed/vendor/clue/socks-react/examples/21-server-proxy-chaining.php new file mode 100755 index 0000000..0d0dee2 --- /dev/null +++ b/instafeed/vendor/clue/socks-react/examples/21-server-proxy-chaining.php @@ -0,0 +1,42 @@ + [...]' . PHP_EOL; + echo 'You can add 1..n proxies in the path' . PHP_EOL; + exit(1); +} + +$listen = $argv[1]; +$path = array_slice($argv, 2); + +// Alternatively, you can also hard-code these values like this: +//$listen = '127.0.0.1:9050'; +//$path = array('127.0.0.1:9051', '127.0.0.1:9052', '127.0.0.1:9053'); + +$loop = React\EventLoop\Factory::create(); + +// set next SOCKS server chain -> p1 -> p2 -> p3 -> destination +$connector = new Connector($loop); +foreach ($path as $proxy) { + $connector = new Client($proxy, $connector); +} + +// listen on 127.0.0.1:1080 or first argument +$socket = new Socket($listen, $loop); + +// start a new server which forwards all connections to the other SOCKS server +$server = new Server($loop, $socket, $connector); + +echo 'SOCKS server listening on ' . $socket->getAddress() . PHP_EOL; +echo 'Forwarding via: ' . implode(' -> ', $path) . PHP_EOL; + +$loop->run(); diff --git a/instafeed/vendor/clue/socks-react/examples/22-server-proxy-chaining-from-random-pool.php b/instafeed/vendor/clue/socks-react/examples/22-server-proxy-chaining-from-random-pool.php new file mode 100755 index 0000000..39245c9 --- /dev/null +++ b/instafeed/vendor/clue/socks-react/examples/22-server-proxy-chaining-from-random-pool.php @@ -0,0 +1,46 @@ + ...' . PHP_EOL; + echo 'You can add 2..n proxies in the pool' . PHP_EOL; + exit(1); +} + +$listen = $argv[1]; +$pool = array_slice($argv, 2); + +// Alternatively, you can also hard-code these values like this: +//$listen = '127.0.0.1:9050'; +//$pool = array('127.0.0.1:9051', '127.0.0.1:9052', '127.0.0.1:9053'); + +$loop = LoopFactory::create(); + +// forward to socks server listening on 127.0.0.1:9051-9053 +// this connector randomly picks one of the the attached connectors from the pool +$connector = new Connector($loop); +$clients = array(); +foreach ($pool as $proxy) { + $clients []= new Client($proxy, $connector); +} +$connector = new ConnectionManagerRandom($clients); + +$socket = new Socket($listen, $loop); + +// start the actual socks server on the given server socket and using our connection manager for outgoing connections +$server = new Server($loop, $socket, $connector); + +echo 'SOCKS server listening on ' . $socket->getAddress() . PHP_EOL; +echo 'Randomly picking from: ' . implode(', ', $pool) . PHP_EOL; + +$loop->run(); diff --git a/instafeed/vendor/clue/socks-react/examples/31-server-secure.php b/instafeed/vendor/clue/socks-react/examples/31-server-secure.php new file mode 100755 index 0000000..b4b2109 --- /dev/null +++ b/instafeed/vendor/clue/socks-react/examples/31-server-secure.php @@ -0,0 +1,21 @@ + array( + 'local_cert' => __DIR__ . '/localhost.pem', +))); + +// start a new server listening for incoming connection on the given socket +$server = new Server($loop, $socket); + +echo 'SOCKS over TLS server listening on ' . str_replace('tls:', 'sockss:', $socket->getAddress()) . PHP_EOL; + +$loop->run(); diff --git a/instafeed/vendor/clue/socks-react/examples/32-http-secure.php b/instafeed/vendor/clue/socks-react/examples/32-http-secure.php new file mode 100755 index 0000000..d304bd4 --- /dev/null +++ b/instafeed/vendor/clue/socks-react/examples/32-http-secure.php @@ -0,0 +1,33 @@ + array( + 'verify_peer' => false, + 'verify_peer_name' => false +)))); +$connector = new Connector($loop, array( + 'tcp' => $client, + 'timeout' => 3.0, + 'dns' => false +)); + +echo 'Demo SOCKS over TLS client connecting to secure SOCKS server ' . $proxy . PHP_EOL; + +$connector->connect('tcp://www.google.com:80')->then(function (ConnectionInterface $stream) { + echo 'connected' . PHP_EOL; + $stream->write("GET / HTTP/1.0\r\n\r\n"); + $stream->on('data', function ($data) { + echo $data; + }); +}, 'printf'); + +$loop->run(); diff --git a/instafeed/vendor/clue/socks-react/examples/localhost.pem b/instafeed/vendor/clue/socks-react/examples/localhost.pem new file mode 100755 index 0000000..be69279 --- /dev/null +++ b/instafeed/vendor/clue/socks-react/examples/localhost.pem @@ -0,0 +1,49 @@ +-----BEGIN CERTIFICATE----- +MIIDfTCCAmWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBZMRIwEAYDVQQDDAkxMjcu +MC4wLjExCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQK +DBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMTYxMjMwMTQ1OTA2WhcNMjYx +MjI4MTQ1OTA2WjBZMRIwEAYDVQQDDAkxMjcuMC4wLjExCzAJBgNVBAYTAkFVMRMw +EQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0 +eSBMdGQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC8SZWNS+Ktg0Py +W8dx5uXZ+ZUawd3wnzLMHW7EhoUpIrIdp3kDU9NezF68dOhPMJY/Kh+6btRCxWXN +2OVTqS5Xi826j3TSE07iF83JRLeveW0PcodjUBd+RzdwCWWo2pfMJz4v7x1wu1c9 +zNi6JxxpDAXTFSB4GiWsI4tFu2XmMRhfm6LRK4WPfsZIJKokdiG5fKSPDn7nrVj0 +UUXr2eBsEAzdwL14U9+mwbLdaAkz3qK3fqi8sEC09lEWm95gKMOhkQf5qvXODtT4 +wdVrrKDTyehLv0xaItnUDnXzrkMBU5QS9TQzzqSW6ZaBsSxtONEFUiXiN9dtyXsY +YCUE54G/AgMBAAGjUDBOMB0GA1UdDgQWBBQ2GRz3QsQzdXaTMnPVCKfpigA10DAf +BgNVHSMEGDAWgBQ2GRz3QsQzdXaTMnPVCKfpigA10DAMBgNVHRMEBTADAQH/MA0G +CSqGSIb3DQEBBQUAA4IBAQA77iZ4KrpPY18Ezjt0mngYAuAxunKddXYdLZ2khywN +0uI/VzYnkFVtrsC7y2jLHSxlmE2/viPPGZDUplENV2acN6JNW+tlt7/bsrQHDQw3 +7VCF27EWiDxHsaghhLkqC+kcop5YR5c0oDQTdEWEKSbow2zayUXDYbRRs76SClTe +824Yul+Ts8Mka+AX2PXDg47iZ84fJRN/nKavcJUTJ2iS1uYw0GNnFMge/uwsfMR3 +V47qN0X5emky8fcq99FlMCbcy0gHAeSWAjClgr2dd2i0LDatUbj7YmdmFcskOgII +IwGfvuWR2yPevYGAE0QgFeLHniN3RW8zmpnX/XtrJ4a7 +-----END CERTIFICATE----- +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC8SZWNS+Ktg0Py +W8dx5uXZ+ZUawd3wnzLMHW7EhoUpIrIdp3kDU9NezF68dOhPMJY/Kh+6btRCxWXN +2OVTqS5Xi826j3TSE07iF83JRLeveW0PcodjUBd+RzdwCWWo2pfMJz4v7x1wu1c9 +zNi6JxxpDAXTFSB4GiWsI4tFu2XmMRhfm6LRK4WPfsZIJKokdiG5fKSPDn7nrVj0 +UUXr2eBsEAzdwL14U9+mwbLdaAkz3qK3fqi8sEC09lEWm95gKMOhkQf5qvXODtT4 +wdVrrKDTyehLv0xaItnUDnXzrkMBU5QS9TQzzqSW6ZaBsSxtONEFUiXiN9dtyXsY +YCUE54G/AgMBAAECggEBAKiO/3FE1CMddkCLZVtUp8ShqJgRokx9WI5ecwFApAkV +ZHsjqDQQYRNmxhDUX/w0tOzLGyhde2xjJyZG29YviKsbHwu6zYwbeOzy/mkGOaK/ +g6DmmMmRs9Z6juifoQCu4GIFZ6il2adIL2vF7OeJh+eKudQj/7NFRSB7mXzNrQWK +tZY3eux5zXWmio7pgZrx1HFZQiiL9NVLwT9J7oBnaoO3fREiu5J2xBpljG9Cr0j1 +LLiVLhukWJYRlHDtGt1CzI9w8iKo44PCRzpKyxpbsOrQxeSyEWUYQRv9VHA59LC7 +tVAJTbnTX1BNHkGZkOkoOpoZLwBaM2XbbDtcOGCAZMECgYEA+mTURFQ85/pxawvk +9ndqZ+5He1u/bMLYIJDp0hdB/vgD+vw3gb2UyRwp0I6Wc6Si4FEEnbY7L0pzWsiR +43CpLs+cyLfnD9NycuIasxs5fKb/1s1nGTkRAp7x9x/ZTtEf8v4YTmmMXFHzdo7V +pv+czO89ppEDkxEtMf/b5SifhO8CgYEAwIDIUvXLduGhL+RPDwjc2SKdydXGV6om +OEdt/V8oS801Z7k8l3gHXFm7zL/MpHmh9cag+F9dHK42kw2RSjDGsBlXXiAO1Z0I +2A34OdPw/kow8fmIKWTMu3+28Kca+3RmUqeyaq0vazQ/bWMO9px+Ud3YfLo1Tn5I +li0MecAx8DECgYEAvsLceKYYtL83c09fg2oc1ctSCCgw4WJcGAtvJ9DyRZacKbXH +b/+H/+OF8879zmKqd+0hcCnqUzAMTCisBLPLIM+o6b45ufPkqKObpcJi/JWaKgLY +vf2c+Psw6o4IF6T5Cz4MNIjzF06UBknxecYZpoPJ20F1kLCwVvxPgfl99l8CgYAb +XfOcv67WTstgiJ+oroTfJamy+P5ClkDqvVTosW+EHz9ZaJ8xlXHOcj9do2LPey9I +Rp250azmF+pQS5x9JKQKgv/FtN8HBVUtigbhCb14GUoODICMCfWFLmnumoMefnTR +iV+3BLn6Dqp5vZxx+NuIffZ5/Or5JsDhALSGVomC8QKBgAi3Z/dNQrDHfkXMNn/L ++EAoLuAbFgLs76r9VGgNaRQ/q5gex2bZEGoBj4Sxvs95NUIcfD9wKT7FF8HdxARv +y3o6Bfc8Xp9So9SlFXrje+gkdEJ0rQR67d+XBuJZh86bXJHVrMwpoNL+ahLGdVSe +81oh1uCH1YPLM29hPyaohxL8 +-----END PRIVATE KEY----- diff --git a/instafeed/vendor/clue/socks-react/phpunit.xml.dist b/instafeed/vendor/clue/socks-react/phpunit.xml.dist new file mode 100755 index 0000000..d451dff --- /dev/null +++ b/instafeed/vendor/clue/socks-react/phpunit.xml.dist @@ -0,0 +1,14 @@ + + + + + + ./tests/ + + + + + ./src/ + + + diff --git a/instafeed/vendor/clue/socks-react/src/Client.php b/instafeed/vendor/clue/socks-react/src/Client.php new file mode 100755 index 0000000..ee643b6 --- /dev/null +++ b/instafeed/vendor/clue/socks-react/src/Client.php @@ -0,0 +1,382 @@ + SOCKS5 authentication + if (isset($parts['user']) || isset($parts['pass'])) { + if ($parts['scheme'] === 'socks') { + // default to using SOCKS5 if not given explicitly + $parts['scheme'] = 'socks5'; + } elseif ($parts['scheme'] !== 'socks5') { + // fail if any other protocol version given explicitly + throw new InvalidArgumentException('Authentication requires SOCKS5. Consider using protocol version 5 or waive authentication'); + } + $parts += array('user' => '', 'pass' => ''); + $this->setAuth(rawurldecode($parts['user']), rawurldecode($parts['pass'])); + } + + // check for valid protocol version from URI scheme + $this->setProtocolVersionFromScheme($parts['scheme']); + + $this->socksUri = $parts['host'] . ':' . $parts['port']; + $this->connector = $connector; + } + + private function setProtocolVersionFromScheme($scheme) + { + if ($scheme === 'socks' || $scheme === 'socks4a') { + $this->protocolVersion = '4a'; + } elseif ($scheme === 'socks5') { + $this->protocolVersion = '5'; + } elseif ($scheme === 'socks4') { + $this->protocolVersion = '4'; + } else { + throw new InvalidArgumentException('Invalid protocol version given "' . $scheme . '://"'); + } + } + + /** + * set login data for username/password authentication method (RFC1929) + * + * @param string $username + * @param string $password + * @link http://tools.ietf.org/html/rfc1929 + */ + private function setAuth($username, $password) + { + if (strlen($username) > 255 || strlen($password) > 255) { + throw new InvalidArgumentException('Both username and password MUST NOT exceed a length of 255 bytes each'); + } + $this->auth = pack('C2', 0x01, strlen($username)) . $username . pack('C', strlen($password)) . $password; + } + + /** + * Establish a TCP/IP connection to the given target URI through the SOCKS server + * + * Many higher-level networking protocols build on top of TCP. It you're dealing + * with one such client implementation, it probably uses/accepts an instance + * implementing React's `ConnectorInterface` (and usually its default `Connector` + * instance). In this case you can also pass this `Connector` instance instead + * to make this client implementation SOCKS-aware. That's it. + * + * @param string $uri + * @return PromiseInterface Promise + */ + public function connect($uri) + { + if (strpos($uri, '://') === false) { + $uri = 'tcp://' . $uri; + } + + $parts = parse_url($uri); + if (!$parts || !isset($parts['scheme'], $parts['host'], $parts['port']) || $parts['scheme'] !== 'tcp') { + return Promise\reject(new InvalidArgumentException('Invalid target URI specified')); + } + + $host = trim($parts['host'], '[]'); + $port = $parts['port']; + + if ($this->protocolVersion === '4' && false === filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { + return Promise\reject(new InvalidArgumentException('Requires an IPv4 address for SOCKS4')); + } + + if (strlen($host) > 255 || $port > 65535 || $port < 0 || (string)$port !== (string)(int)$port) { + return Promise\reject(new InvalidArgumentException('Invalid target specified')); + } + + // construct URI to SOCKS server to connect to + $socksUri = $this->socksUri; + + // append path from URI if given + if (isset($parts['path'])) { + $socksUri .= $parts['path']; + } + + // parse query args + $args = array(); + if (isset($parts['query'])) { + parse_str($parts['query'], $args); + } + + // append hostname from URI to query string unless explicitly given + if (!isset($args['hostname'])) { + $args['hostname'] = $host; + } + + // append query string + $socksUri .= '?' . http_build_query($args, '', '&'); + + // append fragment from URI if given + if (isset($parts['fragment'])) { + $socksUri .= '#' . $parts['fragment']; + } + + $that = $this; + + // start TCP/IP connection to SOCKS server and then + // handle SOCKS protocol once connection is ready + // resolve plain connection once SOCKS protocol is completed + return $this->connector->connect($socksUri)->then( + function (ConnectionInterface $stream) use ($that, $host, $port) { + return $that->handleConnectedSocks($stream, $host, $port); + }, + function (Exception $e) { + throw new RuntimeException('Unable to connect to proxy (ECONNREFUSED)', defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111, $e); + } + ); + } + + /** + * Internal helper used to handle the communication with the SOCKS server + * + * @param ConnectionInterface $stream + * @param string $host + * @param int $port + * @return Promise Promise + * @internal + */ + public function handleConnectedSocks(ConnectionInterface $stream, $host, $port) + { + $deferred = new Deferred(function ($_, $reject) { + $reject(new RuntimeException('Connection canceled while establishing SOCKS session (ECONNABORTED)', defined('SOCKET_ECONNABORTED') ? SOCKET_ECONNABORTED : 103)); + }); + + $reader = new StreamReader(); + $stream->on('data', array($reader, 'write')); + + $stream->on('error', $onError = function (Exception $e) use ($deferred) { + $deferred->reject(new RuntimeException('Stream error while waiting for response from proxy (EIO)', defined('SOCKET_EIO') ? SOCKET_EIO : 5, $e)); + }); + + $stream->on('close', $onClose = function () use ($deferred) { + $deferred->reject(new RuntimeException('Connection to proxy lost while waiting for response (ECONNRESET)', defined('SOCKET_ECONNRESET') ? SOCKET_ECONNRESET : 104)); + }); + + if ($this->protocolVersion === '5') { + $promise = $this->handleSocks5($stream, $host, $port, $reader); + } else { + $promise = $this->handleSocks4($stream, $host, $port, $reader); + } + $promise->then(function () use ($deferred, $stream) { + $deferred->resolve($stream); + }, function (Exception $error) use ($deferred) { + // pass custom RuntimeException through as-is, otherwise wrap in protocol error + if (!$error instanceof RuntimeException) { + $error = new RuntimeException('Invalid response received from proxy (EBADMSG)', defined('SOCKET_EBADMSG') ? SOCKET_EBADMSG: 71, $error); + } + + $deferred->reject($error); + }); + + return $deferred->promise()->then( + function (ConnectionInterface $stream) use ($reader, $onError, $onClose) { + $stream->removeListener('data', array($reader, 'write')); + $stream->removeListener('error', $onError); + $stream->removeListener('close', $onClose); + + return $stream; + }, + function ($error) use ($stream, $onClose) { + $stream->removeListener('close', $onClose); + $stream->close(); + + throw $error; + } + ); + } + + private function handleSocks4(ConnectionInterface $stream, $host, $port, StreamReader $reader) + { + // do not resolve hostname. only try to convert to IP + $ip = ip2long($host); + + // send IP or (0.0.0.1) if invalid + $data = pack('C2nNC', 0x04, 0x01, $port, $ip === false ? 1 : $ip, 0x00); + + if ($ip === false) { + // host is not a valid IP => send along hostname (SOCKS4a) + $data .= $host . pack('C', 0x00); + } + + $stream->write($data); + + return $reader->readBinary(array( + 'null' => 'C', + 'status' => 'C', + 'port' => 'n', + 'ip' => 'N' + ))->then(function ($data) { + if ($data['null'] !== 0x00) { + throw new Exception('Invalid SOCKS response'); + } + if ($data['status'] !== 0x5a) { + throw new RuntimeException('Proxy refused connection with SOCKS error code ' . sprintf('0x%02X', $data['status']) . ' (ECONNREFUSED)', defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111); + } + }); + } + + private function handleSocks5(ConnectionInterface $stream, $host, $port, StreamReader $reader) + { + // protocol version 5 + $data = pack('C', 0x05); + + $auth = $this->auth; + if ($auth === null) { + // one method, no authentication + $data .= pack('C2', 0x01, 0x00); + } else { + // two methods, username/password and no authentication + $data .= pack('C3', 0x02, 0x02, 0x00); + } + $stream->write($data); + + $that = $this; + + return $reader->readBinary(array( + 'version' => 'C', + 'method' => 'C' + ))->then(function ($data) use ($auth, $stream, $reader) { + if ($data['version'] !== 0x05) { + throw new Exception('Version/Protocol mismatch'); + } + + if ($data['method'] === 0x02 && $auth !== null) { + // username/password authentication requested and provided + $stream->write($auth); + + return $reader->readBinary(array( + 'version' => 'C', + 'status' => 'C' + ))->then(function ($data) { + if ($data['version'] !== 0x01 || $data['status'] !== 0x00) { + throw new RuntimeException('Username/Password authentication failed (EACCES)', defined('SOCKET_EACCES') ? SOCKET_EACCES : 13); + } + }); + } else if ($data['method'] !== 0x00) { + // any other method than "no authentication" + throw new RuntimeException('No acceptable authentication method found (EACCES)', defined('SOCKET_EACCES') ? SOCKET_EACCES : 13); + } + })->then(function () use ($stream, $reader, $host, $port) { + // do not resolve hostname. only try to convert to (binary/packed) IP + $ip = @inet_pton($host); + + $data = pack('C3', 0x05, 0x01, 0x00); + if ($ip === false) { + // not an IP, send as hostname + $data .= pack('C2', 0x03, strlen($host)) . $host; + } else { + // send as IPv4 / IPv6 + $data .= pack('C', (strpos($host, ':') === false) ? 0x01 : 0x04) . $ip; + } + $data .= pack('n', $port); + + $stream->write($data); + + return $reader->readBinary(array( + 'version' => 'C', + 'status' => 'C', + 'null' => 'C', + 'type' => 'C' + )); + })->then(function ($data) use ($reader) { + if ($data['version'] !== 0x05 || $data['null'] !== 0x00) { + throw new Exception('Invalid SOCKS response'); + } + if ($data['status'] !== 0x00) { + // map limited list of SOCKS error codes to common socket error conditions + // @link https://tools.ietf.org/html/rfc1928#section-6 + if ($data['status'] === Server::ERROR_GENERAL) { + throw new RuntimeException('SOCKS server reported a general server failure (ECONNREFUSED)', defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111); + } elseif ($data['status'] === Server::ERROR_NOT_ALLOWED_BY_RULESET) { + throw new RuntimeException('SOCKS server reported connection is not allowed by ruleset (EACCES)', defined('SOCKET_EACCES') ? SOCKET_EACCES : 13); + } elseif ($data['status'] === Server::ERROR_NETWORK_UNREACHABLE) { + throw new RuntimeException('SOCKS server reported network unreachable (ENETUNREACH)', defined('SOCKET_ENETUNREACH') ? SOCKET_ENETUNREACH : 101); + } elseif ($data['status'] === Server::ERROR_HOST_UNREACHABLE) { + throw new RuntimeException('SOCKS server reported host unreachable (EHOSTUNREACH)', defined('SOCKET_EHOSTUNREACH') ? SOCKET_EHOSTUNREACH : 113); + } elseif ($data['status'] === Server::ERROR_CONNECTION_REFUSED) { + throw new RuntimeException('SOCKS server reported connection refused (ECONNREFUSED)', defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111); + } elseif ($data['status'] === Server::ERROR_TTL) { + throw new RuntimeException('SOCKS server reported TTL/timeout expired (ETIMEDOUT)', defined('SOCKET_ETIMEDOUT') ? SOCKET_ETIMEDOUT : 110); + } elseif ($data['status'] === Server::ERROR_COMMAND_UNSUPPORTED) { + throw new RuntimeException('SOCKS server does not support the CONNECT command (EPROTO)', defined('SOCKET_EPROTO') ? SOCKET_EPROTO : 71); + } elseif ($data['status'] === Server::ERROR_ADDRESS_UNSUPPORTED) { + throw new RuntimeException('SOCKS server does not support this address type (EPROTO)', defined('SOCKET_EPROTO') ? SOCKET_EPROTO : 71); + } + + throw new RuntimeException('SOCKS server reported an unassigned error code ' . sprintf('0x%02X', $data['status']) . ' (ECONNREFUSED)', defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111); + } + if ($data['type'] === 0x01) { + // IPv4 address => skip IP and port + return $reader->readLength(6); + } elseif ($data['type'] === 0x03) { + // domain name => read domain name length + return $reader->readBinary(array( + 'length' => 'C' + ))->then(function ($data) use ($reader) { + // skip domain name and port + return $reader->readLength($data['length'] + 2); + }); + } elseif ($data['type'] === 0x04) { + // IPv6 address => skip IP and port + return $reader->readLength(18); + } else { + throw new Exception('Invalid SOCKS reponse: Invalid address type'); + } + }); + } +} diff --git a/instafeed/vendor/clue/socks-react/src/Server.php b/instafeed/vendor/clue/socks-react/src/Server.php new file mode 100755 index 0000000..0a069e2 --- /dev/null +++ b/instafeed/vendor/clue/socks-react/src/Server.php @@ -0,0 +1,422 @@ +loop = $loop; + $this->connector = $connector; + + $that = $this; + $serverInterface->on('connection', function ($connection) use ($that) { + $that->emit('connection', array($connection)); + $that->onConnection($connection); + }); + } + + public function setProtocolVersion($version) + { + if ($version !== null) { + $version = (string)$version; + if (!in_array($version, array('4', '4a', '5'), true)) { + throw new InvalidArgumentException('Invalid protocol version given'); + } + if ($version !== '5' && $this->auth !== null){ + throw new UnexpectedValueException('Unable to change protocol version to anything but SOCKS5 while authentication is used. Consider removing authentication info or sticking to SOCKS5'); + } + } + $this->protocolVersion = $version; + } + + public function setAuth($auth) + { + if (!is_callable($auth)) { + throw new InvalidArgumentException('Given authenticator is not a valid callable'); + } + if ($this->protocolVersion !== null && $this->protocolVersion !== '5') { + throw new UnexpectedValueException('Authentication requires SOCKS5. Consider using protocol version 5 or waive authentication'); + } + // wrap authentication callback in order to cast its return value to a promise + $this->auth = function($username, $password, $remote) use ($auth) { + $ret = call_user_func($auth, $username, $password, $remote); + if ($ret instanceof PromiseInterface) { + return $ret; + } + $deferred = new Deferred(); + $ret ? $deferred->resolve() : $deferred->reject(); + return $deferred->promise(); + }; + } + + public function setAuthArray(array $login) + { + $this->setAuth(function ($username, $password) use ($login) { + return (isset($login[$username]) && (string)$login[$username] === $password); + }); + } + + public function unsetAuth() + { + $this->auth = null; + } + + public function onConnection(ConnectionInterface $connection) + { + $that = $this; + $handling = $this->handleSocks($connection)->then(function($remote) use ($connection){ + $connection->emit('ready',array($remote)); + }, function ($error) use ($connection, $that) { + if (!($error instanceof \Exception)) { + $error = new \Exception($error); + } + $connection->emit('error', array($error)); + $that->endConnection($connection); + }); + + $connection->on('close', function () use ($handling) { + $handling->cancel(); + }); + } + + /** + * gracefully shutdown connection by flushing all remaining data and closing stream + */ + public function endConnection(ConnectionInterface $stream) + { + $tid = true; + $loop = $this->loop; + + // cancel below timer in case connection is closed in time + $stream->once('close', function () use (&$tid, $loop) { + // close event called before the timer was set up, so everything is okay + if ($tid === true) { + // make sure to not start a useless timer + $tid = false; + } else { + $loop->cancelTimer($tid); + } + }); + + // shut down connection by pausing input data, flushing outgoing buffer and then exit + $stream->pause(); + $stream->end(); + + // check if connection is not already closed + if ($tid === true) { + // fall back to forcefully close connection in 3 seconds if buffer can not be flushed + $tid = $loop->addTimer(3.0, array($stream,'close')); + } + } + + private function handleSocks(ConnectionInterface $stream) + { + $reader = new StreamReader(); + $stream->on('data', array($reader, 'write')); + + $that = $this; + $that = $this; + + $auth = $this->auth; + $protocolVersion = $this->protocolVersion; + + // authentication requires SOCKS5 + if ($auth !== null) { + $protocolVersion = '5'; + } + + return $reader->readByte()->then(function ($version) use ($stream, $that, $protocolVersion, $auth, $reader){ + if ($version === 0x04) { + if ($protocolVersion === '5') { + throw new UnexpectedValueException('SOCKS4 not allowed due to configuration'); + } + return $that->handleSocks4($stream, $protocolVersion, $reader); + } else if ($version === 0x05) { + if ($protocolVersion !== null && $protocolVersion !== '5') { + throw new UnexpectedValueException('SOCKS5 not allowed due to configuration'); + } + return $that->handleSocks5($stream, $auth, $reader); + } + throw new UnexpectedValueException('Unexpected/unknown version number'); + }); + } + + public function handleSocks4(ConnectionInterface $stream, $protocolVersion, StreamReader $reader) + { + // suppliying hostnames is only allowed for SOCKS4a (or automatically detected version) + $supportsHostname = ($protocolVersion === null || $protocolVersion === '4a'); + + $remote = $stream->getRemoteAddress(); + if ($remote !== null) { + // remove transport scheme and prefix socks4:// instead + $secure = strpos($remote, 'tls://') === 0; + if (($pos = strpos($remote, '://')) !== false) { + $remote = substr($remote, $pos + 3); + } + $remote = 'socks4' . ($secure ? 's' : '') . '://' . $remote; + } + + $that = $this; + return $reader->readByteAssert(0x01)->then(function () use ($reader) { + return $reader->readBinary(array( + 'port' => 'n', + 'ipLong' => 'N', + 'null' => 'C' + )); + })->then(function ($data) use ($reader, $supportsHostname, $remote) { + if ($data['null'] !== 0x00) { + throw new Exception('Not a null byte'); + } + if ($data['ipLong'] === 0) { + throw new Exception('Invalid IP'); + } + if ($data['port'] === 0) { + throw new Exception('Invalid port'); + } + if ($data['ipLong'] < 256 && $supportsHostname) { + // invalid IP => probably a SOCKS4a request which appends the hostname + return $reader->readStringNull()->then(function ($string) use ($data, $remote){ + return array($string, $data['port'], $remote); + }); + } else { + $ip = long2ip($data['ipLong']); + return array($ip, $data['port'], $remote); + } + })->then(function ($target) use ($stream, $that) { + return $that->connectTarget($stream, $target)->then(function (ConnectionInterface $remote) use ($stream){ + $stream->write(pack('C8', 0x00, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)); + + return $remote; + }, function($error) use ($stream){ + $stream->end(pack('C8', 0x00, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)); + + throw $error; + }); + }, function($error) { + throw new UnexpectedValueException('SOCKS4 protocol error',0,$error); + }); + } + + public function handleSocks5(ConnectionInterface $stream, $auth=null, StreamReader $reader) + { + $remote = $stream->getRemoteAddress(); + if ($remote !== null) { + // remove transport scheme and prefix socks5:// instead + $secure = strpos($remote, 'tls://') === 0; + if (($pos = strpos($remote, '://')) !== false) { + $remote = substr($remote, $pos + 3); + } + $remote = 'socks5' . ($secure ? 's' : '') . '://' . $remote; + } + + $that = $this; + return $reader->readByte()->then(function ($num) use ($reader) { + // $num different authentication mechanisms offered + return $reader->readLength($num); + })->then(function ($methods) use ($reader, $stream, $auth, &$remote) { + if ($auth === null && strpos($methods,"\x00") !== false) { + // accept "no authentication" + $stream->write(pack('C2', 0x05, 0x00)); + + return 0x00; + } else if ($auth !== null && strpos($methods,"\x02") !== false) { + // username/password authentication (RFC 1929) sub negotiation + $stream->write(pack('C2', 0x05, 0x02)); + return $reader->readByteAssert(0x01)->then(function () use ($reader) { + return $reader->readByte(); + })->then(function ($length) use ($reader) { + return $reader->readLength($length); + })->then(function ($username) use ($reader, $auth, $stream, &$remote) { + return $reader->readByte()->then(function ($length) use ($reader) { + return $reader->readLength($length); + })->then(function ($password) use ($username, $auth, $stream, &$remote) { + // username and password given => authenticate + + // prefix username/password to remote URI + if ($remote !== null) { + $remote = str_replace('://', '://' . rawurlencode($username) . ':' . rawurlencode($password) . '@', $remote); + } + + return $auth($username, $password, $remote)->then(function () use ($stream, $username) { + // accept + $stream->emit('auth', array($username)); + $stream->write(pack('C2', 0x01, 0x00)); + }, function() use ($stream) { + // reject => send any code but 0x00 + $stream->end(pack('C2', 0x01, 0xFF)); + throw new UnexpectedValueException('Unable to authenticate'); + }); + }); + }); + } else { + // reject all offered authentication methods + $stream->write(pack('C2', 0x05, 0xFF)); + throw new UnexpectedValueException('No acceptable authentication mechanism found'); + } + })->then(function ($method) use ($reader, $stream) { + return $reader->readBinary(array( + 'version' => 'C', + 'command' => 'C', + 'null' => 'C', + 'type' => 'C' + )); + })->then(function ($data) use ($reader) { + if ($data['version'] !== 0x05) { + throw new UnexpectedValueException('Invalid SOCKS version'); + } + if ($data['command'] !== 0x01) { + throw new UnexpectedValueException('Only CONNECT requests supported', Server::ERROR_COMMAND_UNSUPPORTED); + } +// if ($data['null'] !== 0x00) { +// throw new UnexpectedValueException('Reserved byte has to be NULL'); +// } + if ($data['type'] === 0x03) { + // target hostname string + return $reader->readByte()->then(function ($len) use ($reader) { + return $reader->readLength($len); + }); + } else if ($data['type'] === 0x01) { + // target IPv4 + return $reader->readLength(4)->then(function ($addr) { + return inet_ntop($addr); + }); + } else if ($data['type'] === 0x04) { + // target IPv6 + return $reader->readLength(16)->then(function ($addr) { + return inet_ntop($addr); + }); + } else { + throw new UnexpectedValueException('Invalid address type', Server::ERROR_ADDRESS_UNSUPPORTED); + } + })->then(function ($host) use ($reader, &$remote) { + return $reader->readBinary(array('port'=>'n'))->then(function ($data) use ($host, &$remote) { + return array($host, $data['port'], $remote); + }); + })->then(function ($target) use ($that, $stream) { + return $that->connectTarget($stream, $target); + }, function($error) use ($stream) { + throw new UnexpectedValueException('SOCKS5 protocol error', $error->getCode(), $error); + })->then(function (ConnectionInterface $remote) use ($stream) { + $stream->write(pack('C4Nn', 0x05, 0x00, 0x00, 0x01, 0, 0)); + + return $remote; + }, function(Exception $error) use ($stream){ + $stream->write(pack('C4Nn', 0x05, $error->getCode() === 0 ? Server::ERROR_GENERAL : $error->getCode(), 0x00, 0x01, 0, 0)); + + throw $error; + }); + } + + public function connectTarget(ConnectionInterface $stream, array $target) + { + $uri = $target[0]; + if (strpos($uri, ':') !== false) { + $uri = '[' . $uri . ']'; + } + $uri .= ':' . $target[1]; + + // validate URI so a string hostname can not pass excessive URI parts + $parts = parse_url('tcp://' . $uri); + if (!$parts || !isset($parts['scheme'], $parts['host'], $parts['port']) || count($parts) !== 3) { + return Promise\reject(new InvalidArgumentException('Invalid target URI given')); + } + + if (isset($target[2])) { + $uri .= '?source=' . rawurlencode($target[2]); + } + + $stream->emit('target', $target); + $that = $this; + $connecting = $this->connector->connect($uri); + + $stream->on('close', function () use ($connecting) { + $connecting->cancel(); + }); + + return $connecting->then(function (ConnectionInterface $remote) use ($stream, $that) { + $stream->pipe($remote, array('end'=>false)); + $remote->pipe($stream, array('end'=>false)); + + // remote end closes connection => stop reading from local end, try to flush buffer to local and disconnect local + $remote->on('end', function() use ($stream, $that) { + $stream->emit('shutdown', array('remote', null)); + $that->endConnection($stream); + }); + + // local end closes connection => stop reading from remote end, try to flush buffer to remote and disconnect remote + $stream->on('end', function() use ($remote, $that) { + $that->endConnection($remote); + }); + + // set bigger buffer size of 100k to improve performance + $stream->bufferSize = $remote->bufferSize = 100 * 1024 * 1024; + + return $remote; + }, function(Exception $error) { + // default to general/unknown error + $code = Server::ERROR_GENERAL; + + // map common socket error conditions to limited list of SOCKS error codes + if ((defined('SOCKET_EACCES') && $error->getCode() === SOCKET_EACCES) || $error->getCode() === 13) { + $code = Server::ERROR_NOT_ALLOWED_BY_RULESET; + } elseif ((defined('SOCKET_EHOSTUNREACH') && $error->getCode() === SOCKET_EHOSTUNREACH) || $error->getCode() === 113) { + $code = Server::ERROR_HOST_UNREACHABLE; + } elseif ((defined('SOCKET_ENETUNREACH') && $error->getCode() === SOCKET_ENETUNREACH) || $error->getCode() === 101) { + $code = Server::ERROR_NETWORK_UNREACHABLE; + } elseif ((defined('SOCKET_ECONNREFUSED') && $error->getCode() === SOCKET_ECONNREFUSED) || $error->getCode() === 111 || $error->getMessage() === 'Connection refused') { + // Socket component does not currently assign an error code for this, so we have to resort to checking the exception message + $code = Server::ERROR_CONNECTION_REFUSED; + } elseif ((defined('SOCKET_ETIMEDOUT') && $error->getCode() === SOCKET_ETIMEDOUT) || $error->getCode() === 110 || $error instanceof TimeoutException) { + // Socket component does not currently assign an error code for this, but we can rely on the TimeoutException + $code = Server::ERROR_TTL; + } + + throw new UnexpectedValueException('Unable to connect to remote target', $code, $error); + }); + } +} diff --git a/instafeed/vendor/clue/socks-react/src/StreamReader.php b/instafeed/vendor/clue/socks-react/src/StreamReader.php new file mode 100755 index 0000000..6dbf51c --- /dev/null +++ b/instafeed/vendor/clue/socks-react/src/StreamReader.php @@ -0,0 +1,149 @@ +buffer .= $data; + + do { + $current = reset($this->queue); + + if ($current === false) { + break; + } + + /* @var $current Closure */ + + $ret = $current($this->buffer); + + if ($ret === self::RET_INCOMPLETE) { + // current is incomplete, so wait for further data to arrive + break; + } else { + // current is done, remove from list and continue with next + array_shift($this->queue); + } + } while (true); + } + + public function readBinary($structure) + { + $length = 0; + $unpack = ''; + foreach ($structure as $name=>$format) { + if ($length !== 0) { + $unpack .= '/'; + } + $unpack .= $format . $name; + + if ($format === 'C') { + ++$length; + } else if ($format === 'n') { + $length += 2; + } else if ($format === 'N') { + $length += 4; + } else { + throw new InvalidArgumentException('Invalid format given'); + } + } + + return $this->readLength($length)->then(function ($response) use ($unpack) { + return unpack($unpack, $response); + }); + } + + public function readLength($bytes) + { + $deferred = new Deferred(); + + $this->readBufferCallback(function (&$buffer) use ($bytes, $deferred) { + if (strlen($buffer) >= $bytes) { + $deferred->resolve((string)substr($buffer, 0, $bytes)); + $buffer = (string)substr($buffer, $bytes); + + return StreamReader::RET_DONE; + } + }); + + return $deferred->promise(); + } + + public function readByte() + { + return $this->readBinary(array( + 'byte' => 'C' + ))->then(function ($data) { + return $data['byte']; + }); + } + + public function readByteAssert($expect) + { + return $this->readByte()->then(function ($byte) use ($expect) { + if ($byte !== $expect) { + throw new UnexpectedValueException('Unexpected byte encountered'); + } + return $byte; + }); + } + + public function readStringNull() + { + $deferred = new Deferred(); + $string = ''; + + $that = $this; + $readOne = function () use (&$readOne, $that, $deferred, &$string) { + $that->readByte()->then(function ($byte) use ($deferred, &$string, $readOne) { + if ($byte === 0x00) { + $deferred->resolve($string); + } else { + $string .= chr($byte); + $readOne(); + } + }); + }; + $readOne(); + + return $deferred->promise(); + } + + public function readBufferCallback(/* callable */ $callable) + { + if (!is_callable($callable)) { + throw new InvalidArgumentException('Given function must be callable'); + } + + if ($this->queue) { + $this->queue []= $callable; + } else { + $this->queue = array($callable); + + if ($this->buffer !== '') { + // this is the first element in the queue and the buffer is filled => trigger write procedure + $this->write(''); + } + } + } + + public function getBuffer() + { + return $this->buffer; + } +} diff --git a/instafeed/vendor/clue/socks-react/tests/ClientTest.php b/instafeed/vendor/clue/socks-react/tests/ClientTest.php new file mode 100755 index 0000000..e304901 --- /dev/null +++ b/instafeed/vendor/clue/socks-react/tests/ClientTest.php @@ -0,0 +1,403 @@ +loop = React\EventLoop\Factory::create(); + $this->connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock(); + $this->client = new Client('127.0.0.1:1080', $this->connector); + } + + public function testCtorAcceptsUriWithHostAndPort() + { + $client = new Client('127.0.0.1:9050', $this->connector); + + $this->assertTrue(true); + } + + public function testCtorAcceptsUriWithScheme() + { + $client = new Client('socks://127.0.0.1:9050', $this->connector); + + $this->assertTrue(true); + } + + public function testCtorAcceptsUriWithHostOnlyAssumesDefaultPort() + { + $client = new Client('127.0.0.1', $this->connector); + + $this->assertTrue(true); + } + + public function testCtorAcceptsUriWithSecureScheme() + { + $client = new Client('sockss://127.0.0.1:9050', $this->connector); + + $this->assertTrue(true); + } + + public function testCtorAcceptsUriWithSecureVersionScheme() + { + $client = new Client('socks5s://127.0.0.1:9050', $this->connector); + + $this->assertTrue(true); + } + + public function testCtorAcceptsUriWithSocksUnixScheme() + { + $client = new Client('socks+unix:///tmp/socks.socket', $this->connector); + + $this->assertTrue(true); + } + + public function testCtorAcceptsUriWithSocks5UnixScheme() + { + $client = new Client('socks5+unix:///tmp/socks.socket', $this->connector); + + $this->assertTrue(true); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testCtorThrowsForInvalidUri() + { + new Client('////', $this->connector); + } + + public function testValidAuthFromUri() + { + $this->client = new Client('username:password@127.0.0.1', $this->connector); + + $this->assertTrue(true); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testInvalidAuthInformation() + { + new Client(str_repeat('a', 256) . ':test@127.0.0.1', $this->connector); + } + + public function testValidAuthAndVersionFromUri() + { + $this->client = new Client('socks5://username:password@127.0.0.1:9050', $this->connector); + + $this->assertTrue(true); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testInvalidCanNotSetAuthenticationForSocks4Uri() + { + $this->client = new Client('socks4://username:password@127.0.0.1:9050', $this->connector); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testInvalidProtocolVersion() + { + $this->client = new Client('socks3://127.0.0.1:9050', $this->connector); + } + + public function testCreateWillConnectToProxy() + { + $promise = new Promise(function () { }); + + $this->connector->expects($this->once())->method('connect')->with('127.0.0.1:1080?hostname=localhost')->willReturn($promise); + + $promise = $this->client->connect('localhost:80'); + + $this->assertInstanceOf('\React\Promise\PromiseInterface', $promise); + } + + public function testCreateWillConnectToProxyWithFullUri() + { + $promise = new Promise(function () { }); + + $this->connector->expects($this->once())->method('connect')->with('127.0.0.1:1080/?hostname=test#fragment')->willReturn($promise); + + $promise = $this->client->connect('localhost:80/?hostname=test#fragment'); + + $this->assertInstanceOf('\React\Promise\PromiseInterface', $promise); + } + + public function testCreateWithInvalidHostDoesNotConnect() + { + $promise = new Promise(function () { }); + + $this->connector->expects($this->never())->method('connect'); + + $promise = $this->client->connect(str_repeat('a', '256') . ':80'); + + $this->assertInstanceOf('\React\Promise\PromiseInterface', $promise); + } + + public function testCreateWithInvalidPortDoesNotConnect() + { + $promise = new Promise(function () { }); + + $this->connector->expects($this->never())->method('connect'); + + $promise = $this->client->connect('some-random-site:some-random-port'); + + $this->assertInstanceOf('\React\Promise\PromiseInterface', $promise); + } + + public function testConnectorRejectsWillRejectConnection() + { + $promise = \React\Promise\reject(new RuntimeException()); + + $this->connector->expects($this->once())->method('connect')->with('127.0.0.1:1080?hostname=google.com')->willReturn($promise); + + $promise = $this->client->connect('google.com:80'); + + $promise->then(null, $this->expectCallableOnceWithExceptionCode(SOCKET_ECONNREFUSED)); + } + + public function testCancelConnectionDuringConnectionWillCancelConnection() + { + $promise = new Promise(function () { }, function () { + throw new \RuntimeException(); + }); + + $this->connector->expects($this->once())->method('connect')->with('127.0.0.1:1080?hostname=google.com')->willReturn($promise); + + $promise = $this->client->connect('google.com:80'); + $promise->cancel(); + + $this->expectPromiseReject($promise); + } + + public function testCancelConnectionDuringSessionWillCloseStream() + { + $stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->getMock(); + $stream->expects($this->once())->method('close'); + + $promise = new Promise(function ($resolve) use ($stream) { $resolve($stream); }); + + $this->connector->expects($this->once())->method('connect')->with('127.0.0.1:1080?hostname=google.com')->willReturn($promise); + + $promise = $this->client->connect('google.com:80'); + $promise->cancel(); + + $promise->then(null, $this->expectCallableOnceWithExceptionCode(SOCKET_ECONNABORTED)); + } + + public function testEmitConnectionCloseDuringSessionWillRejectConnection() + { + $stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('write', 'close'))->getMock(); + + $promise = \React\Promise\resolve($stream); + + $this->connector->expects($this->once())->method('connect')->with('127.0.0.1:1080?hostname=google.com')->willReturn($promise); + + $promise = $this->client->connect('google.com:80'); + + $stream->emit('close'); + + $promise->then(null, $this->expectCallableOnceWithExceptionCode(SOCKET_ECONNRESET)); + } + + public function testEmitConnectionErrorDuringSessionWillRejectConnection() + { + $stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('write', 'close'))->getMock(); + $stream->expects($this->once())->method('close'); + + $promise = \React\Promise\resolve($stream); + + $this->connector->expects($this->once())->method('connect')->with('127.0.0.1:1080?hostname=google.com')->willReturn($promise); + + $promise = $this->client->connect('google.com:80'); + + $stream->emit('error', array(new RuntimeException())); + + $promise->then(null, $this->expectCallableOnceWithExceptionCode(SOCKET_EIO)); + } + + public function testEmitInvalidSocks4DataDuringSessionWillRejectConnection() + { + $stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('write', 'close'))->getMock(); + $stream->expects($this->once())->method('close'); + + $promise = \React\Promise\resolve($stream); + + $this->connector->expects($this->once())->method('connect')->with('127.0.0.1:1080?hostname=google.com')->willReturn($promise); + + $promise = $this->client->connect('google.com:80'); + + $stream->emit('data', array("HTTP/1.1 400 Bad Request\r\n\r\n")); + + $promise->then(null, $this->expectCallableOnceWithExceptionCode(SOCKET_EBADMSG)); + } + + public function testEmitInvalidSocks5DataDuringSessionWillRejectConnection() + { + $stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('write', 'close'))->getMock(); + $stream->expects($this->once())->method('close'); + + $promise = \React\Promise\resolve($stream); + + $this->connector->expects($this->once())->method('connect')->with('127.0.0.1:1080?hostname=google.com')->willReturn($promise); + + $this->client = new Client('socks5://127.0.0.1:1080', $this->connector); + + $promise = $this->client->connect('google.com:80'); + + $stream->emit('data', array("HTTP/1.1 400 Bad Request\r\n\r\n")); + + $promise->then(null, $this->expectCallableOnceWithExceptionCode(SOCKET_EBADMSG)); + } + + public function testEmitSocks5DataErrorDuringSessionWillRejectConnection() + { + $stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('write', 'close'))->getMock(); + $stream->expects($this->once())->method('close'); + + $promise = \React\Promise\resolve($stream); + + $this->connector->expects($this->once())->method('connect')->with('127.0.0.1:1080?hostname=google.com')->willReturn($promise); + + $this->client = new Client('socks5://127.0.0.1:1080', $this->connector); + + $promise = $this->client->connect('google.com:80'); + + $stream->emit('data', array("\x05\x00" . "\x05\x01\x00\x00")); + + $promise->then(null, $this->expectCallableOnceWithExceptionCode(SOCKET_ECONNREFUSED)); + } + + public function testEmitSocks5DataInvalidAddressTypeWillRejectConnection() + { + $stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('write', 'close'))->getMock(); + $stream->expects($this->once())->method('close'); + + $promise = \React\Promise\resolve($stream); + + $this->connector->expects($this->once())->method('connect')->with('127.0.0.1:1080?hostname=google.com')->willReturn($promise); + + $this->client = new Client('socks5://127.0.0.1:1080', $this->connector); + + $promise = $this->client->connect('google.com:80'); + + $stream->emit('data', array("\x05\x00" . "\x05\x00\x00\x00")); + + $promise->then(null, $this->expectCallableOnceWithExceptionCode(SOCKET_EBADMSG)); + } + + public function testEmitSocks5DataIpv6AddressWillResolveConnection() + { + $stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('write', 'close'))->getMock(); + $stream->expects($this->never())->method('close'); + + $promise = \React\Promise\resolve($stream); + + $this->connector->expects($this->once())->method('connect')->with('127.0.0.1:1080?hostname=%3A%3A1')->willReturn($promise); + + $this->client = new Client('socks5://127.0.0.1:1080', $this->connector); + + $promise = $this->client->connect('[::1]:80'); + + $stream->emit('data', array("\x05\x00" . "\x05\x00\x00\x04" . inet_pton('::1') . "\x00\x50")); + + $promise->then($this->expectCallableOnce()); + } + + public function testEmitSocks5DataHostnameAddressWillResolveConnection() + { + $stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('write', 'close'))->getMock(); + $stream->expects($this->never())->method('close'); + + $promise = \React\Promise\resolve($stream); + + $this->connector->expects($this->once())->method('connect')->with('127.0.0.1:1080?hostname=google.com')->willReturn($promise); + + $this->client = new Client('socks5://127.0.0.1:1080', $this->connector); + + $promise = $this->client->connect('google.com:80'); + + $stream->emit('data', array("\x05\x00" . "\x05\x00\x00\x03\x0Agoogle.com\x00\x50")); + + $promise->then($this->expectCallableOnce()); + } + + public function provideConnectionErrors() + { + return array( + array( + Server::ERROR_GENERAL, + SOCKET_ECONNREFUSED + ), + array( + Server::ERROR_NOT_ALLOWED_BY_RULESET, + SOCKET_EACCES + ), + array( + Server::ERROR_NETWORK_UNREACHABLE, + SOCKET_ENETUNREACH + ), + array( + Server::ERROR_HOST_UNREACHABLE, + SOCKET_EHOSTUNREACH + ), + array( + Server::ERROR_CONNECTION_REFUSED, + SOCKET_ECONNREFUSED + ), + array( + Server::ERROR_TTL, + SOCKET_ETIMEDOUT + ), + array( + Server::ERROR_COMMAND_UNSUPPORTED, + SOCKET_EPROTO + ), + array( + Server::ERROR_ADDRESS_UNSUPPORTED, + SOCKET_EPROTO + ), + array( + 200, + SOCKET_ECONNREFUSED + ) + ); + } + + /** + * @dataProvider provideConnectionErrors + * @param int $error + * @param int $expectedCode + */ + public function testEmitSocks5DataErrorMapsToExceptionCode($error, $expectedCode) + { + $stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('write', 'close'))->getMock(); + $stream->expects($this->once())->method('close'); + + $promise = \React\Promise\resolve($stream); + + $this->connector->expects($this->once())->method('connect')->with('127.0.0.1:1080?hostname=google.com')->willReturn($promise); + + $this->client = new Client('socks5://127.0.0.1:1080', $this->connector); + + $promise = $this->client->connect('google.com:80'); + + $stream->emit('data', array("\x05\x00" . "\x05" . chr($error) . "\x00\x00")); + + $promise->then(null, $this->expectCallableOnceWithExceptionCode($expectedCode)); + } +} diff --git a/instafeed/vendor/clue/socks-react/tests/FunctionalTest.php b/instafeed/vendor/clue/socks-react/tests/FunctionalTest.php new file mode 100755 index 0000000..2f1190e --- /dev/null +++ b/instafeed/vendor/clue/socks-react/tests/FunctionalTest.php @@ -0,0 +1,437 @@ +loop = React\EventLoop\Factory::create(); + + $socket = new React\Socket\Server(0, $this->loop); + $address = $socket->getAddress(); + if (strpos($address, '://') === false) { + $address = 'tcp://' . $address; + } + $this->port = parse_url($address, PHP_URL_PORT); + $this->assertNotEquals(0, $this->port); + + $this->server = new Server($this->loop, $socket); + $this->connector = new TcpConnector($this->loop); + $this->client = new Client('127.0.0.1:' . $this->port, $this->connector); + } + + /** @group internet */ + public function testConnection() + { + $this->assertResolveStream($this->client->connect('www.google.com:80')); + } + + /** @group internet */ + public function testConnectionInvalid() + { + $this->assertRejectPromise($this->client->connect('www.google.com.invalid:80')); + } + + public function testConnectionWithIpViaSocks4() + { + $this->server->setProtocolVersion('4'); + + $this->client = new Client('socks4://127.0.0.1:' . $this->port, $this->connector); + + $this->assertResolveStream($this->client->connect('127.0.0.1:' . $this->port)); + } + + /** @group internet */ + public function testConnectionWithHostnameViaSocks4Fails() + { + $this->client = new Client('socks4://127.0.0.1:' . $this->port, $this->connector); + + $this->assertRejectPromise($this->client->connect('www.google.com:80')); + } + + /** @group internet */ + public function testConnectionWithInvalidPortFails() + { + $this->assertRejectPromise($this->client->connect('www.google.com:100000')); + } + + public function testConnectionWithIpv6ViaSocks4Fails() + { + $this->client = new Client('socks4://127.0.0.1:' . $this->port, $this->connector); + + $this->assertRejectPromise($this->client->connect('[::1]:80')); + } + + /** @group internet */ + public function testConnectionSocks4a() + { + $this->server->setProtocolVersion('4a'); + $this->client = new Client('socks4a://127.0.0.1:' . $this->port, $this->connector); + + $this->assertResolveStream($this->client->connect('www.google.com:80')); + } + + /** @group internet */ + public function testConnectionSocks5() + { + $this->server->setProtocolVersion(5); + $this->client = new Client('socks5://127.0.0.1:' . $this->port, $this->connector); + + $this->assertResolveStream($this->client->connect('www.google.com:80')); + } + + /** @group internet */ + public function testConnectionSocksOverTls() + { + if (!function_exists('stream_socket_enable_crypto')) { + $this->markTestSkipped('Required function does not exist in your environment (HHVM?)'); + } + + $socket = new \React\Socket\Server('tls://127.0.0.1:0', $this->loop, array('tls' => array( + 'local_cert' => __DIR__ . '/../examples/localhost.pem', + ))); + $this->server = new Server($this->loop, $socket); + + $this->connector = new Connector($this->loop, array('tls' => array( + 'verify_peer' => false, + 'verify_peer_name' => false + ))); + $this->client = new Client(str_replace('tls:', 'sockss:', $socket->getAddress()), $this->connector); + + $this->assertResolveStream($this->client->connect('www.google.com:80')); + } + + /** + * @group internet + * @requires PHP 5.6 + */ + public function testConnectionSocksOverTlsUsesPeerNameFromSocksUri() + { + if (!function_exists('stream_socket_enable_crypto')) { + $this->markTestSkipped('Required function does not exist in your environment (HHVM?)'); + } + + $socket = new \React\Socket\Server('tls://127.0.0.1:0', $this->loop, array('tls' => array( + 'local_cert' => __DIR__ . '/../examples/localhost.pem', + ))); + $this->server = new Server($this->loop, $socket); + + $this->connector = new Connector($this->loop, array('tls' => array( + 'verify_peer' => false, + 'verify_peer_name' => true + ))); + $this->client = new Client(str_replace('tls:', 'sockss:', $socket->getAddress()), $this->connector); + + $this->assertResolveStream($this->client->connect('www.google.com:80')); + } + + /** @group internet */ + public function testConnectionSocksOverUnix() + { + if (!in_array('unix', stream_get_transports())) { + $this->markTestSkipped('System does not support unix:// scheme'); + } + + $path = sys_get_temp_dir() . '/test' . mt_rand(1000, 9999) . '.sock'; + $socket = new UnixServer($path, $this->loop); + $this->server = new Server($this->loop, $socket); + + $this->connector = new Connector($this->loop); + $this->client = new Client('socks+unix://' . $path, $this->connector); + + $this->assertResolveStream($this->client->connect('www.google.com:80')); + + unlink($path); + } + + /** @group internet */ + public function testConnectionSocks5OverUnix() + { + if (!in_array('unix', stream_get_transports())) { + $this->markTestSkipped('System does not support unix:// scheme'); + } + + $path = sys_get_temp_dir() . '/test' . mt_rand(1000, 9999) . '.sock'; + $socket = new UnixServer($path, $this->loop); + $this->server = new Server($this->loop, $socket); + $this->server->setProtocolVersion(5); + + $this->connector = new Connector($this->loop); + $this->client = new Client('socks5+unix://' . $path, $this->connector); + + $this->assertResolveStream($this->client->connect('www.google.com:80')); + + unlink($path); + } + + /** @group internet */ + public function testConnectionSocksWithAuthenticationOverUnix() + { + if (!in_array('unix', stream_get_transports())) { + $this->markTestSkipped('System does not support unix:// scheme'); + } + + $path = sys_get_temp_dir() . '/test' . mt_rand(1000, 9999) . '.sock'; + $socket = new UnixServer($path, $this->loop); + $this->server = new Server($this->loop, $socket); + $this->server->setAuthArray(array('name' => 'pass')); + + $this->connector = new Connector($this->loop); + $this->client = new Client('socks+unix://name:pass@' . $path, $this->connector); + + $this->assertResolveStream($this->client->connect('www.google.com:80')); + + unlink($path); + } + + /** @group internet */ + public function testConnectionAuthenticationFromUri() + { + $this->server->setAuthArray(array('name' => 'pass')); + + $this->client = new Client('name:pass@127.0.0.1:' . $this->port, $this->connector); + + $this->assertResolveStream($this->client->connect('www.google.com:80')); + } + + /** @group internet */ + public function testConnectionAuthenticationCallback() + { + $called = 0; + $that = $this; + $this->server->setAuth(function ($name, $pass, $remote) use ($that, &$called) { + ++$called; + $that->assertEquals('name', $name); + $that->assertEquals('pass', $pass); + $that->assertStringStartsWith('socks5://name:pass@127.0.0.1:', $remote); + + return true; + }); + + $this->client = new Client('name:pass@127.0.0.1:' . $this->port, $this->connector); + + $this->assertResolveStream($this->client->connect('www.google.com:80')); + $this->assertEquals(1, $called); + } + + /** @group internet */ + public function testConnectionAuthenticationCallbackWillNotBeInvokedIfClientsSendsNoAuth() + { + $called = 0; + $this->server->setAuth(function () use (&$called) { + ++$called; + + return true; + }); + + $this->client = new Client('127.0.0.1:' . $this->port, $this->connector); + + $this->assertRejectPromise($this->client->connect('www.google.com:80')); + $this->assertEquals(0, $called); + } + + /** @group internet */ + public function testConnectionAuthenticationFromUriEncoded() + { + $this->server->setAuthArray(array('name' => 'p@ss:w0rd')); + + $this->client = new Client(rawurlencode('name') . ':' . rawurlencode('p@ss:w0rd') . '@127.0.0.1:' . $this->port, $this->connector); + + $this->assertResolveStream($this->client->connect('www.google.com:80')); + } + + /** @group internet */ + public function testConnectionAuthenticationFromUriWithOnlyUserAndNoPassword() + { + $this->server->setAuthArray(array('empty' => '')); + + $this->client = new Client('empty@127.0.0.1:' . $this->port, $this->connector); + + $this->assertResolveStream($this->client->connect('www.google.com:80')); + } + + /** @group internet */ + public function testConnectionAuthenticationEmptyPassword() + { + $this->server->setAuthArray(array('user' => '')); + $this->client = new Client('user@127.0.0.1:' . $this->port, $this->connector); + + $this->assertResolveStream($this->client->connect('www.google.com:80')); + } + + /** @group internet */ + public function testConnectionAuthenticationUnused() + { + $this->client = new Client('name:pass@127.0.0.1:' . $this->port, $this->connector); + + $this->assertResolveStream($this->client->connect('www.google.com:80')); + } + + public function testConnectionInvalidProtocolDoesNotMatchSocks5() + { + $this->server->setProtocolVersion(5); + $this->client = new Client('socks4a://127.0.0.1:' . $this->port, $this->connector); + + $this->assertRejectPromise($this->client->connect('www.google.com:80'), null, SOCKET_ECONNRESET); + } + + public function testConnectionInvalidProtocolDoesNotMatchSocks4() + { + $this->server->setProtocolVersion(4); + $this->client = new Client('socks5://127.0.0.1:' . $this->port, $this->connector); + + $this->assertRejectPromise($this->client->connect('www.google.com:80'), null, SOCKET_ECONNRESET); + } + + public function testConnectionInvalidNoAuthentication() + { + $this->server->setAuthArray(array('name' => 'pass')); + + $this->client = new Client('socks5://127.0.0.1:' . $this->port, $this->connector); + + $this->assertRejectPromise($this->client->connect('www.google.com:80'), null, SOCKET_EACCES); + } + + public function testConnectionInvalidAuthenticationMismatch() + { + $this->server->setAuthArray(array('name' => 'pass')); + + $this->client = new Client('user:pass@127.0.0.1:' . $this->port, $this->connector); + + $this->assertRejectPromise($this->client->connect('www.google.com:80'), null, SOCKET_EACCES); + } + + /** @group internet */ + public function testConnectorOkay() + { + $this->assertResolveStream($this->client->connect('www.google.com:80')); + } + + /** @group internet */ + public function testConnectorInvalidDomain() + { + $this->assertRejectPromise($this->client->connect('www.google.commm:80')); + } + + /** @group internet */ + public function testConnectorCancelConnection() + { + $promise = $this->client->connect('www.google.com:80'); + $promise->cancel(); + + $this->assertRejectPromise($promise); + } + + /** @group internet */ + public function testConnectorInvalidUnboundPortTimeout() + { + // time out the connection attempt in 0.1s (as expected) + $tcp = new TimeoutConnector($this->client, 0.1, $this->loop); + + $this->assertRejectPromise($tcp->connect('www.google.com:8080')); + } + + /** @group internet */ + public function testSecureConnectorOkay() + { + if (!function_exists('stream_socket_enable_crypto')) { + $this->markTestSkipped('Required function does not exist in your environment (HHVM?)'); + } + + $ssl = new SecureConnector($this->client, $this->loop); + + $this->assertResolveStream($ssl->connect('www.google.com:443')); + } + + /** @group internet */ + public function testSecureConnectorToBadSslWithVerifyFails() + { + if (!function_exists('stream_socket_enable_crypto')) { + $this->markTestSkipped('Required function does not exist in your environment (HHVM?)'); + } + + $ssl = new SecureConnector($this->client, $this->loop, array('verify_peer' => true)); + + $this->assertRejectPromise($ssl->connect('self-signed.badssl.com:443')); + } + + /** @group internet */ + public function testSecureConnectorToBadSslWithoutVerifyWorks() + { + if (!function_exists('stream_socket_enable_crypto')) { + $this->markTestSkipped('Required function does not exist in your environment (HHVM?)'); + } + + $ssl = new SecureConnector($this->client, $this->loop, array('verify_peer' => false)); + + $this->assertResolveStream($ssl->connect('self-signed.badssl.com:443')); + } + + /** @group internet */ + public function testSecureConnectorInvalidPlaintextIsNotSsl() + { + if (!function_exists('stream_socket_enable_crypto')) { + $this->markTestSkipped('Required function does not exist in your environment (HHVM?)'); + } + + $ssl = new SecureConnector($this->client, $this->loop); + + $this->assertRejectPromise($ssl->connect('www.google.com:80')); + } + + /** @group internet */ + public function testSecureConnectorInvalidUnboundPortTimeout() + { + $ssl = new SecureConnector($this->client, $this->loop); + + // time out the connection attempt in 0.1s (as expected) + $ssl = new TimeoutConnector($ssl, 0.1, $this->loop); + + $this->assertRejectPromise($ssl->connect('www.google.com:8080')); + } + + private function assertResolveStream($promise) + { + $this->expectPromiseResolve($promise); + + $promise->then(function ($stream) { + $stream->close(); + }); + + Block\await($promise, $this->loop, 2.0); + } + + private function assertRejectPromise($promise, $message = null, $code = null) + { + $this->expectPromiseReject($promise); + + if (method_exists($this, 'expectException')) { + $this->expectException('Exception'); + if ($message !== null) { + $this->expectExceptionMessage($message); + } + if ($code !== null) { + $this->expectExceptionCode($code); + } + } else { + $this->setExpectedException('Exception', $message, $code); + } + + Block\await($promise, $this->loop, 2.0); + } +} diff --git a/instafeed/vendor/clue/socks-react/tests/ServerTest.php b/instafeed/vendor/clue/socks-react/tests/ServerTest.php new file mode 100755 index 0000000..5c73845 --- /dev/null +++ b/instafeed/vendor/clue/socks-react/tests/ServerTest.php @@ -0,0 +1,428 @@ +getMockBuilder('React\Socket\ServerInterface') + ->getMock(); + + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface') + ->getMock(); + + $this->connector = $this->getMockBuilder('React\Socket\ConnectorInterface') + ->getMock(); + + $this->server = new Server($loop, $socket, $this->connector); + } + + public function testSetProtocolVersion() + { + $this->server->setProtocolVersion(4); + $this->server->setProtocolVersion('4a'); + $this->server->setProtocolVersion(5); + $this->server->setProtocolVersion(null); + + $this->assertTrue(true); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testSetInvalidProtocolVersion() + { + $this->server->setProtocolVersion(6); + } + + public function testSetAuthArray() + { + $this->server->setAuthArray(array()); + + $this->server->setAuthArray(array( + 'name1' => 'password1', + 'name2' => 'password2' + )); + + $this->assertTrue(true); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testSetAuthInvalid() + { + $this->server->setAuth(true); + } + + /** + * @expectedException UnexpectedValueException + */ + public function testUnableToSetAuthIfProtocolDoesNotSupportAuth() + { + $this->server->setProtocolVersion(4); + + $this->server->setAuthArray(array()); + } + + /** + * @expectedException UnexpectedValueException + */ + public function testUnableToSetProtocolWhichDoesNotSupportAuth() + { + $this->server->setAuthArray(array()); + + // this is okay + $this->server->setProtocolVersion(5); + + $this->server->setProtocolVersion(4); + } + + public function testConnectWillCreateConnection() + { + $stream = $this->getMockBuilder('React\Socket\ConnectionInterface')->getMock(); + + $promise = new Promise(function () { }); + + $this->connector->expects($this->once())->method('connect')->with('google.com:80')->willReturn($promise); + + $promise = $this->server->connectTarget($stream, array('google.com', 80)); + + $this->assertInstanceOf('React\Promise\PromiseInterface', $promise); + } + + public function testConnectWillCreateConnectionWithSourceUri() + { + $stream = $this->getMockBuilder('React\Socket\ConnectionInterface')->getMock(); + + $promise = new Promise(function () { }); + + $this->connector->expects($this->once())->method('connect')->with('google.com:80?source=socks5%3A%2F%2F10.20.30.40%3A5060')->willReturn($promise); + + $promise = $this->server->connectTarget($stream, array('google.com', 80, 'socks5://10.20.30.40:5060')); + + $this->assertInstanceOf('React\Promise\PromiseInterface', $promise); + } + + public function testConnectWillRejectIfConnectionFails() + { + $stream = $this->getMockBuilder('React\Socket\ConnectionInterface')->getMock(); + + $promise = new Promise(function ($_, $reject) { $reject(new \RuntimeException()); }); + + $this->connector->expects($this->once())->method('connect')->with('google.com:80')->willReturn($promise); + + $promise = $this->server->connectTarget($stream, array('google.com', 80)); + + $promise->then(null, $this->expectCallableOnce()); + } + + public function testConnectWillCancelConnectionIfStreamCloses() + { + $stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('close'))->getMock(); + + $promise = new Promise(function () { }, function () { + throw new \RuntimeException(); + }); + + + $this->connector->expects($this->once())->method('connect')->with('google.com:80')->willReturn($promise); + + $promise = $this->server->connectTarget($stream, array('google.com', 80)); + + $stream->emit('close'); + + $promise->then(null, $this->expectCallableOnce()); + } + + public function testConnectWillAbortIfPromiseIsCanceled() + { + $stream = $this->getMockBuilder('React\Socket\ConnectionInterface')->getMock(); + + $promise = new Promise(function () { }, function () { + throw new \RuntimeException(); + }); + + $this->connector->expects($this->once())->method('connect')->with('google.com:80')->willReturn($promise); + + $promise = $this->server->connectTarget($stream, array('google.com', 80)); + + $promise->cancel(); + + $promise->then(null, $this->expectCallableOnce()); + } + + public function provideConnectionErrors() + { + return array( + array( + new RuntimeException('', SOCKET_EACCES), + Server::ERROR_NOT_ALLOWED_BY_RULESET + ), + array( + new RuntimeException('', SOCKET_ENETUNREACH), + Server::ERROR_NETWORK_UNREACHABLE + ), + array( + new RuntimeException('', SOCKET_EHOSTUNREACH), + Server::ERROR_HOST_UNREACHABLE, + ), + array( + new RuntimeException('', SOCKET_ECONNREFUSED), + Server::ERROR_CONNECTION_REFUSED + ), + array( + new RuntimeException('Connection refused'), + Server::ERROR_CONNECTION_REFUSED + ), + array( + new RuntimeException('', SOCKET_ETIMEDOUT), + Server::ERROR_TTL + ), + array( + new TimeoutException(1.0), + Server::ERROR_TTL + ), + array( + new RuntimeException(), + Server::ERROR_GENERAL + ) + ); + } + + /** + * @dataProvider provideConnectionErrors + * @param Exception $error + * @param int $expectedCode + */ + public function testConnectWillReturnMappedSocks5ErrorCodeFromConnector($error, $expectedCode) + { + $stream = $this->getMockBuilder('React\Socket\ConnectionInterface')->getMock(); + + $promise = \React\Promise\reject($error); + + $this->connector->expects($this->once())->method('connect')->willReturn($promise); + + $promise = $this->server->connectTarget($stream, array('google.com', 80)); + + $code = null; + $promise->then(null, function ($error) use (&$code) { + $code = $error->getCode(); + }); + + $this->assertEquals($expectedCode, $code); + } + + public function testHandleSocksConnectionWillEndOnInvalidData() + { + $connection = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('pause', 'end'))->getMock(); + $connection->expects($this->once())->method('pause'); + $connection->expects($this->once())->method('end'); + + $this->server->onConnection($connection); + + $connection->emit('data', array('asdasdasdasdasd')); + } + + public function testHandleSocks4ConnectionWithIpv4WillEstablishOutgoingConnection() + { + $connection = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('pause', 'end'))->getMock(); + + $promise = new Promise(function () { }); + + $this->connector->expects($this->once())->method('connect')->with('127.0.0.1:80')->willReturn($promise); + + $this->server->onConnection($connection); + + $connection->emit('data', array("\x04\x01" . "\x00\x50" . pack('N', ip2long('127.0.0.1')) . "\x00")); + } + + public function testHandleSocks4aConnectionWithHostnameWillEstablishOutgoingConnection() + { + $connection = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('pause', 'end'))->getMock(); + + $promise = new Promise(function () { }); + + $this->connector->expects($this->once())->method('connect')->with('example.com:80')->willReturn($promise); + + $this->server->onConnection($connection); + + $connection->emit('data', array("\x04\x01" . "\x00\x50" . "\x00\x00\x00\x01" . "\x00" . "example.com" . "\x00")); + } + + public function testHandleSocks4aConnectionWithHostnameAndSourceAddressWillEstablishOutgoingConnection() + { + $connection = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('pause', 'end', 'getRemoteAddress'))->getMock(); + $connection->expects($this->once())->method('getRemoteAddress')->willReturn('tcp://10.20.30.40:5060'); + + $promise = new Promise(function () { }); + + $this->connector->expects($this->once())->method('connect')->with('example.com:80?source=socks4%3A%2F%2F10.20.30.40%3A5060')->willReturn($promise); + + $this->server->onConnection($connection); + + $connection->emit('data', array("\x04\x01" . "\x00\x50" . "\x00\x00\x00\x01" . "\x00" . "example.com" . "\x00")); + } + + public function testHandleSocks4aConnectionWithSecureTlsSourceAddressWillEstablishOutgoingConnection() + { + $connection = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('pause', 'end', 'getRemoteAddress'))->getMock(); + $connection->expects($this->once())->method('getRemoteAddress')->willReturn('tls://10.20.30.40:5060'); + + $promise = new Promise(function () { }); + + $this->connector->expects($this->once())->method('connect')->with('example.com:80?source=socks4s%3A%2F%2F10.20.30.40%3A5060')->willReturn($promise); + + $this->server->onConnection($connection); + + $connection->emit('data', array("\x04\x01" . "\x00\x50" . "\x00\x00\x00\x01" . "\x00" . "example.com" . "\x00")); + } + + public function testHandleSocks4aConnectionWithInvalidHostnameWillNotEstablishOutgoingConnection() + { + $connection = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('pause', 'end'))->getMock(); + + $this->connector->expects($this->never())->method('connect'); + + $this->server->onConnection($connection); + + $connection->emit('data', array("\x04\x01" . "\x00\x50" . "\x00\x00\x00\x01" . "\x00" . "tls://example.com:80?" . "\x00")); + } + + public function testHandleSocks5ConnectionWithIpv4WillEstablishOutgoingConnection() + { + $connection = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('pause', 'end', 'write'))->getMock(); + + $promise = new Promise(function () { }); + + $this->connector->expects($this->once())->method('connect')->with('127.0.0.1:80')->willReturn($promise); + + $this->server->onConnection($connection); + + $connection->emit('data', array("\x05\x01\x00" . "\x05\x01\x00\x01" . pack('N', ip2long('127.0.0.1')) . "\x00\x50")); + } + + public function testHandleSocks5ConnectionWithIpv4AndSourceAddressWillEstablishOutgoingConnection() + { + $connection = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('pause', 'end', 'write', 'getRemoteAddress'))->getMock(); + $connection->expects($this->once())->method('getRemoteAddress')->willReturn('tcp://10.20.30.40:5060'); + + $promise = new Promise(function () { }); + + $this->connector->expects($this->once())->method('connect')->with('127.0.0.1:80?source=socks5%3A%2F%2F10.20.30.40%3A5060')->willReturn($promise); + + $this->server->onConnection($connection); + + $connection->emit('data', array("\x05\x01\x00" . "\x05\x01\x00\x01" . pack('N', ip2long('127.0.0.1')) . "\x00\x50")); + } + + public function testHandleSocks5ConnectionWithSecureTlsIpv4AndSourceAddressWillEstablishOutgoingConnection() + { + $connection = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('pause', 'end', 'write', 'getRemoteAddress'))->getMock(); + $connection->expects($this->once())->method('getRemoteAddress')->willReturn('tls://10.20.30.40:5060'); + + $promise = new Promise(function () { }); + + $this->connector->expects($this->once())->method('connect')->with('127.0.0.1:80?source=socks5s%3A%2F%2F10.20.30.40%3A5060')->willReturn($promise); + + $this->server->onConnection($connection); + + $connection->emit('data', array("\x05\x01\x00" . "\x05\x01\x00\x01" . pack('N', ip2long('127.0.0.1')) . "\x00\x50")); + } + + public function testHandleSocks5ConnectionWithIpv6WillEstablishOutgoingConnection() + { + $connection = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('pause', 'end', 'write'))->getMock(); + + $promise = new Promise(function () { }); + + $this->connector->expects($this->once())->method('connect')->with('[::1]:80')->willReturn($promise); + + $this->server->onConnection($connection); + + $connection->emit('data', array("\x05\x01\x00" . "\x05\x01\x00\x04" . inet_pton('::1') . "\x00\x50")); + } + + public function testHandleSocks5ConnectionWithHostnameWillEstablishOutgoingConnection() + { + $connection = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('pause', 'end', 'write'))->getMock(); + + $promise = new Promise(function () { }); + + $this->connector->expects($this->once())->method('connect')->with('example.com:80')->willReturn($promise); + + $this->server->onConnection($connection); + + $connection->emit('data', array("\x05\x01\x00" . "\x05\x01\x00\x03\x0B" . "example.com" . "\x00\x50")); + } + + public function testHandleSocks5ConnectionWithConnectorRefusedWillReturnReturnRefusedError() + { + $connection = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('pause', 'end', 'write'))->getMock(); + + $promise = \React\Promise\reject(new RuntimeException('Connection refused')); + + $this->connector->expects($this->once())->method('connect')->with('example.com:80')->willReturn($promise); + + $this->server->onConnection($connection); + + $connection->expects($this->exactly(2))->method('write')->withConsecutive(array("\x05\x00"), array("\x05\x05" . "\x00\x01\x00\x00\x00\x00\x00\x00")); + + $connection->emit('data', array("\x05\x01\x00" . "\x05\x01\x00\x03\x0B" . "example.com" . "\x00\x50")); + } + + public function testHandleSocks5UdpCommandWillNotEstablishOutgoingConnectionAndReturnCommandError() + { + $connection = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('pause', 'end', 'write'))->getMock(); + + $this->connector->expects($this->never())->method('connect'); + + $this->server->onConnection($connection); + + $connection->expects($this->exactly(2))->method('write')->withConsecutive(array("\x05\x00"), array("\x05\x07" . "\x00\x01\x00\x00\x00\x00\x00\x00")); + + $connection->emit('data', array("\x05\x01\x00" . "\x05\x03\x00\x03\x0B" . "example.com" . "\x00\x50")); + } + + public function testHandleSocks5ConnectionWithInvalidHostnameWillNotEstablishOutgoingConnectionAndReturnGeneralError() + { + $connection = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('pause', 'end', 'write'))->getMock(); + + $this->connector->expects($this->never())->method('connect'); + + $this->server->onConnection($connection); + + $connection->expects($this->exactly(2))->method('write')->withConsecutive(array("\x05\x00"), array("\x05\x01" . "\x00\x01\x00\x00\x00\x00\x00\x00")); + + $connection->emit('data', array("\x05\x01\x00" . "\x05\x01\x00\x03\x15" . "tls://example.com:80?" . "\x00\x50")); + } + + public function testHandleSocksConnectionWillCancelOutputConnectionIfIncomingCloses() + { + $connection = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('pause', 'end'))->getMock(); + + $promise = new Promise(function () { }, $this->expectCallableOnce()); + + $this->connector->expects($this->once())->method('connect')->with('127.0.0.1:80')->willReturn($promise); + + $this->server->onConnection($connection); + + $connection->emit('data', array("\x04\x01" . "\x00\x50" . pack('N', ip2long('127.0.0.1')) . "\x00")); + $connection->emit('close'); + } + + public function testUnsetAuth() + { + $this->server->unsetAuth(); + $this->server->unsetAuth(); + + $this->assertTrue(true); + } +} diff --git a/instafeed/vendor/clue/socks-react/tests/StreamReaderTest.php b/instafeed/vendor/clue/socks-react/tests/StreamReaderTest.php new file mode 100755 index 0000000..a2a0d95 --- /dev/null +++ b/instafeed/vendor/clue/socks-react/tests/StreamReaderTest.php @@ -0,0 +1,82 @@ +reader = new StreamReader(); + } + + public function testReadByteAssertCorrect() + { + $this->reader->readByteAssert(0x01)->then($this->expectCallableOnce(0x01)); + + $this->reader->write("\x01"); + } + + public function testReadByteAssertInvalid() + { + $this->reader->readByteAssert(0x02)->then(null, $this->expectCallableOnce()); + + $this->reader->write("\x03"); + } + + public function testReadStringNull() + { + $this->reader->readStringNull()->then($this->expectCallableOnce('hello')); + + $this->reader->write("hello\x00"); + } + + public function testReadStringLength() + { + $this->reader->readLength(5)->then($this->expectCallableOnce('hello')); + + $this->reader->write('he'); + $this->reader->write('ll'); + $this->reader->write('o '); + + $this->assertEquals(' ', $this->reader->getBuffer()); + } + + public function testReadBuffered() + { + $this->reader->write('hello'); + + $this->reader->readLength(5)->then($this->expectCallableOnce('hello')); + + $this->assertEquals('', $this->reader->getBuffer()); + } + + public function testSequence() + { + $this->reader->readByte()->then($this->expectCallableOnce(ord('h'))); + $this->reader->readByteAssert(ord('e'))->then($this->expectCallableOnce(ord('e'))); + $this->reader->readLength(4)->then($this->expectCallableOnce('llo ')); + $this->reader->readBinary(array('w'=>'C', 'o' => 'C'))->then($this->expectCallableOnce(array('w' => ord('w'), 'o' => ord('o')))); + + $this->reader->write('hello world'); + + $this->assertEquals('rld', $this->reader->getBuffer()); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testInvalidStructure() + { + $this->reader->readBinary(array('invalid' => 'y')); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testInvalidCallback() + { + $this->reader->readBufferCallback(array()); + } +} diff --git a/instafeed/vendor/clue/socks-react/tests/bootstrap.php b/instafeed/vendor/clue/socks-react/tests/bootstrap.php new file mode 100755 index 0000000..029c6b9 --- /dev/null +++ b/instafeed/vendor/clue/socks-react/tests/bootstrap.php @@ -0,0 +1,103 @@ +createCallableMock(); + + + if (func_num_args() > 0) { + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->equalTo(func_get_arg(0))); + } else { + $mock + ->expects($this->once()) + ->method('__invoke'); + } + + return $mock; + } + + protected function expectCallableNever() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->never()) + ->method('__invoke'); + + return $mock; + } + + protected function expectCallableOnceWithExceptionCode($code) + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->callback(function ($e) use ($code) { + return $e->getCode() === $code; + })); + + return $mock; + } + + protected function expectCallableOnceParameter($type) + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->isInstanceOf($type)); + + return $mock; + } + + /** + * @link https://github.com/reactphp/react/blob/master/tests/React/Tests/Socket/TestCase.php (taken from reactphp/react) + */ + protected function createCallableMock() + { + return $this->getMockBuilder('CallableStub')->getMock(); + } + + protected function expectPromiseResolve($promise) + { + $this->assertInstanceOf('React\Promise\PromiseInterface', $promise); + + $that = $this; + $promise->then(null, function($error) use ($that) { + $that->assertNull($error); + $that->fail('promise rejected'); + }); + $promise->then($this->expectCallableOnce(), $this->expectCallableNever()); + + return $promise; + } + + protected function expectPromiseReject($promise) + { + $this->assertInstanceOf('React\Promise\PromiseInterface', $promise); + + $that = $this; + $promise->then(function($value) use ($that) { + $that->assertNull($value); + $that->fail('promise resolved'); + }); + + $promise->then($this->expectCallableNever(), $this->expectCallableOnce()); + + return $promise; + } +} + +class CallableStub +{ + public function __invoke() + { + } +} diff --git a/instafeed/vendor/composer/ClassLoader.php b/instafeed/vendor/composer/ClassLoader.php new file mode 100755 index 0000000..fce8549 --- /dev/null +++ b/instafeed/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') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $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])) { + $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); + foreach ($this->prefixDirsPsr4[$search] as $dir) { + if (file_exists($file = $dir . $pathEnd)) { + 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/instafeed/vendor/composer/LICENSE b/instafeed/vendor/composer/LICENSE new file mode 100755 index 0000000..4b615a3 --- /dev/null +++ b/instafeed/vendor/composer/LICENSE @@ -0,0 +1,56 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: Composer +Upstream-Contact: Jordi Boggiano +Source: https://github.com/composer/composer + +Files: * +Copyright: 2016, Nils Adermann + 2016, Jordi Boggiano +License: Expat + +Files: src/Composer/Util/TlsHelper.php +Copyright: 2016, Nils Adermann + 2016, Jordi Boggiano + 2013, Evan Coury +License: Expat and BSD-2-Clause + +License: BSD-2-Clause + 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. + . + 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. + +License: Expat + 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/instafeed/vendor/composer/autoload_classmap.php b/instafeed/vendor/composer/autoload_classmap.php new file mode 100755 index 0000000..7a91153 --- /dev/null +++ b/instafeed/vendor/composer/autoload_classmap.php @@ -0,0 +1,9 @@ + $vendorDir . '/react/promise/src/functions_include.php', + '972fda704d680a3a53c68e34e193cb22' => $vendorDir . '/react/promise-timer/src/functions_include.php', + '7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php', + 'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php', + 'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php', + 'ebf8799635f67b5d7248946fe2154f4a' => $vendorDir . '/ringcentral/psr7/src/functions_include.php', + '37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php', +); diff --git a/instafeed/vendor/composer/autoload_namespaces.php b/instafeed/vendor/composer/autoload_namespaces.php new file mode 100755 index 0000000..02066fb --- /dev/null +++ b/instafeed/vendor/composer/autoload_namespaces.php @@ -0,0 +1,10 @@ + array($vendorDir . '/evenement/evenement/src'), +); diff --git a/instafeed/vendor/composer/autoload_psr4.php b/instafeed/vendor/composer/autoload_psr4.php new file mode 100755 index 0000000..5d4f0c7 --- /dev/null +++ b/instafeed/vendor/composer/autoload_psr4.php @@ -0,0 +1,32 @@ + array($vendorDir . '/winbox/args/src'), + 'Symfony\\Component\\Process\\' => array($vendorDir . '/symfony/process'), + 'RingCentral\\Psr7\\' => array($vendorDir . '/ringcentral/psr7/src'), + 'React\\Stream\\' => array($vendorDir . '/react/stream/src'), + 'React\\Socket\\' => array($vendorDir . '/react/socket/src'), + 'React\\Promise\\Timer\\' => array($vendorDir . '/react/promise-timer/src'), + 'React\\Promise\\' => array($vendorDir . '/react/promise/src'), + 'React\\EventLoop\\' => array($vendorDir . '/react/event-loop/src'), + 'React\\Dns\\' => array($vendorDir . '/react/dns/src'), + 'React\\Cache\\' => array($vendorDir . '/react/cache/src'), + 'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'), + 'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'), + 'LazyJsonMapper\\' => array($vendorDir . '/lazyjsonmapper/lazyjsonmapper/src'), + 'InstagramAPI\\' => array($vendorDir . '/mgp25/instagram-php/src'), + 'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'), + 'GuzzleHttp\\Promise\\' => array($vendorDir . '/guzzlehttp/promises/src'), + 'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'), + 'GetOptionKit\\' => array($vendorDir . '/corneltek/getoptionkit/src'), + 'Fbns\\Client\\' => array($vendorDir . '/valga/fbns-react/src'), + 'Clue\\React\\Socks\\' => array($vendorDir . '/clue/socks-react/src'), + 'Clue\\React\\HttpProxy\\' => array($vendorDir . '/clue/http-proxy-react/src'), + 'BinSoul\\Net\\Mqtt\\Client\\React\\' => array($vendorDir . '/binsoul/net-mqtt-client-react/src'), + 'BinSoul\\Net\\Mqtt\\' => array($vendorDir . '/binsoul/net-mqtt/src'), +); diff --git a/instafeed/vendor/composer/autoload_real.php b/instafeed/vendor/composer/autoload_real.php new file mode 100755 index 0000000..6a318f4 --- /dev/null +++ b/instafeed/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\ComposerStaticInit331ae81537359437b02a96bc74f11b80::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\ComposerStaticInit331ae81537359437b02a96bc74f11b80::$files; + } else { + $includeFiles = require __DIR__ . '/autoload_files.php'; + } + foreach ($includeFiles as $fileIdentifier => $file) { + composerRequire331ae81537359437b02a96bc74f11b80($fileIdentifier, $file); + } + + return $loader; + } +} + +function composerRequire331ae81537359437b02a96bc74f11b80($fileIdentifier, $file) +{ + if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { + require $file; + + $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; + } +} diff --git a/instafeed/vendor/composer/autoload_static.php b/instafeed/vendor/composer/autoload_static.php new file mode 100755 index 0000000..5c67b20 --- /dev/null +++ b/instafeed/vendor/composer/autoload_static.php @@ -0,0 +1,189 @@ + __DIR__ . '/..' . '/react/promise/src/functions_include.php', + '972fda704d680a3a53c68e34e193cb22' => __DIR__ . '/..' . '/react/promise-timer/src/functions_include.php', + '7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php', + 'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php', + 'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php', + 'ebf8799635f67b5d7248946fe2154f4a' => __DIR__ . '/..' . '/ringcentral/psr7/src/functions_include.php', + '37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php', + ); + + public static $prefixLengthsPsr4 = array ( + 'W' => + array ( + 'Winbox\\' => 7, + ), + 'S' => + array ( + 'Symfony\\Component\\Process\\' => 26, + ), + 'R' => + array ( + 'RingCentral\\Psr7\\' => 17, + 'React\\Stream\\' => 13, + 'React\\Socket\\' => 13, + 'React\\Promise\\Timer\\' => 20, + 'React\\Promise\\' => 14, + 'React\\EventLoop\\' => 16, + 'React\\Dns\\' => 10, + 'React\\Cache\\' => 12, + ), + 'P' => + array ( + 'Psr\\Log\\' => 8, + 'Psr\\Http\\Message\\' => 17, + ), + 'L' => + array ( + 'LazyJsonMapper\\' => 15, + ), + 'I' => + array ( + 'InstagramAPI\\' => 13, + ), + 'G' => + array ( + 'GuzzleHttp\\Psr7\\' => 16, + 'GuzzleHttp\\Promise\\' => 19, + 'GuzzleHttp\\' => 11, + 'GetOptionKit\\' => 13, + ), + 'F' => + array ( + 'Fbns\\Client\\' => 12, + ), + 'C' => + array ( + 'Clue\\React\\Socks\\' => 17, + 'Clue\\React\\HttpProxy\\' => 21, + ), + 'B' => + array ( + 'BinSoul\\Net\\Mqtt\\Client\\React\\' => 30, + 'BinSoul\\Net\\Mqtt\\' => 17, + ), + ); + + public static $prefixDirsPsr4 = array ( + 'Winbox\\' => + array ( + 0 => __DIR__ . '/..' . '/winbox/args/src', + ), + 'Symfony\\Component\\Process\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/process', + ), + 'RingCentral\\Psr7\\' => + array ( + 0 => __DIR__ . '/..' . '/ringcentral/psr7/src', + ), + 'React\\Stream\\' => + array ( + 0 => __DIR__ . '/..' . '/react/stream/src', + ), + 'React\\Socket\\' => + array ( + 0 => __DIR__ . '/..' . '/react/socket/src', + ), + 'React\\Promise\\Timer\\' => + array ( + 0 => __DIR__ . '/..' . '/react/promise-timer/src', + ), + 'React\\Promise\\' => + array ( + 0 => __DIR__ . '/..' . '/react/promise/src', + ), + 'React\\EventLoop\\' => + array ( + 0 => __DIR__ . '/..' . '/react/event-loop/src', + ), + 'React\\Dns\\' => + array ( + 0 => __DIR__ . '/..' . '/react/dns/src', + ), + 'React\\Cache\\' => + array ( + 0 => __DIR__ . '/..' . '/react/cache/src', + ), + 'Psr\\Log\\' => + array ( + 0 => __DIR__ . '/..' . '/psr/log/Psr/Log', + ), + 'Psr\\Http\\Message\\' => + array ( + 0 => __DIR__ . '/..' . '/psr/http-message/src', + ), + 'LazyJsonMapper\\' => + array ( + 0 => __DIR__ . '/..' . '/lazyjsonmapper/lazyjsonmapper/src', + ), + 'InstagramAPI\\' => + array ( + 0 => __DIR__ . '/..' . '/mgp25/instagram-php/src', + ), + 'GuzzleHttp\\Psr7\\' => + array ( + 0 => __DIR__ . '/..' . '/guzzlehttp/psr7/src', + ), + 'GuzzleHttp\\Promise\\' => + array ( + 0 => __DIR__ . '/..' . '/guzzlehttp/promises/src', + ), + 'GuzzleHttp\\' => + array ( + 0 => __DIR__ . '/..' . '/guzzlehttp/guzzle/src', + ), + 'GetOptionKit\\' => + array ( + 0 => __DIR__ . '/..' . '/corneltek/getoptionkit/src', + ), + 'Fbns\\Client\\' => + array ( + 0 => __DIR__ . '/..' . '/valga/fbns-react/src', + ), + 'Clue\\React\\Socks\\' => + array ( + 0 => __DIR__ . '/..' . '/clue/socks-react/src', + ), + 'Clue\\React\\HttpProxy\\' => + array ( + 0 => __DIR__ . '/..' . '/clue/http-proxy-react/src', + ), + 'BinSoul\\Net\\Mqtt\\Client\\React\\' => + array ( + 0 => __DIR__ . '/..' . '/binsoul/net-mqtt-client-react/src', + ), + 'BinSoul\\Net\\Mqtt\\' => + array ( + 0 => __DIR__ . '/..' . '/binsoul/net-mqtt/src', + ), + ); + + public static $prefixesPsr0 = array ( + 'E' => + array ( + 'Evenement' => + array ( + 0 => __DIR__ . '/..' . '/evenement/evenement/src', + ), + ), + ); + + public static function getInitializer(ClassLoader $loader) + { + return \Closure::bind(function () use ($loader) { + $loader->prefixLengthsPsr4 = ComposerStaticInit331ae81537359437b02a96bc74f11b80::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInit331ae81537359437b02a96bc74f11b80::$prefixDirsPsr4; + $loader->prefixesPsr0 = ComposerStaticInit331ae81537359437b02a96bc74f11b80::$prefixesPsr0; + + }, null, ClassLoader::class); + } +} diff --git a/instafeed/vendor/composer/installed.json b/instafeed/vendor/composer/installed.json new file mode 100755 index 0000000..46699b3 --- /dev/null +++ b/instafeed/vendor/composer/installed.json @@ -0,0 +1,1328 @@ +[ + { + "name": "binsoul/net-mqtt", + "version": "0.2.1", + "version_normalized": "0.2.1.0", + "source": { + "type": "git", + "url": "https://github.com/binsoul/net-mqtt.git", + "reference": "286b28e6014739b19e0e7ce0cd5871cdd0cef9b3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/binsoul/net-mqtt/zipball/286b28e6014739b19e0e7ce0cd5871cdd0cef9b3", + "reference": "286b28e6014739b19e0e7ce0cd5871cdd0cef9b3", + "shasum": "" + }, + "require": { + "php": "~5.6|~7.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~1.0", + "phpunit/phpunit": "~4.0||~5.0" + }, + "time": "2017-04-03T20:17:02+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "BinSoul\\Net\\Mqtt\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sebastian Mößler", + "email": "code@binsoul.de", + "homepage": "https://github.com/binsoul", + "role": "Developer" + } + ], + "description": "MQTT protocol implementation", + "homepage": "https://github.com/binsoul/net-mqtt", + "keywords": [ + "mqtt", + "net" + ] + }, + { + "name": "binsoul/net-mqtt-client-react", + "version": "0.3.2", + "version_normalized": "0.3.2.0", + "source": { + "type": "git", + "url": "https://github.com/binsoul/net-mqtt-client-react.git", + "reference": "6a80fea50e927ebb8bb8a631ea7903c22742ded5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/binsoul/net-mqtt-client-react/zipball/6a80fea50e927ebb8bb8a631ea7903c22742ded5", + "reference": "6a80fea50e927ebb8bb8a631ea7903c22742ded5", + "shasum": "" + }, + "require": { + "binsoul/net-mqtt": "~0.2", + "php": "~5.6|~7.0", + "react/promise": "~2.0", + "react/socket": "~0.8" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~1.0", + "phpunit/phpunit": "~4.0||~5.0" + }, + "time": "2017-08-20T08:06:53+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "BinSoul\\Net\\Mqtt\\Client\\React\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sebastian Mößler", + "email": "code@binsoul.de", + "homepage": "https://github.com/binsoul", + "role": "Developer" + } + ], + "description": "Asynchronous MQTT client built on React", + "homepage": "https://github.com/binsoul/net-mqtt-client-react", + "keywords": [ + "client", + "mqtt", + "net" + ] + }, + { + "name": "clue/http-proxy-react", + "version": "v1.3.0", + "version_normalized": "1.3.0.0", + "source": { + "type": "git", + "url": "https://github.com/clue/php-http-proxy-react.git", + "reference": "eeff725640ed53386a6adb05ffdbfc2837404fdf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/clue/php-http-proxy-react/zipball/eeff725640ed53386a6adb05ffdbfc2837404fdf", + "reference": "eeff725640ed53386a6adb05ffdbfc2837404fdf", + "shasum": "" + }, + "require": { + "php": ">=5.3", + "react/promise": " ^2.1 || ^1.2.1", + "react/socket": "^1.0 || ^0.8.4", + "ringcentral/psr7": "^1.2" + }, + "require-dev": { + "clue/block-react": "^1.1", + "phpunit/phpunit": "^5.0 || ^4.8", + "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3" + }, + "time": "2018-02-13T16:31:32+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Clue\\React\\HttpProxy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@lueck.tv" + } + ], + "description": "Async HTTP proxy connector, use any TCP/IP-based protocol through an HTTP CONNECT proxy server, built on top of ReactPHP", + "homepage": "https://github.com/clue/php-http-proxy-react", + "keywords": [ + "async", + "connect", + "http", + "proxy", + "reactphp" + ] + }, + { + "name": "clue/socks-react", + "version": "v0.8.7", + "version_normalized": "0.8.7.0", + "source": { + "type": "git", + "url": "https://github.com/clue/php-socks-react.git", + "reference": "0fcd6f2f506918ff003f1b995c6e78443f26e8ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/clue/php-socks-react/zipball/0fcd6f2f506918ff003f1b995c6e78443f26e8ea", + "reference": "0fcd6f2f506918ff003f1b995c6e78443f26e8ea", + "shasum": "" + }, + "require": { + "evenement/evenement": "~3.0|~1.0|~2.0", + "php": ">=5.3", + "react/promise": "^2.1 || ^1.2", + "react/socket": "^1.0 || ^0.8.6" + }, + "require-dev": { + "clue/block-react": "^1.1", + "clue/connection-manager-extra": "^1.0 || ^0.7", + "phpunit/phpunit": "^6.0 || ^5.7 || ^4.8.35", + "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3" + }, + "time": "2017-12-17T14:47:58+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Clue\\React\\Socks\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@lueck.tv" + } + ], + "description": "Async SOCKS4, SOCKS4a and SOCKS5 proxy client and server implementation, built on top of ReactPHP", + "homepage": "https://github.com/clue/php-socks-react", + "keywords": [ + "async", + "proxy", + "reactphp", + "socks client", + "socks protocol", + "socks server", + "tcp tunnel" + ] + }, + { + "name": "corneltek/getoptionkit", + "version": "2.6.0", + "version_normalized": "2.6.0.0", + "source": { + "type": "git", + "url": "https://github.com/c9s/GetOptionKit.git", + "reference": "995607ddf4fc90ebdb4a7d58fe972d581ad8495f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/c9s/GetOptionKit/zipball/995607ddf4fc90ebdb4a7d58fe972d581ad8495f", + "reference": "995607ddf4fc90ebdb4a7d58fe972d581ad8495f", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "time": "2017-06-30T14:54:48+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "GetOptionKit\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Yo-An Lin", + "email": "yoanlin93@gmail.com" + } + ], + "description": "Powerful command-line option toolkit", + "homepage": "http://github.com/c9s/GetOptionKit" + }, + { + "name": "evenement/evenement", + "version": "v3.0.1", + "version_normalized": "3.0.1.0", + "source": { + "type": "git", + "url": "https://github.com/igorw/evenement.git", + "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/igorw/evenement/zipball/531bfb9d15f8aa57454f5f0285b18bec903b8fb7", + "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7", + "shasum": "" + }, + "require": { + "php": ">=7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "time": "2017-07-23T21:35:13+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-0": { + "Evenement": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + } + ], + "description": "Événement is a very simple event dispatching library for PHP", + "keywords": [ + "event-dispatcher", + "event-emitter" + ] + }, + { + "name": "guzzlehttp/guzzle", + "version": "6.4.1", + "version_normalized": "6.4.1.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "0895c932405407fd3a7368b6910c09a24d26db11" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/0895c932405407fd3a7368b6910c09a24d26db11", + "reference": "0895c932405407fd3a7368b6910c09a24d26db11", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^1.0", + "guzzlehttp/psr7": "^1.6.1", + "php": ">=5.5" + }, + "require-dev": { + "ext-curl": "*", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", + "psr/log": "^1.1" + }, + "suggest": { + "psr/log": "Required for using the Log middleware" + }, + "time": "2019-10-23T15:58:00+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "6.3-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "GuzzleHttp\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "rest", + "web service" + ] + }, + { + "name": "guzzlehttp/promises", + "version": "v1.3.1", + "version_normalized": "1.3.1.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646", + "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646", + "shasum": "" + }, + "require": { + "php": ">=5.5.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0" + }, + "time": "2016-12-20T10:07:11+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ] + }, + { + "name": "guzzlehttp/psr7", + "version": "1.6.1", + "version_normalized": "1.6.1.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "239400de7a173fe9901b9ac7c06497751f00727a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/239400de7a173fe9901b9ac7c06497751f00727a", + "reference": "239400de7a173fe9901b9ac7c06497751f00727a", + "shasum": "" + }, + "require": { + "php": ">=5.4.0", + "psr/http-message": "~1.0", + "ralouphie/getallheaders": "^2.0.5 || ^3.0.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "ext-zlib": "*", + "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8" + }, + "suggest": { + "zendframework/zend-httphandlerrunner": "Emit PSR-7 responses" + }, + "time": "2019-07-01T23:21:34+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Schultze", + "homepage": "https://github.com/Tobion" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ] + }, + { + "name": "lazyjsonmapper/lazyjsonmapper", + "version": "v1.6.3", + "version_normalized": "1.6.3.0", + "source": { + "type": "git", + "url": "https://github.com/lazyjsonmapper/lazyjsonmapper.git", + "reference": "51e093b50f4de15d2d64548b3ca743713eed6ee9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/lazyjsonmapper/lazyjsonmapper/zipball/51e093b50f4de15d2d64548b3ca743713eed6ee9", + "reference": "51e093b50f4de15d2d64548b3ca743713eed6ee9", + "shasum": "" + }, + "require": { + "corneltek/getoptionkit": "2.*", + "php": ">=5.6" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.7.1", + "phpunit/phpunit": "6.*" + }, + "time": "2018-05-02T16:57:09+00:00", + "bin": [ + "bin/lazydoctor" + ], + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "LazyJsonMapper\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "SteveJobzniak", + "role": "Developer", + "homepage": "https://github.com/SteveJobzniak" + } + ], + "description": "Advanced, intelligent & automatic object-oriented JSON containers for PHP.", + "homepage": "https://github.com/SteveJobzniak/LazyJsonMapper", + "keywords": [ + "development", + "json" + ] + }, + { + "name": "mgp25/instagram-php", + "version": "v7.0.1", + "version_normalized": "7.0.1.0", + "source": { + "type": "git", + "url": "https://github.com/mgp25/Instagram-API.git", + "reference": "53421f90b9ef7743f1c6221c4963f2b9f7a592e8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mgp25/Instagram-API/zipball/53421f90b9ef7743f1c6221c4963f2b9f7a592e8", + "reference": "53421f90b9ef7743f1c6221c4963f2b9f7a592e8", + "shasum": "" + }, + "require": { + "binsoul/net-mqtt-client-react": "^0.3.2", + "clue/http-proxy-react": "^1.1.0", + "clue/socks-react": "^0.8.2", + "ext-bcmath": "*", + "ext-curl": "*", + "ext-exif": "*", + "ext-gd": "*", + "ext-mbstring": "*", + "ext-zlib": "*", + "guzzlehttp/guzzle": "^6.2", + "lazyjsonmapper/lazyjsonmapper": "^1.6.1", + "php": ">=5.6", + "psr/log": "^1.0", + "react/event-loop": "^0.4.3", + "react/promise": "^2.5", + "react/socket": "^0.8", + "symfony/process": "^3.4|^4.0", + "valga/fbns-react": "^0.1.8", + "winbox/args": "1.0.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.11.0", + "monolog/monolog": "^1.23", + "phpunit/phpunit": "^5.7 || ^6.2", + "react/http": "^0.7.2" + }, + "suggest": { + "ext-event": "Installing PHP's native Event extension enables faster Realtime class event handling." + }, + "time": "2019-09-17T00:56:42+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "InstagramAPI\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "RPL-1.5", + "proprietary" + ], + "authors": [ + { + "name": "mgp25", + "email": "me@mgp25.com", + "role": "Founder" + }, + { + "name": "SteveJobzniak", + "homepage": "https://github.com/SteveJobzniak", + "role": "Developer" + } + ], + "description": "Instagram's private API for PHP", + "keywords": [ + "api", + "instagram", + "php", + "private" + ] + }, + { + "name": "psr/http-message", + "version": "1.0.1", + "version_normalized": "1.0.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "time": "2016-08-06T14:39:51+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ] + }, + { + "name": "psr/log", + "version": "1.1.1", + "version_normalized": "1.1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "bf73deb2b3b896a9d9c75f3f0d88185d2faa27e2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/bf73deb2b3b896a9d9c75f3f0d88185d2faa27e2", + "reference": "bf73deb2b3b896a9d9c75f3f0d88185d2faa27e2", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "time": "2019-10-25T08:06:51+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.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": "ralouphie/getallheaders", + "version": "3.0.3", + "version_normalized": "3.0.3.0", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "time": "2019-03-08T08:55:37+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders." + }, + { + "name": "react/cache", + "version": "v1.0.0", + "version_normalized": "1.0.0.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/cache.git", + "reference": "aa10d63a1b40a36a486bdf527f28bac607ee6466" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/cache/zipball/aa10d63a1b40a36a486bdf527f28bac607ee6466", + "reference": "aa10d63a1b40a36a486bdf527f28bac607ee6466", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "react/promise": "~2.0|~1.1" + }, + "require-dev": { + "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35" + }, + "time": "2019-07-11T13:45:28+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "React\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Async, Promise-based cache interface for ReactPHP", + "keywords": [ + "cache", + "caching", + "promise", + "reactphp" + ] + }, + { + "name": "react/dns", + "version": "v0.4.19", + "version_normalized": "0.4.19.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/dns.git", + "reference": "6852fb98e22d2e5bb35fe5aeeaa96551b120e7c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/dns/zipball/6852fb98e22d2e5bb35fe5aeeaa96551b120e7c9", + "reference": "6852fb98e22d2e5bb35fe5aeeaa96551b120e7c9", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "react/cache": "^1.0 || ^0.6 || ^0.5", + "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5", + "react/promise": "^2.1 || ^1.2.1", + "react/promise-timer": "^1.2", + "react/stream": "^1.0 || ^0.7 || ^0.6 || ^0.5 || ^0.4.5" + }, + "require-dev": { + "clue/block-react": "^1.2", + "phpunit/phpunit": "^7.0 || ^6.4 || ^5.7 || ^4.8.35" + }, + "time": "2019-07-10T21:00:53+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "React\\Dns\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Async DNS resolver for ReactPHP", + "keywords": [ + "async", + "dns", + "dns-resolver", + "reactphp" + ] + }, + { + "name": "react/event-loop", + "version": "v0.4.3", + "version_normalized": "0.4.3.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/event-loop.git", + "reference": "8bde03488ee897dc6bb3d91e4e17c353f9c5252f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/event-loop/zipball/8bde03488ee897dc6bb3d91e4e17c353f9c5252f", + "reference": "8bde03488ee897dc6bb3d91e4e17c353f9c5252f", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.8" + }, + "suggest": { + "ext-event": "~1.0", + "ext-libev": "*", + "ext-libevent": ">=0.1.0" + }, + "time": "2017-04-27T10:56:23+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "React\\EventLoop\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Event loop abstraction layer that libraries can use for evented I/O.", + "keywords": [ + "asynchronous", + "event-loop" + ] + }, + { + "name": "react/promise", + "version": "v2.7.1", + "version_normalized": "2.7.1.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/promise.git", + "reference": "31ffa96f8d2ed0341a57848cbb84d88b89dd664d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/promise/zipball/31ffa96f8d2ed0341a57848cbb84d88b89dd664d", + "reference": "31ffa96f8d2ed0341a57848cbb84d88b89dd664d", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.8" + }, + "time": "2019-01-07T21:25:54+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "React\\Promise\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com" + } + ], + "description": "A lightweight implementation of CommonJS Promises/A for PHP", + "keywords": [ + "promise", + "promises" + ] + }, + { + "name": "react/promise-timer", + "version": "v1.5.1", + "version_normalized": "1.5.1.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/promise-timer.git", + "reference": "35fb910604fd86b00023fc5cda477c8074ad0abc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/promise-timer/zipball/35fb910604fd86b00023fc5cda477c8074ad0abc", + "reference": "35fb910604fd86b00023fc5cda477c8074ad0abc", + "shasum": "" + }, + "require": { + "php": ">=5.3", + "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5", + "react/promise": "^2.7.0 || ^1.2.1" + }, + "require-dev": { + "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35" + }, + "time": "2019-03-27T18:10:32+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "React\\Promise\\Timer\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@lueck.tv" + } + ], + "description": "A trivial implementation of timeouts for Promises, built on top of ReactPHP.", + "homepage": "https://github.com/reactphp/promise-timer", + "keywords": [ + "async", + "event-loop", + "promise", + "reactphp", + "timeout", + "timer" + ] + }, + { + "name": "react/socket", + "version": "v0.8.12", + "version_normalized": "0.8.12.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/socket.git", + "reference": "7f7e6c56ccda7418a1a264892a625f38a5bdee0c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/socket/zipball/7f7e6c56ccda7418a1a264892a625f38a5bdee0c", + "reference": "7f7e6c56ccda7418a1a264892a625f38a5bdee0c", + "shasum": "" + }, + "require": { + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.0", + "react/dns": "^0.4.13", + "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5", + "react/promise": "^2.6.0 || ^1.2.1", + "react/promise-timer": "^1.4.0", + "react/stream": "^1.0 || ^0.7.1" + }, + "require-dev": { + "clue/block-react": "^1.2", + "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35" + }, + "time": "2018-06-11T14:33:43+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "React\\Socket\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP", + "keywords": [ + "Connection", + "Socket", + "async", + "reactphp", + "stream" + ] + }, + { + "name": "react/stream", + "version": "v1.1.0", + "version_normalized": "1.1.0.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/stream.git", + "reference": "50426855f7a77ddf43b9266c22320df5bf6c6ce6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/stream/zipball/50426855f7a77ddf43b9266c22320df5bf6c6ce6", + "reference": "50426855f7a77ddf43b9266c22320df5bf6c6ce6", + "shasum": "" + }, + "require": { + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.8", + "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5" + }, + "require-dev": { + "clue/stream-filter": "~1.2", + "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35" + }, + "time": "2019-01-01T16:15:09+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "React\\Stream\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP", + "keywords": [ + "event-driven", + "io", + "non-blocking", + "pipe", + "reactphp", + "readable", + "stream", + "writable" + ] + }, + { + "name": "ringcentral/psr7", + "version": "1.2.2", + "version_normalized": "1.2.2.0", + "source": { + "type": "git", + "url": "https://github.com/ringcentral/psr7.git", + "reference": "dcd84bbb49b96c616d1dcc8bfb9bef3f2cd53d1c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ringcentral/psr7/zipball/dcd84bbb49b96c616d1dcc8bfb9bef3f2cd53d1c", + "reference": "dcd84bbb49b96c616d1dcc8bfb9bef3f2cd53d1c", + "shasum": "" + }, + "require": { + "php": ">=5.3", + "psr/http-message": "~1.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "time": "2018-01-15T21:00:49+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "RingCentral\\Psr7\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "PSR-7 message implementation", + "keywords": [ + "http", + "message", + "stream", + "uri" + ] + }, + { + "name": "symfony/process", + "version": "v4.3.5", + "version_normalized": "4.3.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "50556892f3cc47d4200bfd1075314139c4c9ff4b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/50556892f3cc47d4200bfd1075314139c4c9ff4b", + "reference": "50556892f3cc47d4200bfd1075314139c4c9ff4b", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "time": "2019-09-26T21:17:10+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.3-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "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 Process Component", + "homepage": "https://symfony.com" + }, + { + "name": "valga/fbns-react", + "version": "0.1.8", + "version_normalized": "0.1.8.0", + "source": { + "type": "git", + "url": "https://github.com/valga/fbns-react.git", + "reference": "4bbf513a8ffed7e0c9ca10776033d34515bb8b37" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/valga/fbns-react/zipball/4bbf513a8ffed7e0c9ca10776033d34515bb8b37", + "reference": "4bbf513a8ffed7e0c9ca10776033d34515bb8b37", + "shasum": "" + }, + "require": { + "binsoul/net-mqtt": "~0.2", + "evenement/evenement": "~2.0|~3.0", + "ext-mbstring": "*", + "ext-zlib": "*", + "php": "~5.6|~7.0", + "psr/log": "~1.0", + "react/event-loop": "^0.4.3", + "react/promise": "~2.0", + "react/socket": "~0.8" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~2.4", + "monolog/monolog": "~1.23" + }, + "suggest": { + "ext-event": "For more efficient event loop implementation.", + "ext-gmp": "To be able to run this code on x86 PHP builds." + }, + "time": "2017-10-09T07:54:13+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Fbns\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Abyr Valg", + "email": "valga.github@abyrga.ru" + } + ], + "description": "A PHP client for the FBNS built on top of ReactPHP", + "keywords": [ + "FBNS", + "client", + "php" + ] + }, + { + "name": "winbox/args", + "version": "v1.0.0", + "version_normalized": "1.0.0.0", + "source": { + "type": "git", + "url": "https://github.com/johnstevenson/winbox-args.git", + "reference": "389a9ed9410e6f422b1031b3e55a402ace716296" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/johnstevenson/winbox-args/zipball/389a9ed9410e6f422b1031b3e55a402ace716296", + "reference": "389a9ed9410e6f422b1031b3e55a402ace716296", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "time": "2016-08-04T14:30:27+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Winbox\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Windows command-line formatter", + "homepage": "http://github.com/johnstevenson/winbox-args", + "keywords": [ + "Escape", + "command", + "windows" + ] + } +] diff --git a/instafeed/vendor/corneltek/getoptionkit/.gitignore b/instafeed/vendor/corneltek/getoptionkit/.gitignore new file mode 100755 index 0000000..f32e02a --- /dev/null +++ b/instafeed/vendor/corneltek/getoptionkit/.gitignore @@ -0,0 +1,3 @@ +/build +/vendor +*.tgz diff --git a/instafeed/vendor/corneltek/getoptionkit/.travis.yml b/instafeed/vendor/corneltek/getoptionkit/.travis.yml new file mode 100755 index 0000000..147e77c --- /dev/null +++ b/instafeed/vendor/corneltek/getoptionkit/.travis.yml @@ -0,0 +1,29 @@ +language: php +php: +- 5.6 +- 7.0 +- 7.1 +- hhvm +install: +- phpenv rehash +- composer require "satooshi/php-coveralls" "^1" --dev --no-update +- composer install --no-interaction +script: +- phpunit -c phpunit.xml.dist +after_success: +- php vendor/bin/coveralls -v +matrix: + fast_finish: true + allow_failures: + - php: hhvm + - php: '5.6' +cache: + apt: true + directories: + - vendor +notifications: + email: + on_success: change + on_failure: change + slack: + secure: dKH3qw9myjwDO+OIz6qBqn5vJJqoFyD2frS4eH68jI5gtxHX2PJVwaP9waXTSu+FFq9wILsHGQezrawWHcMkbEo4gxbri2Ne7gFT4CJD0DAOyf02JgQ1A/cJaqQ5XrLO9CwjP0/8PKaMCHiND4SLEWgmtlFRoB7gr33QjbQjBvc= diff --git a/instafeed/vendor/corneltek/getoptionkit/CHANGELOG.md b/instafeed/vendor/corneltek/getoptionkit/CHANGELOG.md new file mode 100755 index 0000000..2446ddb --- /dev/null +++ b/instafeed/vendor/corneltek/getoptionkit/CHANGELOG.md @@ -0,0 +1,56 @@ +CHANGELOG +================== + +## v2.3.0 - Thu May 12 10:29:19 2016 + +- Fixed bugs for multiple value parsing with arguments. +- OptionParser::parse(argv) now expects the first argument to be the program name, + so you can pass argv directly to the parser. + +## v2.2.5-6 - Wed May 11 2016 + +- Fixed bugs for ContinuousOptionParser. + +## v2.2.4 - Fri Oct 2 15:53:33 2015 + +- ContinuousOptionParser improvements. + +## v2.2.2 - Tue Jul 14 00:15:26 2015 + +- Added PathType. + +## v2.2.1 - Tue Jul 14 00:17:19 2015 + +Merged PRs: + +- Commit 7bbb91b: Merge pull request #34 from 1Franck/master + + added value type option(s) support, added class RegexType + +- Commit 3722992: Merge pull request #33 from 1Franck/patch-1 + + Clarified InvalidOptionValue exception message + + +## v2.2.0 - Thu Jun 4 13:51:47 2015 + +- Added more types for type constraint option. url, ip, ipv4, ipv6, email by @1Franck++ +- Several bug fixes by @1Franck++ + + + +## v2.1.0 - Fri Apr 24 16:43:00 2015 + +- Added incremental option value support. +- Fixed #21 for negative value. +- Used autoloading with PSR-4 + +## v2.0.12 - Tue Apr 21 18:51:12 2015 + +- Improved hinting text for default value +- Some coding style fix +- Added default value support +- Updated default value support for ContinuousOptionParser +- Added getValue() accessor on OptionSpec class +- Merged pull request #22 from Gasol/zero-option. @Gasol++ + - Fix option that can't not be 0 diff --git a/instafeed/vendor/corneltek/getoptionkit/CONTRIBUTORS.txt b/instafeed/vendor/corneltek/getoptionkit/CONTRIBUTORS.txt new file mode 100755 index 0000000..59cb935 --- /dev/null +++ b/instafeed/vendor/corneltek/getoptionkit/CONTRIBUTORS.txt @@ -0,0 +1,11 @@ +Bitdeli Chef +Dlussky Kirill +Francois Lajoie +Gasol Wu +Igor Santos +Jevon Wright +MartyIX +Michał Kniotek +Robbert Klarenbeek +Yo-An Lin +Yo-An Lin diff --git a/instafeed/vendor/corneltek/getoptionkit/LICENSE b/instafeed/vendor/corneltek/getoptionkit/LICENSE new file mode 100755 index 0000000..30a0180 --- /dev/null +++ b/instafeed/vendor/corneltek/getoptionkit/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2013 Yo-An Lin + +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/instafeed/vendor/corneltek/getoptionkit/README.md b/instafeed/vendor/corneltek/getoptionkit/README.md new file mode 100755 index 0000000..1e5794b --- /dev/null +++ b/instafeed/vendor/corneltek/getoptionkit/README.md @@ -0,0 +1,370 @@ +GetOptionKit +============ + +Code Quality + +[![Build Status](https://travis-ci.org/c9s/GetOptionKit.svg?branch=master)](https://travis-ci.org/c9s/GetOptionKit) +[![Coverage Status](https://coveralls.io/repos/github/c9s/GetOptionKit/badge.svg?branch=master)](https://coveralls.io/github/c9s/GetOptionKit?branch=master) + +Versions & Stats + +[![Latest Stable Version](https://poser.pugx.org/corneltek/getoptionkit/v/stable)](https://packagist.org/packages/corneltek/getoptionkit) +[![Latest Unstable Version](https://poser.pugx.org/corneltek/getoptionkit/v/unstable)](https://packagist.org/packages/corneltek/getoptionkit) +[![Total Downloads](https://poser.pugx.org/corneltek/getoptionkit/downloads)](https://packagist.org/packages/corneltek/getoptionkit) +[![Monthly Downloads](https://poser.pugx.org/corneltek/getoptionkit/d/monthly)](https://packagist.org/packages/corneltek/getoptionkit) +[![Daily Downloads](https://poser.pugx.org/corneltek/getoptionkit/d/daily)](https://packagist.org/packages/corneltek/getoptionkit) +[![License](https://poser.pugx.org/corneltek/getoptionkit/license)](https://packagist.org/packages/corneltek/getoptionkit) + +A powerful option parser toolkit for PHP, supporting type constraints, +flag, multiple flag, multiple values and required value checking. + +GetOptionKit supports PHP5.3, with fine unit testing with PHPUnit testing +framework. + +GetOptionKit is object-oriented, it's flexible and extendable. + +Powering PHPBrew , CLIFramework and AssetKit + + + +## Features + +- Simple format. +- Type constrant. +- Multiple value, requried value, optional value checking. +- Auto-generated help text from defined options. +- Support app/subcommand option parsing. +- Option Value Validator +- Option Suggestions +- SPL library. +- HHVM support. + +## Requirements + +* PHP 5.3+ + +## Install From Composer + +```sh +composer require corneltek/getoptionkit +``` + + +## Supported Option Formats + +simple flags: + +```sh +program.php -a -b -c +program.php -abc +program.php -vvv # incremental flag v=3 +program.php -a -bc +``` + +with multiple values: + +```sh +program.php -a foo -a bar -a zoo -b -b -b +``` + +specify value with equal sign: + +```sh +program.php -a=foo +program.php --long=foo +``` + +with normal arguments: + +``` +program.php -a=foo -b=bar arg1 arg2 arg3 +program.php arg1 arg2 arg3 -a=foo -b=bar +``` + +## Option SPEC + + v|verbose flag option (with boolean value true) + d|dir: option require a value (MUST require) + d|dir+ option with multiple values. + d|dir? option with optional value + dir:=string option with type constraint of string + dir:=number option with type constraint of number + dir:=file option with type constraint of file + dir:=date option with type constraint of date + dir:=boolean option with type constraint of boolean + d single character only option + dir long option name + +## Command Line Forms + + app [app-opts] [app arguments] + + app [app-opts] subcommand [subcommand-opts] [subcommand-args] + + app [app-opts] subcmd1 [subcmd-opts1] subcmd2 [subcmd-opts] subcmd3 [subcmd-opts3] [subcommand arguments....] + + +## Documentation + +See more details in the [documentation](https://github.com/c9s/GetOptionKit/wiki) + +## Demo + +Please check `examples/demo.php`. + +Run: + +```sh +% php examples/demo.php -f test -b 123 -b 333 +``` + +Print: + + * Available options: + -f, --foo option requires a value. + -b, --bar + option with multiple value. + -z, --zoo [] option with optional value. + -v, --verbose verbose message. + -d, --debug debug message. + --long long option name only. + -s short option name only. + Enabled options: + * key:foo spec:-f, --foo desc:option requires a value. + value => test + + * key:bar spec:-b, --bar + desc:option with multiple value. + Array + ( + [0] => 123 + [1] => 333 + ) + +## Synopsis + +```php +use GetOptionKit\OptionCollection; +use GetOptionKit\OptionParser; +use GetOptionKit\OptionPrinter\ConsoleOptionPrinter; + +$specs = new OptionCollection; +$specs->add('f|foo:', 'option requires a value.' ) + ->isa('String'); + +$specs->add('b|bar+', 'option with multiple value.' ) + ->isa('Number'); + +$specs->add('ip+', 'Ip constraint' ) + ->isa('Ip'); + +$specs->add('email+', 'Email address constraint' ) + ->isa('Email'); + +$specs->add('z|zoo?', 'option with optional value.' ) + ->isa('Boolean'); + +$specs->add('file:', 'option value should be a file.' ) + ->isa('File'); + +$specs->add('v|verbose', 'verbose message.' ); +$specs->add('d|debug', 'debug message.' ); +$specs->add('long', 'long option name only.' ); +$specs->add('s', 'short option name only.' ); + +$printer = new ConsoleOptionPrinter(); +echo $printer->render($specs); + +$parser = new OptionParser($specs); + +echo "Enabled options: \n"; +try { + $result = $parser->parse( $argv ); + foreach ($result->keys as $key => $spec) { + print_r($spec); + } + + $opt = $result->keys['foo']; // return the option object. + $str = $result->keys['foo']->value; // return the option value + + print_r($opt); + var_dump($str); + +} catch( Exception $e ) { + echo $e->getMessage(); +} +``` + + +## Documentation + +See for more details. + +### Option Value Type + +The option value type help you validate the input, +the following list is the current supported types: + +- `string` +- `number` +- `boolean` +- `file` +- `date` +- `url` +- `email` +- `ip` +- `ipv4` +- `ipv6` +- `regex` + +And here is the related sample code: + +```php +$opt->add( 'f|foo:' , 'with string type value' ) + ->isa('string'); + +$opt->add( 'b|bar+' , 'with number type value' ) + ->isa('number'); + +$opt->add( 'z|zoo?' , 'with boolean type value' ) + ->isa('boolean'); + +$opt->add( 'file:' , 'with file type value' ) + ->isa('file'); + +$opt->add( 'date:' , 'with date type value' ) + ->isa('date'); + +$opt->add( 'url:' , 'with url type value' ) + ->isa('url'); + +$opt->add( 'email:' , 'with email type value' ) + ->isa('email'); + +$opt->add( 'ip:' , 'with ip(v4/v6) type value' ) + ->isa('ip'); + +$opt->add( 'ipv4:' , 'with ipv4 type value' ) + ->isa('ipv4'); + +$opt->add( 'ipv6:' , 'with ipv6 type value' ) + ->isa('ipv6'); + +$specs->add('r|regex:', 'with custom regex type value') + ->isa('Regex', '/^([a-z]+)$/'); +``` + +> Please note that currently only `string`, `number`, `boolean` types can be validated. + +### ContinuousOptionParser + +```php +$specs = new OptionCollection; +$spec_verbose = $specs->add('v|verbose'); +$spec_color = $specs->add('c|color'); +$spec_debug = $specs->add('d|debug'); +$spec_verbose->description = 'verbose flag'; + +// ContinuousOptionParser +$parser = new ContinuousOptionParser( $specs ); +$result = $parser->parse(explode(' ','program -v -d test -a -b -c subcommand -e -f -g subcommand2')); +$result2 = $parser->continueParse(); +``` + +### OptionPrinter + +GetOptionKit\OptionPrinter can print options for you: + + * Available options: + -f, --foo option requires a value. + -b, --bar option with multiple value. + -z, --zoo option with optional value. + -v, --verbose verbose message. + -d, --debug debug message. + --long long option name only. + -s short option name only. + + +## Command-line app with subcommands + +For application with subcommands is designed by following form: + + + [app name] [app opts] + [subcommand1] [subcommand-opts] + [subcommand2] [subcommand-opts] + [subcommand3] [subcommand-opts] + [arguments] + +You can check the `tests/GetOptionKit/ContinuousOptionParserTest.php` unit test file: + +```php +// subcommand stack +$subcommands = array('subcommand1','subcommand2','subcommand3'); + +// different command has its own options +$subcommandSpecs = array( + 'subcommand1' => $cmdspecs, + 'subcommand2' => $cmdspecs, + 'subcommand3' => $cmdspecs, +); + +// for saved options +$subcommandOptions = array(); + +// command arguments +$arguments = array(); + +$argv = explode(' ','program -v -d -c subcommand1 -a -b -c subcommand2 -c subcommand3 arg1 arg2 arg3'); + +// parse application options first +$parser = new ContinuousOptionParser( $appspecs ); +$app_options = $parser->parse( $argv ); +while (! $parser->isEnd()) { + if (@$subcommands[0] && $parser->getCurrentArgument() == $subcommands[0]) { + $parser->advance(); + $subcommand = array_shift( $subcommands ); + $parser->setSpecs( $subcommandSpecs[$subcommand] ); + $subcommandOptions[ $subcommand ] = $parser->continueParse(); + } else { + $arguments[] = $parser->advance(); + } +} +``` + +## Todo + +* Option Spec group. +* option valid value checking. +* custom command mapping. + +## Command Line Utility Design Concept + +* main program name should be easy to type, easy to remember. +* subcommand should be easy to type, easy to remember. length should be shorter than 7 characters. +* options should always have long descriptive name +* a program should be easy to check usage. + +## General command interface + +To list usage of all subcommands or the program itself: + + $ prog help + +To list the subcommand usage + + $ prog help subcommand subcommand2 subcommand3 + +## Hacking + +Fork this repository and clone it: + + $ git clone git://github.com/c9s/GetOptionKit.git + $ cd GetOptionKit + $ composer install + +Run PHPUnit to test: + + $ phpunit + +## License + +This project is released under MIT License. diff --git a/instafeed/vendor/corneltek/getoptionkit/build.xml b/instafeed/vendor/corneltek/getoptionkit/build.xml new file mode 100755 index 0000000..f53f06d --- /dev/null +++ b/instafeed/vendor/corneltek/getoptionkit/build.xml @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/instafeed/vendor/corneltek/getoptionkit/composer.json b/instafeed/vendor/corneltek/getoptionkit/composer.json new file mode 100755 index 0000000..e6577d5 --- /dev/null +++ b/instafeed/vendor/corneltek/getoptionkit/composer.json @@ -0,0 +1,16 @@ +{ + "name": "corneltek/getoptionkit", + "homepage": "http://github.com/c9s/GetOptionKit", + "description": "Powerful command-line option toolkit", + "require": { + "php": ">=5.3.0" + }, + "license": "MIT", + "authors": [ { "name": "Yo-An Lin", "email": "yoanlin93@gmail.com" } ], + "autoload": { + "psr-4": { + "GetOptionKit\\": "src/" + } + }, + "extra": { "branch-alias": { "dev-master": "2.6.x-dev" } } +} diff --git a/instafeed/vendor/corneltek/getoptionkit/composer.lock b/instafeed/vendor/corneltek/getoptionkit/composer.lock new file mode 100755 index 0000000..1dd27fe --- /dev/null +++ b/instafeed/vendor/corneltek/getoptionkit/composer.lock @@ -0,0 +1,19 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "content-hash": "5a9b6e9fccaf89d121650d35313e842b", + "packages": [], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=5.3.0" + }, + "platform-dev": [] +} diff --git a/instafeed/vendor/corneltek/getoptionkit/examples/demo.php b/instafeed/vendor/corneltek/getoptionkit/examples/demo.php new file mode 100755 index 0000000..905f5c3 --- /dev/null +++ b/instafeed/vendor/corneltek/getoptionkit/examples/demo.php @@ -0,0 +1,76 @@ +#!/usr/bin/env php + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ +require 'vendor/autoload.php'; + +use GetOptionKit\OptionCollection; +use GetOptionKit\OptionParser; +use GetOptionKit\OptionPrinter\ConsoleOptionPrinter; + +$specs = new OptionCollection; +$specs->add('f|foo:', 'option requires a value.' ) + ->isa('String'); + +$specs->add('b|bar+', 'option with multiple value.' ) + ->isa('Number'); + +$specs->add('z|zoo?', 'option with optional value.' ) + ->isa('Boolean') + ; + +$specs->add('o|output?', 'option with optional value.' ) + ->isa('File') + ->defaultValue('output.txt') + ; + +// works for -vvv => verbose = 3 +$specs->add('v|verbose', 'verbose') + ->isa('Number') + ->incremental(); + +$specs->add('file:', 'option value should be a file.' ) + ->trigger(function($value) { + echo "Set value to :"; + var_dump($value); + }) + ->isa('File'); + +$specs->add('r|regex:', 'with custom regex type value') + ->isa('Regex', '/^([a-z]+)$/'); + +$specs->add('d|debug', 'debug message.' ); +$specs->add('long', 'long option name only.' ); +$specs->add('s', 'short option name only.' ); +$specs->add('m', 'short option m'); +$specs->add('4', 'short option with digit'); + +$printer = new ConsoleOptionPrinter; +echo $printer->render($specs); + +$parser = new OptionParser($specs); + +echo "Enabled options: \n"; +try { + $result = $parser->parse( $argv ); + foreach ($result->keys as $key => $spec) { + print_r($spec); + } + + $opt = $result->keys['foo']; // return the option object. + $str = $result->keys['foo']->value; // return the option value + + print_r($opt); + var_dump($str); + +} catch( Exception $e ) { + echo $e->getMessage(); +} + diff --git a/instafeed/vendor/corneltek/getoptionkit/package.ini b/instafeed/vendor/corneltek/getoptionkit/package.ini new file mode 100755 index 0000000..4d71095 --- /dev/null +++ b/instafeed/vendor/corneltek/getoptionkit/package.ini @@ -0,0 +1,15 @@ +[package] +name = GetOptionKit +version = 1.2.2 +desc = "A powerful GetOpt toolkit for PHP, which supports type constraints, flag, +multiple flag, multiple values, required value checking." + +author = "Yo-An Lin (c9s) " +channel = pear.corneltek.com +stability = stable + +[require] +php = 5.3 +pearinstaller = 1.4.1 +pear.corneltek.com/Universal = +pear.corneltek.com/PHPUnit_TestMore = diff --git a/instafeed/vendor/corneltek/getoptionkit/phpdox.xml b/instafeed/vendor/corneltek/getoptionkit/phpdox.xml new file mode 100755 index 0000000..c727483 --- /dev/null +++ b/instafeed/vendor/corneltek/getoptionkit/phpdox.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/instafeed/vendor/corneltek/getoptionkit/phprelease.ini b/instafeed/vendor/corneltek/getoptionkit/phprelease.ini new file mode 100755 index 0000000..a5c5cc9 --- /dev/null +++ b/instafeed/vendor/corneltek/getoptionkit/phprelease.ini @@ -0,0 +1,2 @@ +Steps = PHPUnit, BumpVersion, GitCommit, GitTag, GitPush, GitPushTags +; VersionFrom = src/PHPRelease/Console.php \ No newline at end of file diff --git a/instafeed/vendor/corneltek/getoptionkit/phpunit-ci.xml b/instafeed/vendor/corneltek/getoptionkit/phpunit-ci.xml new file mode 100755 index 0000000..0fed2fa --- /dev/null +++ b/instafeed/vendor/corneltek/getoptionkit/phpunit-ci.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + tests + + + + + + + + + diff --git a/instafeed/vendor/corneltek/getoptionkit/phpunit.xml.dist b/instafeed/vendor/corneltek/getoptionkit/phpunit.xml.dist new file mode 100755 index 0000000..87bb483 --- /dev/null +++ b/instafeed/vendor/corneltek/getoptionkit/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + + src + + + + + + tests + + + + + + + + + + + diff --git a/instafeed/vendor/corneltek/getoptionkit/src/Argument.php b/instafeed/vendor/corneltek/getoptionkit/src/Argument.php new file mode 100755 index 0000000..2a92717 --- /dev/null +++ b/instafeed/vendor/corneltek/getoptionkit/src/Argument.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 GetOptionKit; + +class Argument +{ + public $arg; + + public function __construct($arg) + { + $this->arg = $arg; + } + + public function isLongOption() + { + return substr($this->arg, 0, 2) === '--'; + } + + public function isShortOption() + { + return (substr($this->arg, 0, 1) === '-') + && (substr($this->arg, 1, 1) !== '-'); + } + + public function isEmpty() + { + return $this->arg === null || empty($this->arg) && ('0' !== $this->arg); + } + + /** + * Check if an option is one of the option in the collection. + */ + public function anyOfOptions(OptionCollection $options) + { + $name = $this->getOptionName(); + $keys = $options->keys(); + + return in_array($name, $keys); + } + + /** + * Check current argument is an option by the preceding dash. + * note this method does not work for string with negative value. + * + * -a + * --foo + */ + public function isOption() + { + return $this->isShortOption() || $this->isLongOption(); + } + + /** + * Parse option and return the name after dash. e.g., + * '--foo' returns 'foo' + * '-f' returns 'f'. + * + * @return string + */ + public function getOptionName() + { + if (preg_match('/^[-]+([a-zA-Z0-9-]+)/', $this->arg, $regs)) { + return $regs[1]; + } + } + + public function splitAsOption() + { + return explode('=', $this->arg, 2); + } + + public function containsOptionValue() + { + return preg_match('/=.+/', $this->arg); + } + + public function getOptionValue() + { + if (preg_match('/=(.+)/', $this->arg, $regs)) { + return $regs[1]; + } + + return; + } + + /** + * Check combined short flags for "-abc" or "-vvv". + * + * like: -abc + */ + public function withExtraFlagOptions() + { + return preg_match('/^-[a-zA-Z0-9]{2,}/', $this->arg); + } + + public function extractExtraFlagOptions() + { + $args = array(); + for ($i = 2;$i < strlen($this->arg); ++$i) { + $args[] = '-'.$this->arg[$i]; + } + $this->arg = substr($this->arg, 0, 2); # -[a-z] + return $args; + } + + public function __toString() + { + return $this->arg; + } +} diff --git a/instafeed/vendor/corneltek/getoptionkit/src/ContinuousOptionParser.php b/instafeed/vendor/corneltek/getoptionkit/src/ContinuousOptionParser.php new file mode 100755 index 0000000..fa592fd --- /dev/null +++ b/instafeed/vendor/corneltek/getoptionkit/src/ContinuousOptionParser.php @@ -0,0 +1,201 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace GetOptionKit; + +use Exception; +use LogicException; +use GetOptionKit\Exception\InvalidOptionException; +use GetOptionKit\Exception\RequireValueException; + +/** + * A common command line argument format:. + * + * app.php + * [--app-options] + * + * [subcommand + * --subcommand-options] + * [subcommand + * --subcommand-options] + * [subcommand + * --subcommand-options] + * + * [arguments] + * + * ContinuousOptionParser is for the process flow: + * + * init app options, + * parse app options + * + * + * + * while not end + * if stop at command + * shift command + * parse command options + * else if stop at arguments + * shift arguments + * execute current command with the arguments. + * + * Example code: + * + * + * // subcommand stack + * $subcommands = array('subcommand1','subcommand2','subcommand3'); + * + * // different command has its own options + * $subcommand_specs = array( + * 'subcommand1' => $cmdspecs, + * 'subcommand2' => $cmdspecs, + * 'subcommand3' => $cmdspecs, + * ); + * + * // for saved options + * $subcommand_options = array(); + * + * // command arguments + * $arguments = array(); + * + * $argv = explode(' ','-v -d -c subcommand1 -a -b -c subcommand2 -c subcommand3 arg1 arg2 arg3'); + * + * // parse application options first + * $parser = new ContinuousOptionParser( $appspecs ); + * $app_options = $parser->parse( $argv ); + * while (! $parser->isEnd()) { + * if( $parser->getCurrentArgument() == $subcommands[0] ) { + * $parser->advance(); + * $subcommand = array_shift( $subcommands ); + * $parser->setSpecs( $subcommand_specs[$subcommand] ); + * $subcommand_options[ $subcommand ] = $parser->continueParse(); + * } else { + * $arguments[] = $parser->advance(); + * } + * } + **/ +class ContinuousOptionParser extends OptionParser +{ + public $index; + public $length; + public $argv; + + /* for the constructor , the option specs is application options */ + public function __construct(OptionCollection $specs) + { + parent::__construct($specs); + $this->index = 1; + } + + /** + * @codeCoverageIgnore + */ + public function startFrom($index) + { + $this->index = $index; + } + + public function isEnd() + { + # echo "!! {$this->index} >= {$this->length}\n"; + return $this->index >= $this->length; + } + + /** + * Return the current argument and advance to the next position. + * + * @return string + */ + public function advance() + { + if ($this->index >= $this->length) { + throw new LogicException("Argument index out of bounds."); + } + return $this->argv[$this->index++]; + } + + /** + * Return the current argument that the index pointed to. + * + * @return string + */ + public function getCurrentArgument() + { + return $this->argv[$this->index]; + } + + public function continueParse() + { + return $this->parse($this->argv); + } + + + protected function fillDefaultValues(OptionCollection $opts, OptionResult $result) + { + // register option result from options with default value + foreach ($opts as $opt) { + if ($opt->value === null && $opt->defaultValue !== null) { + $opt->setValue($opt->getDefaultValue()); + $result->set($opt->getId(), $opt); + } + } + } + + + public function parse(array $argv) + { + // create new Result object. + $result = new OptionResult(); + list($this->argv, $extra) = $this->preprocessingArguments($argv); + $this->length = count($this->argv); + + // from last parse index + for (; $this->index < $this->length; ++$this->index) { + $arg = new Argument($this->argv[$this->index]); + + /* let the application decide for: command or arguments */ + if (!$arg->isOption()) { + # echo "stop at {$this->index}\n"; + $this->fillDefaultValues($this->specs, $result); + return $result; + } + + // if the option is with extra flags, + // split it out, and insert into the argv array + // + // like -abc + if ($arg->withExtraFlagOptions()) { + $extra = $arg->extractExtraFlagOptions(); + array_splice($this->argv, $this->index + 1, 0, $extra); + $this->argv[$this->index] = $arg->arg; // update argument to current argv list. + $this->length = count($this->argv); // update argv list length + } + + $next = null; + if ($this->index + 1 < count($this->argv)) { + $next = new Argument($this->argv[$this->index + 1]); + } + + $spec = $this->specs->get($arg->getOptionName()); + if (!$spec) { + throw new InvalidOptionException('Invalid option: '.$arg); + } + + // This if block is unnecessary + // if ($spec->isRequired() || $spec->isMultiple() || $spec->isOptional() || $spec->isFlag()) { + $this->index += $this->consumeOptionToken($spec, $arg, $next); + $result->set($spec->getId(), $spec); + } + + $this->fillDefaultValues($this->specs, $result); + + return $result; + } +} diff --git a/instafeed/vendor/corneltek/getoptionkit/src/Exception/InvalidOptionException.php b/instafeed/vendor/corneltek/getoptionkit/src/Exception/InvalidOptionException.php new file mode 100755 index 0000000..dea01de --- /dev/null +++ b/instafeed/vendor/corneltek/getoptionkit/src/Exception/InvalidOptionException.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 GetOptionKit\Exception; + +use Exception; + +class InvalidOptionException extends Exception +{ +} diff --git a/instafeed/vendor/corneltek/getoptionkit/src/Exception/InvalidOptionValueException.php b/instafeed/vendor/corneltek/getoptionkit/src/Exception/InvalidOptionValueException.php new file mode 100755 index 0000000..fa4b163 --- /dev/null +++ b/instafeed/vendor/corneltek/getoptionkit/src/Exception/InvalidOptionValueException.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 GetOptionKit\Exception; + +use Exception; + +class InvalidOptionValueException extends Exception +{ +} diff --git a/instafeed/vendor/corneltek/getoptionkit/src/Exception/NonNumericException.php b/instafeed/vendor/corneltek/getoptionkit/src/Exception/NonNumericException.php new file mode 100755 index 0000000..757d214 --- /dev/null +++ b/instafeed/vendor/corneltek/getoptionkit/src/Exception/NonNumericException.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 GetOptionKit\Exception; + +use Exception; + +class NonNumericException extends Exception +{ +} diff --git a/instafeed/vendor/corneltek/getoptionkit/src/Exception/OptionConflictException.php b/instafeed/vendor/corneltek/getoptionkit/src/Exception/OptionConflictException.php new file mode 100755 index 0000000..6a3c9e1 --- /dev/null +++ b/instafeed/vendor/corneltek/getoptionkit/src/Exception/OptionConflictException.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 GetOptionKit\Exception; + +use Exception; + +class OptionConflictException extends Exception +{ +} diff --git a/instafeed/vendor/corneltek/getoptionkit/src/Exception/RequireValueException.php b/instafeed/vendor/corneltek/getoptionkit/src/Exception/RequireValueException.php new file mode 100755 index 0000000..c9e69ef --- /dev/null +++ b/instafeed/vendor/corneltek/getoptionkit/src/Exception/RequireValueException.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 GetOptionKit\Exception; + +use Exception; + +class RequireValueException extends Exception +{ +} diff --git a/instafeed/vendor/corneltek/getoptionkit/src/Option.php b/instafeed/vendor/corneltek/getoptionkit/src/Option.php new file mode 100755 index 0000000..f11a93a --- /dev/null +++ b/instafeed/vendor/corneltek/getoptionkit/src/Option.php @@ -0,0 +1,557 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace GetOptionKit; + + +use Exception; +use LogicException; +use InvalidArgumentException; +use GetOptionKit\Exception\InvalidOptionValueException; + +class Option +{ + public $short; + + public $long; + + /** + * @var string the description of this option + */ + public $desc; + + /** + * @var string The option key + */ + public $key; /* key to store values */ + + public $value; + + public $type; + + public $valueName; /* name for the value place holder, for printing */ + + public $isa; + + public $isaOption; + + public $validValues; + + public $suggestions; + + public $defaultValue; + + public $incremental = false; + + /** + * @var Closure The filter closure of the option value. + */ + public $filter; + + public $validator; + + public $multiple = false; + + public $optional = false; + + public $required = false; + + public $flag = false; + + /** + * @var callable trigger callback after value is set. + */ + protected $trigger; + + public function __construct($spec) + { + $this->initFromSpecString($spec); + } + + /** + * Build spec attributes from spec string. + * + * @param string $specString + */ + protected function initFromSpecString($specString) + { + $pattern = '/ + ( + (?:[a-zA-Z0-9-]+) + (?: + \| + (?:[a-zA-Z0-9-]+) + )? + ) + + # option attribute operators + ([:+?])? + + # value types + (?:=(boolean|string|number|date|file|dir|url|email|ip|ipv6|ipv4))? + /x'; + $ret = preg_match($pattern, $specString, $regs); + if ($ret === false || $ret === 0) { + throw new Exception('Incorrect spec string'); + } + + $orig = $regs[0]; + $name = $regs[1]; + $attributes = isset($regs[2]) ? $regs[2] : null; + $type = isset($regs[3]) ? $regs[3] : null; + + $short = null; + $long = null; + + // check long,short option name. + if (strpos($name, '|') !== false) { + list($short, $long) = explode('|', $name); + } else if (strlen($name) === 1) { + $short = $name; + } else if (strlen($name) > 1) { + $long = $name; + } + + $this->short = $short; + $this->long = $long; + + // option is required. + if (strpos($attributes, ':') !== false) { + $this->required(); + } else if (strpos($attributes, '+') !== false) { + // option with multiple value + $this->multiple(); + } else if (strpos($attributes, '?') !== false) { + // option is optional.(zero or one value) + $this->optional(); + } else { + $this->flag(); + } + if ($type) { + $this->isa($type); + } + } + + /* + * get the option key for result key mapping. + */ + public function getId() + { + return $this->key ?: $this->long ?: $this->short; + } + + /** + * To make -v, -vv, -vvv works. + */ + public function incremental() + { + $this->incremental = true; + + return $this; + } + + public function required() + { + $this->required = true; + + return $this; + } + + /** + * Set default value + * + * @param mixed|Closure $value + */ + public function defaultValue($value) + { + $this->defaultValue = $value; + + return $this; + } + + public function multiple() + { + $this->multiple = true; + $this->value = array(); # for value pushing + return $this; + } + + public function optional() + { + $this->optional = true; + + return $this; + } + + public function flag() + { + $this->flag = true; + + return $this; + } + + public function trigger(callable $trigger) + { + $this->trigger = $trigger; + + return $this; + } + + public function isIncremental() + { + return $this->incremental; + } + + public function isFlag() + { + return $this->flag; + } + + public function isMultiple() + { + return $this->multiple; + } + + public function isRequired() + { + return $this->required; + } + + public function isOptional() + { + return $this->optional; + } + + public function isTypeNumber() + { + return $this->isa == 'number'; + } + + public function isType($type) + { + return $this->isa === $type; + } + + public function getTypeClass() + { + $class = 'GetOptionKit\\ValueType\\'.ucfirst($this->isa).'Type'; + if (class_exists($class, true)) { + return new $class($this->isaOption); + } + throw new Exception("Type class '$class' not found."); + } + + public function testValue($value) + { + $type = $this->getTypeClass(); + return $type->test($value); + } + + protected function _preprocessValue($value) + { + $val = $value; + + if ($isa = ucfirst($this->isa)) { + $type = $this->getTypeClass(); + if ($type->test($value)) { + $val = $type->parse($value); + } else { + if (strtolower($isa) === 'regex') { + $isa .= '('.$this->isaOption.')'; + } + throw new InvalidOptionValueException("Invalid value for {$this->renderReadableSpec(false)}. Requires a type $isa."); + } + } + + // check pre-filter for option value + if ($this->filter) { + $val = call_user_func($this->filter, $val); + } + + // check validValues + if ($validValues = $this->getValidValues()) { + if (!in_array($value, $validValues)) { + throw new InvalidOptionValueException('valid values are: '.implode(', ', $validValues)); + } + } + + if (!$this->validate($value)[0]) { + throw new InvalidOptionValueException('option is invalid'); + } + + return $val; + } + + protected function callTrigger() + { + if ($this->trigger) { + if ($ret = call_user_func($this->trigger, $this->value)) { + $this->value = $ret; + } + } + } + + /* + * set option value + */ + public function setValue($value) + { + $this->value = $this->_preprocessValue($value); + $this->callTrigger(); + } + + /** + * This method is for incremental option. + */ + public function increaseValue() + { + if (!$this->value) { + $this->value = 1; + } else { + ++$this->value; + } + $this->callTrigger(); + } + + /** + * push option value, when the option accept multiple values. + * + * @param mixed + */ + public function pushValue($value) + { + $value = $this->_preprocessValue($value); + $this->value[] = $value; + $this->callTrigger(); + } + + public function desc($desc) + { + $this->desc = $desc; + } + + /** + * valueName is for option value hinting:. + * + * --name= + */ + public function valueName($name) + { + $this->valueName = $name; + + return $this; + } + + public function renderValueHint() + { + $n = null; + if ($this->valueName) { + $n = $this->valueName; + } else if ($values = $this->getValidValues()) { + $n = '('.implode(',', $values).')'; + } else if ($values = $this->getSuggestions()) { + $n = '['.implode(',', $values).']'; + } else if ($val = $this->getDefaultValue()) { + // This allows for `0` and `false` values to be displayed also. + if ((is_scalar($val) && strlen((string) $val)) || is_bool($val)) { + if (is_bool($val)) { + $n = ($val ? 'true' : 'false'); + } else { + $n = $val; + } + } + } + + if (!$n && $this->isa !== null) { + $n = '<'.$this->isa.'>'; + } + if ($this->isRequired()) { + return sprintf('=%s', $n); + } else if ($this->isOptional() || $this->defaultValue) { + return sprintf('[=%s]', $n); + } else if ($n) { + return '='.$n; + } + + return ''; + } + + public function getDefaultValue() + { + if (is_callable($this->defaultValue)) { + return $this->defaultValue; + } + + return $this->defaultValue; + } + + public function getValue() + { + if (null !== $this->value) { + if (is_callable($this->value)) { + return call_user_func($this->value); + } + return $this->value; + } + + return $this->getDefaultValue(); + } + + /** + * get readable spec for printing. + * + * @param string $renderHint render also value hint + */ + public function renderReadableSpec($renderHint = true) + { + $c1 = ''; + if ($this->short && $this->long) { + $c1 = sprintf('-%s, --%s', $this->short, $this->long); + } else if ($this->short) { + $c1 = sprintf('-%s', $this->short); + } else if ($this->long) { + $c1 = sprintf('--%s', $this->long); + } + if ($renderHint) { + return $c1.$this->renderValueHint(); + } + + return $c1; + } + + public function __toString() + { + $c1 = $this->renderReadableSpec(); + $return = ''; + $return .= sprintf('* key:%-8s spec:%s desc:%s', $this->getId(), $c1, $this->desc)."\n"; + $val = $this->getValue(); + if (is_array($val)) { + $return .= ' value => ' . join(',', array_map(function($v) { return var_export($v, true); }, $val))."\n"; + } else { + $return .= sprintf(' value => %s', $val)."\n"; + } + + return $return; + } + + /** + * Value Type Setters. + * + * @param string $type the value type, valid values are 'number', 'string', + * 'file', 'boolean', you can also use your own value type name. + * @param mixed $option option(s) for value type class (optionnal) + */ + public function isa($type, $option = null) + { + // "bool" was kept for backward compatibility + if ($type === 'bool') { + $type = 'boolean'; + } + $this->isa = $type; + $this->isaOption = $option; + + return $this; + } + + /** + * Assign validValues to member value. + */ + public function validValues($values) + { + $this->validValues = $values; + + return $this; + } + + /** + * Assign suggestions. + * + * @param Closure|array + */ + public function suggestions($suggestions) + { + $this->suggestions = $suggestions; + + return $this; + } + + /** + * Return valud values array. + * + * @return string[] or nil + */ + public function getValidValues() + { + if ($this->validValues) { + if (is_callable($this->validValues)) { + return call_user_func($this->validValues); + } + + return $this->validValues; + } + + return; + } + + /** + * Return suggestions. + * + * @return string[] or nil + */ + public function getSuggestions() + { + if ($this->suggestions) { + if (is_callable($this->suggestions)) { + return call_user_func($this->suggestions); + } + + return $this->suggestions; + } + + return; + } + + public function validate($value) + { + if ($this->validator) { + $ret = call_user_func($this->validator, $value); + if (is_array($ret)) { + return $ret; + } else if ($ret === false) { + return array(false, "Invalid value: $value"); + } else if ($ret === true) { + return array(true, 'Successfully validated.'); + } + throw new InvalidArgumentException('Invalid return value from the validator.'); + } + + return array(true); + } + + public function validator($cb) + { + $this->validator = $cb; + + return $this; + } + + /** + * Set up a filter function for the option value. + * + * todo: add "callable" type hint later. + */ + public function filter($cb) + { + $this->filter = $cb; + + return $this; + } +} diff --git a/instafeed/vendor/corneltek/getoptionkit/src/OptionCollection.php b/instafeed/vendor/corneltek/getoptionkit/src/OptionCollection.php new file mode 100755 index 0000000..22cb638 --- /dev/null +++ b/instafeed/vendor/corneltek/getoptionkit/src/OptionCollection.php @@ -0,0 +1,207 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace GetOptionKit; + + +use ArrayIterator; +use IteratorAggregate; +use Countable; +use Exception; +use LogicException; +use GetOptionKit\Exception\OptionConflictException; + +class OptionCollection + implements IteratorAggregate, Countable +{ + public $data = array(); + + /** + * @var Option[string] + * + * read-only property + */ + public $longOptions = array(); + + /** + * @var Option[string] + * + * read-only property + */ + public $shortOptions = array(); + + /** + * @var Option[] + * + * read-only property + */ + public $options = array(); + + public function __construct() + { + $this->data = array(); + } + + public function __clone() + { + foreach ($this->data as $k => $v) { + $this->data[ $k ] = clone $v; + } + foreach ($this->longOptions as $k => $v) { + $this->longOptions[ $k ] = clone $v; + } + foreach ($this->shortOptions as $k => $v) { + $this->shortOptions[ $k ] = clone $v; + } + foreach ($this->options as $k => $v) { + $this->options[ $k ] = clone $v; + } + } + + /** + * add( [spec string], [desc string] ). + * + * add( [option object] ) + */ + public function add() + { + $num = func_num_args(); + $args = func_get_args(); + $first = $args[0]; + + if ($first instanceof Option) { + + $this->addOption($first); + + } else if (is_string($first)) { + + $specString = $args[0]; + $desc = isset($args[1]) ? $args[1] : null; + $key = isset($args[2]) ? $args[2] : null; + + // parse spec string + $spec = new Option($specString); + if ($desc) { + $spec->desc($desc); + } + if ($key) { + $spec->key = $key; + } + $this->addOption($spec); + return $spec; + + } else { + + throw new LogicException('Unknown Spec Type'); + + } + } + + /** + * Add option object. + * + * @param object $spec the option object. + */ + public function addOption(Option $spec) + { + $this->data[$spec->getId()] = $spec; + if ($spec->long) { + if (isset($this->longOptions[$spec->long])) { + throw new OptionConflictException('Option conflict: --'.$spec->long.' is already defined.'); + } + $this->longOptions[$spec->long] = $spec; + } + if ($spec->short) { + if (isset($this->shortOptions[$spec->short])) { + throw new OptionConflictException('Option conflict: -'.$spec->short.' is already defined.'); + } + $this->shortOptions[$spec->short] = $spec; + } + $this->options[] = $spec; + if (!$spec->long && !$spec->short) { + throw new Exception('Neither long option name nor short name is not given.'); + } + } + + public function getLongOption($name) + { + return isset($this->longOptions[ $name ]) ? $this->longOptions[ $name ] : null; + } + + public function getShortOption($name) + { + return isset($this->shortOptions[ $name ]) ? $this->shortOptions[ $name ] : null; + } + + /* Get spec by spec id */ + public function get($id) + { + if (isset($this->data[$id])) { + return $this->data[$id]; + } else if (isset($this->longOptions[$id])) { + return $this->longOptions[$id]; + } else if (isset($this->shortOptions[$id])) { + return $this->shortOptions[$id]; + } + } + + public function find($name) + { + foreach ($this->options as $option) { + if ($option->short === $name || $option->long === $name) { + return $option; + } + } + } + + public function size() + { + return count($this->data); + } + + public function all() + { + return $this->data; + } + + public function toArray() + { + $array = array(); + foreach ($this->data as $k => $spec) { + $item = array(); + if ($spec->long) { + $item['long'] = $spec->long; + } + if ($spec->short) { + $item['short'] = $spec->short; + } + $item['desc'] = $spec->desc; + $array[] = $item; + } + + return $array; + } + + public function keys() + { + return array_merge(array_keys($this->longOptions), array_keys($this->shortOptions)); + } + + public function count() + { + return count($this->data); + } + + public function getIterator() + { + return new ArrayIterator($this->data); + } +} diff --git a/instafeed/vendor/corneltek/getoptionkit/src/OptionParser.php b/instafeed/vendor/corneltek/getoptionkit/src/OptionParser.php new file mode 100755 index 0000000..da9cba5 --- /dev/null +++ b/instafeed/vendor/corneltek/getoptionkit/src/OptionParser.php @@ -0,0 +1,193 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace GetOptionKit; + +use Exception; +use GetOptionKit\Exception\InvalidOptionException; +use GetOptionKit\Exception\RequireValueException; + +class OptionParser +{ + public $specs; + public $longOptions; + public $shortOptions; + + public function __construct(OptionCollection $specs) + { + $this->specs = $specs; + } + + public function setSpecs(OptionCollection $specs) + { + $this->specs = $specs; + } + + /** + * consume option value from current argument or from the next argument + * + * @return boolean next token consumed? + */ + protected function consumeOptionToken(Option $spec, $arg, $next, & $success = false) + { + // Check options doesn't require next token before + // all options that require values. + if ($spec->isFlag()) { + + if ($spec->isIncremental()) { + $spec->increaseValue(); + } else { + $spec->setValue(true); + } + return 0; + + } else if ($spec->isRequired()) { + + if ($next && !$next->isEmpty() && !$next->anyOfOptions($this->specs)) { + $spec->setValue($next->arg); + return 1; + } else { + throw new RequireValueException("Option '{$arg->getOptionName()}' requires a value."); + } + + } else if ($spec->isMultiple()) { + + if ($next && !$next->isEmpty() && !$next->anyOfOptions($this->specs)) { + $this->pushOptionValue($spec, $arg, $next); + return 1; + } + + } else if ($spec->isOptional() && $next && !$next->isEmpty() && !$next->anyOfOptions($this->specs)) { + + $spec->setValue($next->arg); + return 1; + + } + return 0; + } + + /* + * push value to multipl value option + */ + protected function pushOptionValue(Option $spec, $arg, $next) + { + if ($next && !$next->anyOfOptions($this->specs)) { + $spec->pushValue($next->arg); + } + } + + /** + * preprocess the argv array + * + * - split option and option value + * - separate arguments after "--" + */ + protected function preprocessingArguments(array $argv) + { + // preprocessing arguments + $newArgv = array(); + $extra = array(); + $afterDash = false; + foreach ($argv as $arg) { + if ($arg === '--') { + $afterDash = true; + continue; + } + if ($afterDash) { + $extra[] = $arg; + continue; + } + + $a = new Argument($arg); + if ($a->anyOfOptions($this->specs) && $a->containsOptionValue()) { + list($opt, $val) = $a->splitAsOption(); + array_push($newArgv, $opt, $val); + } else { + $newArgv[] = $arg; + } + } + return array($newArgv, $extra); + } + + protected function fillDefaultValues(OptionCollection $opts, OptionResult $result) + { + // register option result from options with default value + foreach ($opts as $opt) { + if ($opt->value === null && $opt->defaultValue !== null) { + $opt->setValue($opt->getDefaultValue()); + $result->set($opt->getId(), $opt); + } + } + } + + /** + * @param array $argv + * + * @return OptionResult|Option[] + * + * @throws Exception\RequireValueException + * @throws Exception\InvalidOptionException + * @throws \Exception + */ + public function parse(array $argv) + { + $result = new OptionResult(); + + list($argv, $extra) = $this->preprocessingArguments($argv); + + $len = count($argv); + + // some people might still pass only the option names here. + $first = new Argument($argv[0]); + if ($first->isOption()) { + throw new Exception('parse(argv) expects the first argument to be the program name.'); + } + + for ($i = 1; $i < $len; ++$i) { + $arg = new Argument($argv[$i]); + + // if looks like not an option, push it to argument list. + // TODO: we might want to support argument with preceding dash (?) + if (!$arg->isOption()) { + $result->addArgument($arg); + continue; + } + + // if the option is with extra flags, + // split the string, and insert into the argv array + if ($arg->withExtraFlagOptions()) { + $extra = $arg->extractExtraFlagOptions(); + array_splice($argv, $i + 1, 0, $extra); + $argv[$i] = $arg->arg; // update argument to current argv list. + $len = count($argv); // update argv list length + } + + $next = null; + if ($i + 1 < count($argv)) { + $next = new Argument($argv[$i + 1]); + } + + $spec = $this->specs->get($arg->getOptionName()); + if (!$spec) { + throw new InvalidOptionException('Invalid option: '.$arg); + } + + // This if expr might be unnecessary, becase we have default mode - flag + // if ($spec->isRequired() || $spec->isMultiple() || $spec->isOptional() || $spec->isFlag()) { + $i += $this->consumeOptionToken($spec, $arg, $next); + $result->set($spec->getId(), $spec); + } + + $this->fillDefaultValues($this->specs, $result); + + return $result; + } +} diff --git a/instafeed/vendor/corneltek/getoptionkit/src/OptionPrinter/ConsoleOptionPrinter.php b/instafeed/vendor/corneltek/getoptionkit/src/OptionPrinter/ConsoleOptionPrinter.php new file mode 100755 index 0000000..dba0a50 --- /dev/null +++ b/instafeed/vendor/corneltek/getoptionkit/src/OptionPrinter/ConsoleOptionPrinter.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace GetOptionKit\OptionPrinter; + +use GetOptionKit\OptionCollection; +use GetOptionKit\Option; + +class ConsoleOptionPrinter implements OptionPrinter +{ + public $screenWidth = 78; + + /** + * Render readable spec. + */ + public function renderOption(Option $opt) + { + $c1 = ''; + if ($opt->short && $opt->long) { + $c1 = sprintf('-%s, --%s', $opt->short, $opt->long); + } else if ($opt->short) { + $c1 = sprintf('-%s', $opt->short); + } else if ($opt->long) { + $c1 = sprintf('--%s', $opt->long); + } + $c1 .= $opt->renderValueHint(); + + return $c1; + } + + /** + * render option descriptions. + * + * @return string output + */ + public function render(OptionCollection $options) + { + # echo "* Available options:\n"; + $lines = array(); + foreach ($options as $option) { + $c1 = $this->renderOption($option); + $lines[] = "\t".$c1; + $lines[] = wordwrap("\t\t".$option->desc, $this->screenWidth, "\n\t\t"); # wrap text + $lines[] = ''; + } + + return implode("\n", $lines); + } +} diff --git a/instafeed/vendor/corneltek/getoptionkit/src/OptionPrinter/OptionPrinter.php b/instafeed/vendor/corneltek/getoptionkit/src/OptionPrinter/OptionPrinter.php new file mode 100755 index 0000000..4ed0c70 --- /dev/null +++ b/instafeed/vendor/corneltek/getoptionkit/src/OptionPrinter/OptionPrinter.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace GetOptionKit; + +use ArrayIterator; +use ArrayAccess; +use IteratorAggregate; +use Countable; + +/** + * Define the getopt parsing result. + * + * create option result from array() + * + * OptionResult::create($spec, array( + * 'key' => 'value' + * ), array( ... arguments ... ) ); + */ +class OptionResult + implements IteratorAggregate, ArrayAccess, Countable +{ + /** + * @var array option specs, key => Option object + * */ + public $keys = array(); + + private $currentKey; + + /* arguments */ + public $arguments = array(); + + public function getIterator() + { + return new ArrayIterator($this->keys); + } + + public function count() + { + return count($this->keys); + } + + public function merge(OptionResult $a) + { + $this->keys = array_merge($this->keys, $a->keys); + $this->arguments = array_merge($this->arguments, $a->arguments); + } + + public function __isset($key) + { + return isset($this->keys[$key]); + } + + public function __get($key) + { + return $this->get($key); + } + + public function get($key) + { + if (isset($this->keys[$key])) { + return $this->keys[$key]->getValue(); + } + + // verifying if we got a camelCased key: http://stackoverflow.com/a/7599674/102960 + // get $options->baseDir as $option->{'base-dir'} + $parts = preg_split('/(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])/', $key); + if (sizeof($parts) > 1) { + $key = implode('-', array_map('strtolower', $parts)); + } + if (isset($this->keys[$key])) { + return $this->keys[$key]->getValue(); + } + } + + public function __set($key, $value) + { + $this->keys[ $key ] = $value; + } + + public function has($key) + { + return isset($this->keys[ $key ]); + } + + public function set($key, Option $value) + { + $this->keys[ $key ] = $value; + } + + public function addArgument(Argument $arg) + { + $this->arguments[] = $arg; + } + + public function getArguments() + { + return array_map(function ($e) { return $e->__toString(); }, $this->arguments); + } + + public function offsetSet($name, $value) + { + $this->keys[ $name ] = $value; + } + + public function offsetExists($name) + { + return isset($this->keys[ $name ]); + } + + public function offsetGet($name) + { + return $this->keys[ $name ]; + } + + public function offsetUnset($name) + { + unset($this->keys[$name]); + } + + public function toArray() + { + $array = array(); + foreach ($this->keys as $key => $option) { + $array[ $key ] = $option->getValue(); + } + + return $array; + } + + public static function create($specs, array $values = array(), array $arguments = null) + { + $new = new self(); + foreach ($specs as $spec) { + $id = $spec->getId(); + if (isset($values[$id])) { + $new->$id = $spec; + $spec->setValue($values[$id]); + } + if ($arguments) { + foreach ($arguments as $arg) { + $new->addArgument(new Argument($arg)); + } + } + } + + return $new; + } +} diff --git a/instafeed/vendor/corneltek/getoptionkit/src/ValueType/BaseType.php b/instafeed/vendor/corneltek/getoptionkit/src/ValueType/BaseType.php new file mode 100755 index 0000000..98477bf --- /dev/null +++ b/instafeed/vendor/corneltek/getoptionkit/src/ValueType/BaseType.php @@ -0,0 +1,34 @@ +option = $option; + } + } + + /** + * Test a value to see if it fit the type. + * + * @param mixed $value + */ + abstract public function test($value); + + /** + * Parse a string value into it's type value. + * + * @param mixed $value + */ + abstract public function parse($value); +} diff --git a/instafeed/vendor/corneltek/getoptionkit/src/ValueType/BoolType.php b/instafeed/vendor/corneltek/getoptionkit/src/ValueType/BoolType.php new file mode 100755 index 0000000..e2922d8 --- /dev/null +++ b/instafeed/vendor/corneltek/getoptionkit/src/ValueType/BoolType.php @@ -0,0 +1,10 @@ + DateTime::ATOM, + ); + + public function test($value) + { + return DateTime::createFromFormat($this->option['format'], $value) !== false; + } + + public function parse($value) + { + return DateTime::createFromFormat($this->option['format'], $value); + } +} diff --git a/instafeed/vendor/corneltek/getoptionkit/src/ValueType/DateType.php b/instafeed/vendor/corneltek/getoptionkit/src/ValueType/DateType.php new file mode 100755 index 0000000..d168ef4 --- /dev/null +++ b/instafeed/vendor/corneltek/getoptionkit/src/ValueType/DateType.php @@ -0,0 +1,20 @@ + 0) { + return false; + } + return true; + } + + public function parse($value) + { + return date_parse($value); + } +} diff --git a/instafeed/vendor/corneltek/getoptionkit/src/ValueType/DirType.php b/instafeed/vendor/corneltek/getoptionkit/src/ValueType/DirType.php new file mode 100755 index 0000000..de28279 --- /dev/null +++ b/instafeed/vendor/corneltek/getoptionkit/src/ValueType/DirType.php @@ -0,0 +1,18 @@ +option = $option; + } + + public function test($value) + { + return preg_match($this->option, $value) !== 0; + } + + public function parse($value) + { + preg_match($this->option, $value, $this->matches); + return strval($value); + } +} diff --git a/instafeed/vendor/corneltek/getoptionkit/src/ValueType/StringType.php b/instafeed/vendor/corneltek/getoptionkit/src/ValueType/StringType.php new file mode 100755 index 0000000..cf41b7b --- /dev/null +++ b/instafeed/vendor/corneltek/getoptionkit/src/ValueType/StringType.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +use GetOptionKit\Argument; +class ArgumentTest extends \PHPUnit\Framework\TestCase +{ + function test() + { + $arg = new Argument( '--option' ); + $this->assertTrue( $arg->isLongOption() ); + $this->assertFalse( $arg->isShortOption() ); + $this->assertEquals('option' , $arg->getOptionName()); + + $this->assertEquals(null, $arg->getOptionValue()); + } + + function test2() + { + $arg = new Argument('--option=value'); + $this->assertNotNull( $arg->containsOptionValue() ); + $this->assertEquals('value' , $arg->getOptionValue()); + $this->assertEquals('option' , $arg->getOptionName()); + } + + function test3() + { + $arg = new Argument( '-abc' ); + $this->assertNotNull( $arg->withExtraFlagOptions() ); + + $args = $arg->extractExtraFlagOptions(); + $this->assertNotNull( $args ); + $this->assertCount( 2, $args ); + + $this->assertEquals( '-b', $args[0] ); + $this->assertEquals( '-c', $args[1] ); + $this->assertEquals( '-a', $arg->arg); + } + + function testZeroValue() + { + $arg = new Argument( '0' ); + $this->assertFalse( $arg->isShortOption() ); + $this->assertFalse( $arg->isLongOption() ); + $this->assertFalse( $arg->isEmpty() ); + } +} + + diff --git a/instafeed/vendor/corneltek/getoptionkit/tests/ContinuousOptionParserTest.php b/instafeed/vendor/corneltek/getoptionkit/tests/ContinuousOptionParserTest.php new file mode 100755 index 0000000..b1c2ad1 --- /dev/null +++ b/instafeed/vendor/corneltek/getoptionkit/tests/ContinuousOptionParserTest.php @@ -0,0 +1,335 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace tests\GetOptionKit; +use GetOptionKit\ContinuousOptionParser; +use GetOptionKit\OptionCollection; + +class ContinuousOptionParserTest extends \PHPUnit\Framework\TestCase +{ + + public function testOptionCollection() + { + $specs = new OptionCollection; + $specVerbose = $specs->add('v|verbose'); + $specColor = $specs->add('c|color'); + $specDebug = $specs->add('d|debug'); + } + + + + public function argumentProvider() + { + return [ + [ + ['program','subcommand1', 'arg1', 'arg2', 'arg3', 'subcommand2', '-b', 1, 'subcommand3', '-b', 2], + [ + 'args' => ['arg1', 'arg2', 'arg3'] + ], + ], + [ + ['program','-v', '-c', 'subcommand1', '--as', 99, 'arg1', 'arg2', 'arg3'], + [ + 'app' => ['verbose' => true ], + 'args' => ['arg1', 'arg2', 'arg3'] + ], + ], + [ + ['program','-v', '-c', 'subcommand1', '--as', 99, 'arg1', 'arg2', 'arg3', '--','zz','xx','vv'], + [ + 'app' => ['verbose' => true], + 'args' => ['arg1', 'arg2', 'arg3'] + ], + ], + ]; + } + + + + /** + * @dataProvider argumentProvider + */ + public function testParseSubCommandOptions($argv, $expected) + { + $appspecs = new OptionCollection; + $appspecs->add('v|verbose'); + $appspecs->add('c|color'); + $appspecs->add('d|debug'); + + $cmdspecs = new OptionCollection; + $cmdspecs->add('as:'); + $cmdspecs->add('b:'); + $cmdspecs->add('c:'); + $cmdspecs->add('def:')->isa('number')->defaultValue(3); + + $parser = new ContinuousOptionParser( $appspecs ); + + $subcommands = array('subcommand1','subcommand2','subcommand3'); + $subcommand_specs = array( + 'subcommand1' => clone $cmdspecs, + 'subcommand2' => clone $cmdspecs, + 'subcommand3' => clone $cmdspecs, + ); + $subcommand_options = array(); + + // $argv = explode(' ','program -v -c subcommand1 --as 99 arg1 arg2 arg3 -- zz xx vv'); + // $argv = explode(' ','program subcommand1 -a 1 subcommand2 -a 2 subcommand3 -a 3 arg1 arg2 arg3'); + $app_options = $parser->parse( $argv ); + $arguments = array(); + while (! $parser->isEnd()) { + if (!empty($subcommands) && $parser->getCurrentArgument() == $subcommands[0]) { + $parser->advance(); + $subcommand = array_shift($subcommands); + $parser->setSpecs($subcommand_specs[$subcommand]); + $subcommand_options[$subcommand] = $parser->continueParse(); + } else { + $arguments[] = $parser->advance(); + } + } + $this->assertSame($expected['args'], $arguments); + if (isset($expected['app'])) { + foreach ($expected['app'] as $k => $v) { + $this->assertEquals($v, $app_options->get($k)); + } + } + + // $this->assertEquals(99, $subcommand_options['subcommand1']->as); + } + + + + public function testParser3() + { + $appspecs = new OptionCollection; + $appspecs->add('v|verbose'); + $appspecs->add('c|color'); + $appspecs->add('d|debug'); + + $cmdspecs = new OptionCollection; + $cmdspecs->add('n|name:=string'); + $cmdspecs->add('p|phone:=string'); + $cmdspecs->add('a|address:=string'); + + + $subcommands = array('subcommand1','subcommand2','subcommand3'); + $subcommand_specs = array( + 'subcommand1' => $cmdspecs, + 'subcommand2' => $cmdspecs, + 'subcommand3' => $cmdspecs, + ); + $subcommand_options = array(); + $arguments = array(); + + $argv = explode(' ','program -v -d -c subcommand1 --name=c9s --phone=123123123 --address=somewhere arg1 arg2 arg3'); + $parser = new ContinuousOptionParser( $appspecs ); + $app_options = $parser->parse( $argv ); + while (! $parser->isEnd()) { + if (@$subcommands[0] && $parser->getCurrentArgument() == $subcommands[0]) { + $parser->advance(); + $subcommand = array_shift( $subcommands ); + $parser->setSpecs( $subcommand_specs[$subcommand] ); + $subcommand_options[ $subcommand ] = $parser->continueParse(); + } else { + $arguments[] = $parser->advance(); + } + } + + $this->assertCount(3, $arguments); + $this->assertEquals('arg1', $arguments[0]); + $this->assertEquals('arg2', $arguments[1]); + $this->assertEquals('arg3', $arguments[2]); + + $this->assertNotNull($subcommand_options['subcommand1']); + $this->assertEquals('c9s', $subcommand_options['subcommand1']->name ); + $this->assertEquals('123123123', $subcommand_options['subcommand1']->phone ); + $this->assertEquals('somewhere', $subcommand_options['subcommand1']->address ); + } + + + /* test parser without options */ + function testParser4() + { + $appspecs = new OptionCollection; + $appspecs->add('v|verbose'); + $appspecs->add('c|color'); + $appspecs->add('d|debug'); + + $cmdspecs = new OptionCollection; + $cmdspecs->add('a:'); // required + $cmdspecs->add('b?'); // optional + $cmdspecs->add('c+'); // multiple (required) + + + + $parser = new ContinuousOptionParser( $appspecs ); + $this->assertNotNull( $parser ); + + $subcommands = array('subcommand1','subcommand2','subcommand3'); + $subcommand_specs = array( + 'subcommand1' => clone $cmdspecs, + 'subcommand2' => clone $cmdspecs, + 'subcommand3' => clone $cmdspecs, + ); + $subcommand_options = array(); + + $argv = explode(' ','program subcommand1 subcommand2 subcommand3 -a a -b b -c c'); + $app_options = $parser->parse( $argv ); + $arguments = array(); + while( ! $parser->isEnd() ) { + if( @$subcommands[0] && $parser->getCurrentArgument() == $subcommands[0] ) { + $parser->advance(); + $subcommand = array_shift( $subcommands ); + $parser->setSpecs( $subcommand_specs[$subcommand] ); + $subcommand_options[ $subcommand ] = $parser->continueParse(); + } else { + $arguments[] = $parser->advance(); + } + } + + $this->assertNotNull( $subcommand_options ); + $this->assertNotNull( $subcommand_options['subcommand1'] ); + $this->assertNotNull( $subcommand_options['subcommand2'] ); + $this->assertNotNull( $subcommand_options['subcommand3'] ); + + $r = $subcommand_options['subcommand3']; + $this->assertNotNull( $r ); + + + + $this->assertNotNull( $r->a , 'option a' ); + $this->assertNotNull( $r->b , 'option b' ); + $this->assertNotNull( $r->c , 'option c' ); + + $this->assertEquals( 'a', $r->a ); + $this->assertEquals( 'b', $r->b ); + $this->assertEquals( 'c', $r->c[0] ); + } + + /* test parser without options */ + function testParser5() + { + $appspecs = new OptionCollection; + $appspecs->add('v|verbose'); + $appspecs->add('c|color'); + $appspecs->add('d|debug'); + + $cmdspecs = new OptionCollection; + $cmdspecs->add('a:'); + $cmdspecs->add('b'); + $cmdspecs->add('c'); + + $parser = new ContinuousOptionParser( $appspecs ); + $this->assertNotNull( $parser ); + + $subcommands = array('subcommand1','subcommand2','subcommand3'); + $subcommand_specs = array( + 'subcommand1' => clone $cmdspecs, + 'subcommand2' => clone $cmdspecs, + 'subcommand3' => clone $cmdspecs, + ); + $subcommand_options = array(); + + $argv = explode(' ','program subcommand1 -a 1 subcommand2 -a 2 subcommand3 -a 3 arg1 arg2 arg3'); + $app_options = $parser->parse( $argv ); + $arguments = array(); + while (! $parser->isEnd()) { + if (!empty($subcommands) && $parser->getCurrentArgument() == $subcommands[0] ) { + $parser->advance(); + $subcommand = array_shift( $subcommands ); + $parser->setSpecs($subcommand_specs[$subcommand]); + $subcommand_options[ $subcommand ] = $parser->continueParse(); + } else { + $arguments[] = $parser->advance(); + } + } + + $this->assertEquals( 'arg1', $arguments[0] ); + $this->assertEquals( 'arg2', $arguments[1] ); + $this->assertEquals( 'arg3', $arguments[2] ); + $this->assertNotNull( $subcommand_options ); + + $this->assertEquals(1, $subcommand_options['subcommand1']->a); + $this->assertNotNull( 2, $subcommand_options['subcommand2']->a ); + $this->assertNotNull( 3, $subcommand_options['subcommand3']->a ); + } + + /** + * @expectedException GetOptionKit\Exception\InvalidOptionException + */ + public function testParseInvalidOptionException() + { + $parser = new ContinuousOptionParser(new OptionCollection); + $parser->parse(array('app','--foo')); + $arguments = array(); + while (!$parser->isEnd()) + { + $arguments[] = $parser->getCurrentArgument(); + $parser->advance(); + } + } + + + + public function testMultipleShortOption() + { + $options = new OptionCollection; + $options->add("a"); + $options->add("b"); + $options->add("c"); + + $parser = new ContinuousOptionParser($options); + + $result = $parser->parse(array('app', '-ab', 'foo', 'bar')); + while (!$parser->isEnd()) + { + $arguments[] = $parser->getCurrentArgument(); + $parser->advance(); + } + + $this->assertTrue($result->keys["a"]->value); + $this->assertTrue($result->keys["b"]->value); + } + + public function testIncrementalValue() + { + $options = new OptionCollection; + $options->add("v|verbose")->incremental(); + $parser = new ContinuousOptionParser($options); + $result = $parser->parse(array('app', '-vvv')); + $this->assertEquals(3, $result->keys["verbose"]->value); + } + + + /** + * @expectedException GetOptionKit\Exception\InvalidOptionException + */ + public function testUnknownOption() + { + $options = new OptionCollection; + $options->add("v|verbose"); + $parser = new ContinuousOptionParser($options); + $result = $parser->parse(array('app', '-b')); + } + + /** + * @expectedException LogicException + */ + public function testAdvancedOutOfBounds() + { + $options = new OptionCollection; + $options->add("v|verbose"); + $parser = new ContinuousOptionParser($options); + $result = $parser->parse(array('app', '-v')); + $parser->advance(); + } + +} + diff --git a/instafeed/vendor/corneltek/getoptionkit/tests/OptionCollectionTest.php b/instafeed/vendor/corneltek/getoptionkit/tests/OptionCollectionTest.php new file mode 100755 index 0000000..542b293 --- /dev/null +++ b/instafeed/vendor/corneltek/getoptionkit/tests/OptionCollectionTest.php @@ -0,0 +1,46 @@ +add($o = new Option('v|verbose')); + $this->assertSame($o, $opts->getLongOption('verbose')); + $this->assertSame($o, $opts->getShortOption('v')); + } + + + /** + * @expectedException LogicException + */ + public function testAddInvalidOption() + { + $opts = new OptionCollection; + $opts->add(123); + } + + /** + * @expectedException GetOptionKit\Exception\OptionConflictException + */ + public function testOptionConflictShort() + { + $opts = new OptionCollection; + $opts->add('r|repeat'); + $opts->add('t|time'); + $opts->add('r|regex'); + } + + /** + * @expectedException GetOptionKit\Exception\OptionConflictException + */ + public function testOptionConflictLong() + { + $opts = new OptionCollection; + $opts->add('r|repeat'); + $opts->add('t|time'); + $opts->add('c|repeat'); + } +} diff --git a/instafeed/vendor/corneltek/getoptionkit/tests/OptionParserTest.php b/instafeed/vendor/corneltek/getoptionkit/tests/OptionParserTest.php new file mode 100755 index 0000000..5d5a9b9 --- /dev/null +++ b/instafeed/vendor/corneltek/getoptionkit/tests/OptionParserTest.php @@ -0,0 +1,477 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ +use GetOptionKit\InvalidOptionValue; +use GetOptionKit\OptionCollection; +use GetOptionKit\OptionParser; +use GetOptionKit\Option; + +class OptionParserTest extends \PHPUnit\Framework\TestCase +{ + public $parser; + public $specs; + + public function setUp() + { + $this->specs = new OptionCollection; + $this->parser = new OptionParser($this->specs); + } + + /** + * @expectedException Exception + */ + public function testInvalidOption() + { + $options = new OptionCollection; + $options->addOption(new Option(0)); + } + + + public function testResultArrayAccessor() + { + $options = new OptionCollection; + $options->add('n|nice:' , 'I take negative value'); + $parser = new OptionParser($options); + $result = $parser->parse(array('a', '-n', '-1', '--', '......')); + + $this->assertTrue(isset($result->nice)); + $this->assertTrue($result->has('nice')); + $this->assertTrue(isset($result['nice'])); + $this->assertEquals(-1, $result['nice']->value); + + $res = clone $result['nice']; + $res->value = 10; + $result['nice'] = $res; + $this->assertEquals(10, $result['nice']->value); + + unset($result['nice']); + } + + public function testCamelCaseOptionName() + { + $this->specs->add('base-dir:=dir' , 'I take path'); + $result = $this->parser->parse(array('a', '--base-dir', 'src')); + $this->assertInstanceOf('SplFileInfo', $result->baseDir); + } + + public function testOptionWithNegativeValue() + { + $this->specs->add('n|nice:' , 'I take negative value'); + $result = $this->parser->parse(array('a', '-n', '-1')); + $this->assertEquals(-1, $result->nice); + } + + public function testShortOptionName() + { + $this->specs->add('f:' , 'file'); + $result = $this->parser->parse(array('a', '-f', 'aaa')); + $this->assertEquals('aaa',$result['f']->getValue()); + } + + public function testOptionWithShortNameAndLongName() + { + $this->specs->add( 'f|foo' , 'flag' ); + $result = $this->parser->parse(array('a', '-f')); + $this->assertTrue($result->foo); + + $result = $this->parser->parse(array('a', '--foo')); + $this->assertTrue($result->foo); + } + + public function testSpec() + { + $options = new OptionCollection; + $options->add( 'f|foo:' , 'option require value' ); + $options->add( 'b|bar+' , 'option with multiple value' ); + $options->add( 'z|zoo?' , 'option with optional value' ); + $options->add( 'v|verbose' , 'verbose message' ); + $options->add( 'd|debug' , 'debug message' ); + $this->assertEquals(5, $options->size()); + $this->assertEquals(5, count($options)); + + + $opt = $options->get('foo'); + $this->assertTrue($opt->isRequired()); + + $opt = $options->get('bar'); + $this->assertTrue( $opt->isMultiple() ); + + $opt = $options->get('zoo'); + $this->assertTrue( $opt->isOptional() ); + + $opt = $options->get( 'debug' ); + $this->assertNotNull( $opt ); + $this->assertInstanceOf('GetOptionKit\\Option', $opt); + $this->assertEquals('debug', $opt->long); + $this->assertEquals('d', $opt->short); + $this->assertTrue($opt->isFlag()); + + return $options; + } + + /** + * @depends testSpec + */ + public function testOptionFinder($options) + { + $this->assertNotNull($options->find('f')); + $this->assertNotNull($options->find('foo')); + $this->assertNull($options->find('xyz')); + } + + public function testRequire() + { + $this->specs->add( 'f|foo:' , 'option require value' ); + $this->specs->add( 'b|bar+' , 'option with multiple value' ); + $this->specs->add( 'z|zoo?' , 'option with optional value' ); + $this->specs->add( 'v|verbose' , 'verbose message' ); + $this->specs->add( 'd|debug' , 'debug message' ); + + $firstExceptionRaised = false; + $secondExceptionRaised = false; + + // option required a value should throw an exception + try { + $result = $this->parser->parse( array('a', '-f' , '-v' , '-d' ) ); + } + catch (Exception $e) { + $firstExceptionRaised = true; + } + + // even if only one option presented in args array + try { + $result = $this->parser->parse(array('a','-f')); + } catch (Exception $e) { + $secondExceptionRaised = true; + } + if ($firstExceptionRaised && $secondExceptionRaised) { + return; + } + $this->fail('An expected exception has not been raised.'); + } + + public function testMultiple() + { + $opt = new OptionCollection; + $opt->add( 'b|bar+' , 'option with multiple value' ); + $parser = new OptionParser($opt); + $result = $parser->parse(explode(' ','app -b 1 -b 2 --bar 3')); + $this->assertNotNull($result->bar); + $this->assertCount(3,$result->bar); + } + + + public function testMultipleNumber() + { + $opt = new OptionCollection; + $opt->add('b|bar+=number' , 'option with multiple value'); + $parser = new OptionParser($opt); + $result = $parser->parse(explode(' ','app --bar 1 --bar 2 --bar 3')); + $this->assertNotNull($result->bar); + $this->assertCount(3,$result->bar); + $this->assertSame(array(1,2,3),$result->bar); + } + + public function testSimpleOptionWithDefaultValue() + { + $opts = new OptionCollection; + $opts->add('p|proc=number' , 'option with required value') + ->defaultValue(10) + ; + $parser = new OptionParser($opts); + $result = $parser->parse(explode(' ','app')); + $this->assertEquals(10, $result['proc']->value); + } + + public function testOptionalOptionWithDefaultValue() + { + $opts = new OptionCollection; + $opts->add('p|proc?=number' , 'option with required value') + ->defaultValue(10) + ; + $parser = new OptionParser($opts); + $result = $parser->parse(explode(' ','app --proc')); + $this->assertEquals(10, $result['proc']->value); + } + + public function testMultipleString() + { + $opts = new OptionCollection; + $opts->add('b|bar+=string' , 'option with multiple value'); + $bar = $opts->get('bar'); + $this->assertNotNull($bar); + $this->assertTrue($bar->isMultiple()); + $this->assertTrue($bar->isType('string')); + $this->assertFalse($bar->isType('number')); + + + $parser = new OptionParser($opts); + $result = $parser->parse(explode(' ','app --bar lisa --bar mary --bar john a b c')); + $this->assertNotNull($result->bar); + $this->assertCount(3,$result->bar); + $this->assertSame(array('lisa', 'mary', 'john'),$result->bar); + $this->assertSame(array('a','b','c'), $result->getArguments()); + } + + public function testParseIncrementalOption() + { + $opts = new OptionCollection; + $opts->add('v|verbose' , 'verbose') + ->isa("number") + ->incremental(); + + $parser = new OptionParser($opts); + $result = $parser->parse(explode(' ','app -vvv arg1 arg2')); + $this->assertInstanceOf('GetOptionKit\Option',$result['verbose']); + $this->assertNotNull($result['verbose']); + $this->assertEquals(3, $result['verbose']->value); + } + + + /** + * @expectedException Exception + */ + public function testIntegerTypeNonNumeric() + { + $opt = new OptionCollection; + $opt->add( 'b|bar:=number' , 'option with integer type' ); + + $parser = new OptionParser($opt); + $spec = $opt->get('bar'); + $this->assertTrue($spec->isTypeNumber()); + + // test non numeric + $result = $parser->parse(explode(' ','app -b test')); + $this->assertNotNull($result->bar); + } + + + public function testIntegerTypeNumericWithoutEqualSign() + { + $opt = new OptionCollection; + $opt->add('b|bar:=number', 'option with integer type'); + + $spec = $opt->get('bar'); + $this->assertTrue($spec->isTypeNumber()); + + $parser = new OptionParser($opt); + $result = $parser->parse(explode(' ','app -b 123123')); + $this->assertNotNull($result); + $this->assertEquals(123123, $result->bar); + } + + public function testIntegerTypeNumericWithEqualSign() + { + $opt = new OptionCollection; + $opt->add('b|bar:=number' , 'option with integer type'); + + $spec = $opt->get('bar'); + $this->assertTrue($spec->isTypeNumber()); + + $parser = new OptionParser($opt); + $result = $parser->parse(explode(' ','app -b=123123')); + $this->assertNotNull($result); + $this->assertNotNull($result->bar); + $this->assertEquals(123123, $result->bar); + } + + public function testStringType() + { + $this->specs->add( 'b|bar:=string' , 'option with type' ); + + $spec = $this->specs->get('bar'); + + $result = $this->parser->parse(explode(' ','app -b text arg1 arg2 arg3')); + $this->assertNotNull($result->bar); + + $result = $this->parser->parse(explode(' ','app -b=text arg1 arg2 arg3')); + $this->assertNotNull($result->bar); + + $args = $result->getArguments(); + $this->assertNotEmpty($args); + $this->assertCount(3,$args); + $this->assertEquals('arg1', $args[0]); + $this->assertEquals('arg2', $args[1]); + $this->assertEquals('arg3', $args[2]); + } + + public function testStringQuoteOptionValue() + { + $opts = new OptionCollection(); + $opts->add('f|foo:' , 'option requires a value.'); + $parser = new OptionParser($opts); + $res = $parser->parse(['app','--foo=aa bb cc']); + $this->assertEquals('aa bb cc', $res->get('foo')); + } + + public function testSpec2() + { + $this->specs->add('long' , 'long option name only.'); + $this->specs->add('a' , 'short option name only.'); + $this->specs->add('b' , 'short option name only.'); + $this->assertNotNull($this->specs->all()); + $this->assertNotNull($this->specs); + $this->assertNotNull($result = $this->parser->parse(explode(' ','app -a -b --long')) ); + $this->assertNotNull($result->a); + $this->assertNotNull($result->b); + } + + + public function testSpecCollection() + { + $this->specs->add( 'f|foo:' , 'option requires a value.' ); + $this->specs->add( 'b|bar+' , 'option with multiple value.' ); + $this->specs->add( 'z|zoo?' , 'option with optional value.' ); + $this->specs->add( 'v|verbose' , 'verbose message.' ); + $this->specs->add( 'd|debug' , 'debug message.' ); + $this->specs->add( 'long' , 'long option name only.' ); + $this->specs->add( 's' , 'short option name only.' ); + + $this->assertNotNull( $this->specs->all() ); + $this->assertNotNull( $this->specs ); + + $this->assertCount( 7 , $array = $this->specs->toArray() ); + $this->assertNotEmpty( isset($array[0]['long'] )); + $this->assertNotEmpty( isset($array[0]['short'] )); + $this->assertNotEmpty( isset($array[0]['desc'] )); + } + + public function optionTestProvider() + { + return array( + array( 'foo', 'simple boolean option', 'foo', true, + [['a','--foo','a', 'b', 'c']] + ), + array( 'f|foo', 'simple boolean option', 'foo', true, + [['a','--foo'], ['a','-f']] + ), + array( 'f|foo:=string', 'string option', 'foo', 'xxx', + [['a','--foo','xxx'], ['a','-f', 'xxx']] + ), + array( 'f|foo:=string', 'string option', 'foo', 'xxx', + [['a','b', 'c', '--foo','xxx'], ['a', 'a', 'b', 'c', '-f', 'xxx']] + ), + ); + } + + /** + * @dataProvider optionTestProvider + */ + public function test($specString, $desc, $key, $expectedValue, array $argvList) + { + $opts = new OptionCollection(); + $opts->add($specString, $desc); + $parser = new OptionParser($opts); + foreach ($argvList as $argv) { + $res = $parser->parse($argv); + $this->assertSame($expectedValue, $res->get($key)); + } + } + + /** + * @expectedException Exception + */ + public function testParseWithoutProgramName() + { + $parser = new OptionParser(new OptionCollection); + $parser->parse(array('--foo')); + } + + /** + * @expectedException GetOptionKit\Exception\InvalidOptionException + */ + public function testParseInvalidOptionException() + { + $parser = new OptionParser(new OptionCollection); + $parser->parse(array('app','--foo')); + } + + /** + * @expectedException GetOptionKit\Exception\RequireValueException + */ + public function testParseOptionRequireValueException() + { + $options = new OptionCollection; + $options->add('name:=string', 'name'); + + $parser = new OptionParser($options); + $parser->parse(array('app','--name')); + } + + + + public function testMore() + { + $this->specs->add('f|foo:' , 'option require value' ); + $this->specs->add('b|bar+' , 'option with multiple value' ); + $this->specs->add('z|zoo?' , 'option with optional value' ); + $this->specs->add('v|verbose' , 'verbose message' ); + $this->specs->add('d|debug' , 'debug message' ); + + $result = $this->parser->parse( array('a', '-f' , 'foo value' , '-v' , '-d' ) ); + $this->assertNotNull($result->foo); + $this->assertNotNull($result->verbose); + $this->assertNotNull($result->debug); + $this->assertEquals( 'foo value', $result->foo ); + $this->assertNotNull( $result->verbose ); + $this->assertNotNull( $result->debug ); + + foreach ($result as $k => $v) { + $this->assertTrue(in_array($k, ['foo','bar','zoo','verbose', 'debug'])); + $this->assertInstanceOf('GetOptionKit\\Option', $v); + } + $this->assertSame([ + 'foo' => 'foo value', + 'verbose' => true, + 'debug' => true + ], $result->toArray()); + + $result = $this->parser->parse( array('a', '-f=foo value' , '-v' , '-d' ) ); + $this->assertNotNull( $result ); + $this->assertNotNull( $result->foo ); + $this->assertNotNull( $result->verbose ); + $this->assertNotNull( $result->debug ); + + $this->assertEquals( 'foo value', $result->foo ); + $this->assertNotNull( $result->verbose ); + $this->assertNotNull( $result->debug ); + + $result = $this->parser->parse( array('a', '-vd' ) ); + $this->assertNotNull( $result->verbose ); + $this->assertNotNull( $result->debug ); + } + + public function testParseAcceptsValidOption() + { + $this->specs + ->add('f:foo', 'test option') + ->validator(function($value) { + return $value === 'valid-option'; + }); + + $result = $this->parser->parse(array('a', '-f' , 'valid-option')); + + $this->assertArrayHasKey('f', $result); + } + + /** + * @expectedException GetOptionKit\Exception\InvalidOptionValueException + */ + public function testParseThrowsExceptionOnInvalidOption() + { + $this->specs + ->add('f:foo', 'test option') + ->validator(function($value) { + return $value === 'valid-option'; + }); + + $this->parser->parse(array('a', '-f' , 'not-a-valid-option')); + } +} diff --git a/instafeed/vendor/corneltek/getoptionkit/tests/OptionPrinter/ConsoleOptionPrinterTest.php b/instafeed/vendor/corneltek/getoptionkit/tests/OptionPrinter/ConsoleOptionPrinterTest.php new file mode 100755 index 0000000..2f06767 --- /dev/null +++ b/instafeed/vendor/corneltek/getoptionkit/tests/OptionPrinter/ConsoleOptionPrinterTest.php @@ -0,0 +1,32 @@ +add('f|foo:', 'option requires a value.' ) + ->isa('String'); + + $options->add('b|bar+', 'option with multiple value.' ) + ->isa('Number'); + + $options->add('z|zoo?', 'option with optional value.' ) + ->isa('Boolean') + ; + + $options->add('n', 'n flag' ); + + $options->add('verbose', 'verbose'); + + $options->add('o|output?', 'option with optional value.' ) + ->isa('File') + ->defaultValue('output.txt') + ; + $printer = new ConsoleOptionPrinter; + $output = $printer->render($options); + } + +} diff --git a/instafeed/vendor/corneltek/getoptionkit/tests/OptionResultTest.php b/instafeed/vendor/corneltek/getoptionkit/tests/OptionResultTest.php new file mode 100755 index 0000000..01be96d --- /dev/null +++ b/instafeed/vendor/corneltek/getoptionkit/tests/OptionResultTest.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +class OptionResultTest extends \PHPUnit\Framework\TestCase +{ + + function testOption() + { + $option = new \GetOptionKit\OptionResult; + $this->assertNotNull( $option ); + + $specs = new \GetOptionKit\OptionCollection; + $specs->add('name:','name'); + $result = \GetOptionKit\OptionResult::create($specs,array( 'name' => 'c9s' ),array( 'arg1' )); + $this->assertNotNull( $result ); + $this->assertNotNull( $result->arguments ); + $this->assertNotNull( $result->name ); + $this->assertEquals( 'c9s', $result->name ); + $this->assertEquals( $result->arguments[0] , 'arg1' ); + } + +} + + diff --git a/instafeed/vendor/corneltek/getoptionkit/tests/OptionTest.php b/instafeed/vendor/corneltek/getoptionkit/tests/OptionTest.php new file mode 100755 index 0000000..7b06f1d --- /dev/null +++ b/instafeed/vendor/corneltek/getoptionkit/tests/OptionTest.php @@ -0,0 +1,227 @@ +assertNotNull($opt); + } + + /** + * @expectedException Exception + */ + public function testInvalidOptionSpec() + { + new Option('....'); + } + + public function testValueName() + { + $opt = new Option('z'); + $opt->defaultValue(10); + $opt->valueName('priority'); + $this->assertEquals('[=priority]', $opt->renderValueHint()); + $this->assertEquals('-z[=priority]', $opt->renderReadableSpec()); + } + + + public function testDefaultValue() + { + $opt = new Option('z'); + $opt->defaultValue(10); + $this->assertEquals(10, $opt->getValue()); + $this->assertEquals('-z[=10]',$opt->renderReadableSpec(true)); + } + + public function testBackwardCompatibleBoolean() + { + $opt = new Option('scope'); + $opt->isa('bool'); + $this->assertEquals('boolean', $opt->isa); + $this->assertEquals('--scope=',$opt->renderReadableSpec(true)); + } + + + + public function validatorProvider() + { + return [ + [function($a) { return in_array($a, ['public', 'private']); }], + [function($a) { return [in_array($a, ['public', 'private']), "message"]; }] + ]; + } + + /** + * @dataProvider validatorProvider + */ + public function testValidator($cb) + { + $opt = new Option('scope'); + $opt->validator($cb); + $ret = $opt->validate('public'); + $this->assertTrue($ret[0]); + $ret = $opt->validate('private'); + $this->assertTrue($ret[0]); + $ret = $opt->validate('foo'); + $this->assertFalse($ret[0]); + $this->assertEquals('--scope', $opt->renderReadableSpec(true)); + } + + /** + * @expectedException Exception + */ + public function testInvalidTypeClass() + { + $opt = new Option('scope'); + $opt->isa('SomethingElse'); + $class = $opt->getTypeClass(); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testValidatorReturnValue() + { + $opt = new Option('scope'); + $opt->validator(function($val) { + return 123454; + }); + $ret = $opt->validate('public'); + } + + public function testOptionWithoutValidator() + { + $opt = new Option('scope'); + $ret = $opt->validate('public'); + $this->assertTrue($ret[0]); + $ret = $opt->validate('private'); + $this->assertTrue($ret[0]); + $ret = $opt->validate('foo'); + $this->assertTrue($ret[0]); + $this->assertEquals('--scope',$opt->renderReadableSpec(true)); + } + + + + + public function testSuggestionsCallback() + { + $opt = new Option('scope'); + $this->assertEmpty($opt->getSuggestions()); + + $opt->suggestions(function() { + return ['public', 'private']; + }); + $this->assertNotEmpty($opt->getSuggestions()); + $this->assertSame(['public', 'private'],$opt->getSuggestions()); + $opt->setValue('public'); + $opt->setValue('private'); + $this->assertEquals('private',$opt->value); + + $this->assertEquals('--scope=[public,private]',$opt->renderReadableSpec(true)); + } + + public function testSuggestions() + { + $opt = new Option('scope'); + $opt->suggestions(['public', 'private']); + $this->assertNotEmpty($opt->getSuggestions()); + $this->assertSame(['public', 'private'],$opt->getSuggestions()); + $opt->setValue('public'); + $opt->setValue('private'); + $this->assertEquals('private',$opt->value); + + $this->assertEquals('--scope=[public,private]',$opt->renderReadableSpec(true)); + } + + public function testValidValuesCallback() { + $opt = new Option('scope'); + $opt->validValues(function() { + return ['public', 'private']; + }); + $this->assertNotNull($opt->getValidValues()); + $this->assertNotEmpty($opt->getValidValues()); + + $opt->setValue('public'); + $opt->setValue('private'); + $this->assertEquals('private',$opt->value); + $this->assertEquals('--scope=(public,private)',$opt->renderReadableSpec(true)); + } + + public function testTrigger() + { + $opt = new Option('scope'); + $opt->validValues([ 'public', 'private' ]); + + $state = 0; + $opt->trigger(function($val) use(& $state) { + $state++; + }); + $this->assertNotEmpty($opt->getValidValues()); + $opt->setValue('public'); + + $this->assertEquals(1, $state); + $opt->setValue('private'); + $this->assertEquals(2, $state); + + } + + + + public function testArrayValueToString() + { + $opt = new Option('uid'); + $opt->setValue([1,2,3,4]); + $toString = '* key:uid spec:--uid desc: + value => 1,2,3,4 +'; + $this->assertEquals($toString,$opt->__toString()); + } + + public function testValidValues() + { + $opt = new Option('scope'); + $opt->validValues([ 'public', 'private' ]) + ; + $this->assertNotEmpty($opt->getValidValues()); + $this->assertTrue(is_array($opt->getValidValues())); + + $opt->setValue('public'); + $opt->setValue('private'); + $this->assertEquals('private',$opt->value); + $this->assertEquals('--scope=(public,private)',$opt->renderReadableSpec(true)); + $this->assertNotEmpty($opt->__toString()); + } + + + public function testFilter() { + $opt = new Option('scope'); + $opt->filter(function($val) { + return preg_replace('#a#', 'x', $val); + }) + ; + $opt->setValue('aa'); + $this->assertEquals('xx', $opt->value); + } +} + diff --git a/instafeed/vendor/corneltek/getoptionkit/tests/RegexValueTypeTest.php b/instafeed/vendor/corneltek/getoptionkit/tests/RegexValueTypeTest.php new file mode 100755 index 0000000..6917918 --- /dev/null +++ b/instafeed/vendor/corneltek/getoptionkit/tests/RegexValueTypeTest.php @@ -0,0 +1,26 @@ +assertEquals($regex->option, '#^Test$#'); + } + + public function testValidation() + { + $regex = new RegexType('#^Test$#'); + $this->assertTrue($regex->test('Test')); + $this->assertFalse($regex->test('test')); + + $regex->option = '/^([a-z]+)$/'; + $this->assertTrue($regex->test('barfoo')); + $this->assertFalse($regex->test('foobar234')); + $ret = $regex->parse('foobar234'); + $this->assertNotNull($ret); + } +} + diff --git a/instafeed/vendor/corneltek/getoptionkit/tests/ValueTypeTest.php b/instafeed/vendor/corneltek/getoptionkit/tests/ValueTypeTest.php new file mode 100755 index 0000000..08472c5 --- /dev/null +++ b/instafeed/vendor/corneltek/getoptionkit/tests/ValueTypeTest.php @@ -0,0 +1,200 @@ +assertNotNull( new BooleanType ); + $this->assertNotNull( new StringType ); + $this->assertNotNull( new FileType ); + $this->assertNotNull( new DateType ); + $this->assertNotNull( new DateTimeType ); + $this->assertNotNull( new NumberType ); + $this->assertNotNull( new UrlType ); + $this->assertNotNull( new IpType ); + $this->assertNotNull( new Ipv4Type ); + $this->assertNotNull( new Ipv6Type ); + $this->assertNotNull( new EmailType ); + $this->assertNotNull( new PathType ); + $this->assertNotNull( new RegexType("/[a-z]/")); + } + + + public function testDateTimeType() + { + $type = new DateTimeType([ 'format' => 'Y-m-d' ]); + $this->assertTrue($type->test('2016-12-30')); + $a = $type->parse('2016-12-30'); + $this->assertEquals(2016, $a->format('Y')); + $this->assertEquals(12, $a->format('m')); + $this->assertEquals(30, $a->format('d')); + $this->assertFalse($type->test('foo')); + } + + public function testDateType() + { + $type = new DateType; + $this->assertTrue($type->test('2016-12-30')); + $a = $type->parse('2016-12-30'); + $this->assertEquals(2016, $a['year']); + $this->assertEquals(12, $a['month']); + $this->assertEquals(30, $a['day']); + $this->assertFalse($type->test('foo')); + } + + + + public function booleanTestProvider() + { + return [ + [true , true, true], + [false , true, false], + ['true' , true, true], + ['false' , true, false], + ['0' , true, false], + ['1' , true, true], + ['foo' , false, null], + ['123' , false, null], + ]; + } + + /** + * @dataProvider booleanTestProvider + */ + public function testBooleanType($a, $test, $expected) + { + $bool = new BooleanType; + $this->assertEquals($test, $bool->test($a)); + if ($bool->test($a)) { + $this->assertEquals($expected, $bool->parse($a)); + } + } + + public function testDirType() + { + $type = new DirType; + $this->assertTrue($type->test('tests')); + $this->assertFalse($type->test('composer.json')); + $this->assertFalse($type->test('foo/bar')); + $this->assertInstanceOf('SplFileInfo',$type->parse('tests')); + } + + public function testFileType() + { + $type = new FileType; + $this->assertFalse($type->test('tests')); + $this->assertTrue($type->test('composer.json')); + $this->assertFalse($type->test('foo/bar')); + $this->assertInstanceOf('SplFileInfo', $type->parse('composer.json')); + } + + public function testPathType() + { + $type = new PathType; + $this->assertTrue($type->test('tests')); + $this->assertTrue($type->test('composer.json')); + $this->assertFalse($type->test('foo/bar')); + $this->assertInstanceOf('SplFileInfo', $type->parse('composer.json')); + } + + public function testUrlType() + { + $url = new UrlType; + $this->assertTrue($url->test('http://t')); + $this->assertTrue($url->test('http://t.c')); + $this->assertFalse($url->test('t.c')); + $this->assertEquals('http://t.c', $url->parse('http://t.c')); + } + + public function ipV4Provider() + { + return [ + ['192.168.25.58', true], + ['8.8.8.8', true], + ['github.com', false], + ]; + } + + public function ipV6Provider() + { + return [ + ['192.168.25.58', false], + ['2607:f0d0:1002:51::4', true], + ['2607:f0d0:1002:0051:0000:0000:0000:0004', true], + ['::1', true], + ['10.10.15.10/16', false], + ['github.com', false], + ]; + } + + public function ipProvider() + { + return [ + ['192.168.25.58', true], + ['2607:f0d0:1002:51::4', true], + ['::1', true], + ['10.10.15.10/16', false], + ['github.com', false], + ]; + } + + /** + * @dataProvider ipProvider + */ + public function testIpType($ipstr, $pass = true) + { + $ip = new IpType; + $this->assertEquals($pass, $ip->test($ipstr)); + if ($pass) { + $this->assertNotNull($ip->parse($ipstr)); + } + } + + /** + * @dataProvider ipV4Provider + */ + public function testIpv4Type($ipstr, $pass = true) + { + $ipv4 = new Ipv4Type; + $this->assertEquals($pass, $ipv4->test($ipstr)); + if ($pass) { + $this->assertNotNull($ipv4->parse($ipstr)); + } + } + + /** + * @dataProvider ipV6Provider + */ + public function testIpv6Type($ipstr, $pass = true) + { + $ipv6 = new Ipv6Type; + $this->assertEquals($pass, $ipv6->test($ipstr)); + if ($pass) { + $this->assertNotNull($ipv6->parse($ipstr)); + } + } + + public function testEmailType() + { + $email = new EmailType; + $this->assertTrue($email->test('test@gmail.com')); + $this->assertFalse($email->test('test@test')); + $email->parse('test@gmail.com'); + } +} + diff --git a/instafeed/vendor/evenement/evenement/.gitignore b/instafeed/vendor/evenement/evenement/.gitignore new file mode 100755 index 0000000..987e2a2 --- /dev/null +++ b/instafeed/vendor/evenement/evenement/.gitignore @@ -0,0 +1,2 @@ +composer.lock +vendor diff --git a/instafeed/vendor/evenement/evenement/.travis.yml b/instafeed/vendor/evenement/evenement/.travis.yml new file mode 100755 index 0000000..65ba0ce --- /dev/null +++ b/instafeed/vendor/evenement/evenement/.travis.yml @@ -0,0 +1,24 @@ +language: php + +php: + - 7.0 + - 7.1 + - hhvm + - nightly + +matrix: + allow_failures: + - php: hhvm + - php: nightly + +before_script: + - wget http://getcomposer.org/composer.phar + - php composer.phar install + +script: + - ./vendor/bin/phpunit --coverage-text + - php -n examples/benchmark-emit-no-arguments.php + - php -n examples/benchmark-emit-one-argument.php + - php -n examples/benchmark-emit.php + - php -n examples/benchmark-emit-once.php + - php -n examples/benchmark-remove-listener-once.php diff --git a/instafeed/vendor/evenement/evenement/CHANGELOG.md b/instafeed/vendor/evenement/evenement/CHANGELOG.md new file mode 100755 index 0000000..568f229 --- /dev/null +++ b/instafeed/vendor/evenement/evenement/CHANGELOG.md @@ -0,0 +1,35 @@ +CHANGELOG +========= + + +* v3.0.1 (2017-07-23) + + * Resolved regression introduced in once listeners in v3.0.0 [#49](https://github.com/igorw/evenement/pull/49) + +* v3.0.0 (2017-07-23) + + * Passing null as event name throw exception [#46](https://github.com/igorw/evenement/pull/46), and [#47](https://github.com/igorw/evenement/pull/47) + * Performance improvements [#39](https://github.com/igorw/evenement/pull/39), and [#45](https://github.com/igorw/evenement/pull/45) + * Remove once listeners [#44](https://github.com/igorw/evenement/pull/44), [#45](https://github.com/igorw/evenement/pull/45) + +* v2.1.0 (2017-07-17) + + * Chaining for "on" method [#30](https://github.com/igorw/evenement/pull/30) + * Unit tests (on Travis) improvements [#33](https://github.com/igorw/evenement/pull/33), [#36](https://github.com/igorw/evenement/pull/36), and [#37](https://github.com/igorw/evenement/pull/37) + * Benchmarks added [#35](https://github.com/igorw/evenement/pull/35), and [#40](https://github.com/igorw/evenement/pull/40) + * Minor performance improvements [#42](https://github.com/igorw/evenement/pull/42), and [#38](https://github.com/igorw/evenement/pull/38) + +* v2.0.0 (2012-11-02) + + * Require PHP >=5.4.0 + * Added EventEmitterTrait + * Removed EventEmitter2 + +* v1.1.0 (2017-07-17) + + * Chaining for "on" method [#29](https://github.com/igorw/evenement/pull/29) + * Minor performance improvements [#43](https://github.com/igorw/evenement/pull/43) + +* v1.0.0 (2012-05-30) + + * Inital stable release diff --git a/instafeed/vendor/evenement/evenement/LICENSE b/instafeed/vendor/evenement/evenement/LICENSE new file mode 100755 index 0000000..d9a37d0 --- /dev/null +++ b/instafeed/vendor/evenement/evenement/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2011 Igor Wiedler + +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/instafeed/vendor/evenement/evenement/README.md b/instafeed/vendor/evenement/evenement/README.md new file mode 100755 index 0000000..9443011 --- /dev/null +++ b/instafeed/vendor/evenement/evenement/README.md @@ -0,0 +1,83 @@ +# Événement + +Événement is a very simple event dispatching library for PHP. + +It has the same design goals as [Silex](http://silex-project.org) and +[Pimple](http://pimple-project.org), to empower the user while staying concise +and simple. + +It is very strongly inspired by the EventEmitter API found in +[node.js](http://nodejs.org). + +[![Build Status](https://secure.travis-ci.org/igorw/evenement.png?branch=master)](http://travis-ci.org/igorw/evenement) + +## Fetch + +The recommended way to install Événement is [through composer](http://getcomposer.org). + +Just create a composer.json file for your project: + +```JSON +{ + "require": { + "evenement/evenement": "^3.0 || ^2.0" + } +} +``` + +**Note:** The `3.x` version of Événement requires PHP 7 and the `2.x` version requires PHP 5.4. If you are +using PHP 5.3, please use the `1.x` version: + +```JSON +{ + "require": { + "evenement/evenement": "^1.0" + } +} +``` + +And run these two commands to install it: + + $ curl -s http://getcomposer.org/installer | php + $ php composer.phar install + +Now you can add the autoloader, and you will have access to the library: + +```php +on('user.created', function (User $user) use ($logger) { + $logger->log(sprintf("User '%s' was created.", $user->getLogin())); +}); +``` + +### Emitting Events + +```php +emit('user.created', [$user]); +``` + +Tests +----- + + $ ./vendor/bin/phpunit + +License +------- +MIT, see LICENSE. diff --git a/instafeed/vendor/evenement/evenement/composer.json b/instafeed/vendor/evenement/evenement/composer.json new file mode 100755 index 0000000..cbb4827 --- /dev/null +++ b/instafeed/vendor/evenement/evenement/composer.json @@ -0,0 +1,29 @@ +{ + "name": "evenement/evenement", + "description": "Événement is a very simple event dispatching library for PHP", + "keywords": ["event-dispatcher", "event-emitter"], + "license": "MIT", + "authors": [ + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + } + ], + "require": { + "php": ">=7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "autoload": { + "psr-0": { + "Evenement": "src" + } + }, + "autoload-dev": { + "psr-0": { + "Evenement": "tests" + }, + "files": ["tests/Evenement/Tests/functions.php"] + } +} diff --git a/instafeed/vendor/evenement/evenement/doc/00-intro.md b/instafeed/vendor/evenement/evenement/doc/00-intro.md new file mode 100755 index 0000000..6c28a2a --- /dev/null +++ b/instafeed/vendor/evenement/evenement/doc/00-intro.md @@ -0,0 +1,28 @@ +# Introduction + +Événement is is French and means "event". The événement library aims to +provide a simple way of subscribing to events and notifying those subscribers +whenever an event occurs. + +The API that it exposes is almost a direct port of the EventEmitter API found +in node.js. It also includes an "EventEmitter". There are some minor +differences however. + +The EventEmitter is an implementation of the publish-subscribe pattern, which +is a generalized version of the observer pattern. The observer pattern +specifies an observable subject, which observers can register themselves to. +Once something interesting happens, the subject notifies its observers. + +Pub/sub takes the same idea but encapsulates the observation logic inside a +separate object which manages all of its subscribers or listeners. Subscribers +are bound to an event name, and will only receive notifications of the events +they subscribed to. + +**TLDR: What does evenement do, in short? It provides a mapping from event +names to a list of listener functions and triggers each listener for a given +event when it is emitted.** + +Why do we do this, you ask? To achieve decoupling. + +It allows you to design a system where the core will emit events, and modules +are able to subscribe to these events. And respond to them. diff --git a/instafeed/vendor/evenement/evenement/doc/01-api.md b/instafeed/vendor/evenement/evenement/doc/01-api.md new file mode 100755 index 0000000..17ba333 --- /dev/null +++ b/instafeed/vendor/evenement/evenement/doc/01-api.md @@ -0,0 +1,91 @@ +# API + +The API that événement exposes is defined by the +`Evenement\EventEmitterInterface`. The interface is useful if you want to +define an interface that extends the emitter and implicitly defines certain +events to be emitted, or if you want to type hint an `EventEmitter` to be +passed to a method without coupling to the specific implementation. + +## on($event, callable $listener) + +Allows you to subscribe to an event. + +Example: + +```php +$emitter->on('user.created', function (User $user) use ($logger) { + $logger->log(sprintf("User '%s' was created.", $user->getLogin())); +}); +``` + +Since the listener can be any callable, you could also use an instance method +instead of the anonymous function: + +```php +$loggerSubscriber = new LoggerSubscriber($logger); +$emitter->on('user.created', array($loggerSubscriber, 'onUserCreated')); +``` + +This has the benefit that listener does not even need to know that the emitter +exists. + +You can also accept more than one parameter for the listener: + +```php +$emitter->on('numbers_added', function ($result, $a, $b) {}); +``` + +## once($event, callable $listener) + +Convenience method that adds a listener which is guaranteed to only be called +once. + +Example: + +```php +$conn->once('connected', function () use ($conn, $data) { + $conn->send($data); +}); +``` + +## emit($event, array $arguments = []) + +Emit an event, which will call all listeners. + +Example: + +```php +$conn->emit('data', [$data]); +``` + +The second argument to emit is an array of listener arguments. This is how you +specify more args: + +```php +$result = $a + $b; +$emitter->emit('numbers_added', [$result, $a, $b]); +``` + +## listeners($event) + +Allows you to inspect the listeners attached to an event. Particularly useful +to check if there are any listeners at all. + +Example: + +```php +$e = new \RuntimeException('Everything is broken!'); +if (0 === count($emitter->listeners('error'))) { + throw $e; +} +``` + +## removeListener($event, callable $listener) + +Remove a specific listener for a specific event. + +## removeAllListeners($event = null) + +Remove all listeners for a specific event or all listeners all together. This +is useful for long-running processes, where you want to remove listeners in +order to allow them to get garbage collected. diff --git a/instafeed/vendor/evenement/evenement/doc/02-plugin-system.md b/instafeed/vendor/evenement/evenement/doc/02-plugin-system.md new file mode 100755 index 0000000..6a08371 --- /dev/null +++ b/instafeed/vendor/evenement/evenement/doc/02-plugin-system.md @@ -0,0 +1,155 @@ +# Example: Plugin system + +In this example I will show you how to create a generic plugin system with +événement where plugins can alter the behaviour of the app. The app is a blog. +Boring, I know. By using the EventEmitter it will be easy to extend this blog +with additional functionality without modifying the core system. + +The blog is quite basic. Users are able to create blog posts when they log in. +The users are stored in a static config file, so there is no sign up process. +Once logged in they get a "new post" link which gives them a form where they +can create a new blog post with plain HTML. That will store the post in a +document database. The index lists all blog post titles by date descending. +Clicking on the post title will take you to the full post. + +## Plugin structure + +The goal of the plugin system is to allow features to be added to the blog +without modifying any core files of the blog. + +The plugins are managed through a config file, `plugins.json`. This JSON file +contains a JSON-encoded list of class-names for plugin classes. This allows +you to enable and disable plugins in a central location. The initial +`plugins.json` is just an empty array: +```json +[] +``` + +A plugin class must implement the `PluginInterface`: +```php +interface PluginInterface +{ + function attachEvents(EventEmitterInterface $emitter); +} +``` + +The `attachEvents` method allows the plugin to attach any events to the +emitter. For example: +```php +class FooPlugin implements PluginInterface +{ + public function attachEvents(EventEmitterInterface $emitter) + { + $emitter->on('foo', function () { + echo 'bar!'; + }); + } +} +``` + +The blog system creates an emitter instance and loads the plugins: +```php +$emitter = new EventEmitter(); + +$pluginClasses = json_decode(file_get_contents('plugins.json'), true); +foreach ($pluginClasses as $pluginClass) { + $plugin = new $pluginClass(); + $pluginClass->attachEvents($emitter); +} +``` + +This is the base system. There are no plugins yet, and there are no events yet +either. That's because I don't know which extension points will be needed. I +will add them on demand. + +## Feature: Markdown + +Writing blog posts in HTML sucks! Wouldn't it be great if I could write them +in a nice format such as markdown, and have that be converted to HTML for me? + +This feature will need two extension points. I need to be able to mark posts +as markdown, and I need to be able to hook into the rendering of the post body +and convert it from markdown to HTML. So the blog needs two new events: +`post.create` and `post.render`. + +In the code that creates the post, I'll insert the `post.create` event: +```php +class PostEvent +{ + public $post; + + public function __construct(array $post) + { + $this->post = $post; + } +} + +$post = createPostFromRequest($_POST); + +$event = new PostEvent($post); +$emitter->emit('post.create', [$event]); +$post = $event->post; + +$db->save('post', $post); +``` + +This shows that you can wrap a value in an event object to make it mutable, +allowing listeners to change it. + +The same thing for the `post.render` event: +```php +public function renderPostBody(array $post) +{ + $emitter = $this->emitter; + + $event = new PostEvent($post); + $emitter->emit('post.render', [$event]); + $post = $event->post; + + return $post['body']; +} + +

+

+``` + +Ok, the events are in place. It's time to create the first plugin, woohoo! I +will call this the `MarkdownPlugin`, so here's `plugins.json`: +```json +[ + "MarkdownPlugin" +] +``` + +The `MarkdownPlugin` class will be autoloaded, so I don't have to worry about +including any files. I just have to worry about implementing the plugin class. +The `markdown` function represents a markdown to HTML converter. +```php +class MarkdownPlugin implements PluginInterface +{ + public function attachEvents(EventEmitterInterface $emitter) + { + $emitter->on('post.create', function (PostEvent $event) { + $event->post['format'] = 'markdown'; + }); + + $emitter->on('post.render', function (PostEvent $event) { + if (isset($event->post['format']) && 'markdown' === $event->post['format']) { + $event->post['body'] = markdown($event->post['body']); + } + }); + } +} +``` + +There you go, the blog now renders posts as markdown. But all of the previous +posts before the addition of the markdown plugin are still rendered correctly +as raw HTML. + +## Feature: Comments + +TODO + +## Feature: Comment spam control + +TODO diff --git a/instafeed/vendor/evenement/evenement/examples/benchmark-emit-no-arguments.php b/instafeed/vendor/evenement/evenement/examples/benchmark-emit-no-arguments.php new file mode 100755 index 0000000..53d7f4b --- /dev/null +++ b/instafeed/vendor/evenement/evenement/examples/benchmark-emit-no-arguments.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +const ITERATIONS = 10000000; + +use Evenement\EventEmitter; + +require __DIR__.'/../vendor/autoload.php'; + +$emitter = new EventEmitter(); + +$emitter->on('event', function () {}); + +$start = microtime(true); +for ($i = 0; $i < ITERATIONS; $i++) { + $emitter->emit('event'); +} +$time = microtime(true) - $start; + +echo 'Emitting ', number_format(ITERATIONS), ' events took: ', number_format($time, 2), 's', PHP_EOL; diff --git a/instafeed/vendor/evenement/evenement/examples/benchmark-emit-once.php b/instafeed/vendor/evenement/evenement/examples/benchmark-emit-once.php new file mode 100755 index 0000000..74f4d17 --- /dev/null +++ b/instafeed/vendor/evenement/evenement/examples/benchmark-emit-once.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +ini_set('memory_limit', '512M'); + +const ITERATIONS = 100000; + +use Evenement\EventEmitter; + +require __DIR__.'/../vendor/autoload.php'; + +$emitter = new EventEmitter(); + +for ($i = 0; $i < ITERATIONS; $i++) { + $emitter->once('event', function ($a, $b, $c) {}); +} + +$start = microtime(true); +$emitter->emit('event', [1, 2, 3]); +$time = microtime(true) - $start; + +echo 'Emitting one event to ', number_format(ITERATIONS), ' once listeners took: ', number_format($time, 2), 's', PHP_EOL; diff --git a/instafeed/vendor/evenement/evenement/examples/benchmark-emit-one-argument.php b/instafeed/vendor/evenement/evenement/examples/benchmark-emit-one-argument.php new file mode 100755 index 0000000..39fc4ba --- /dev/null +++ b/instafeed/vendor/evenement/evenement/examples/benchmark-emit-one-argument.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +const ITERATIONS = 10000000; + +use Evenement\EventEmitter; + +require __DIR__.'/../vendor/autoload.php'; + +$emitter = new EventEmitter(); + +$emitter->on('event', function ($a) {}); + +$start = microtime(true); +for ($i = 0; $i < ITERATIONS; $i++) { + $emitter->emit('event', [1]); +} +$time = microtime(true) - $start; + +echo 'Emitting ', number_format(ITERATIONS), ' events took: ', number_format($time, 2), 's', PHP_EOL; diff --git a/instafeed/vendor/evenement/evenement/examples/benchmark-emit.php b/instafeed/vendor/evenement/evenement/examples/benchmark-emit.php new file mode 100755 index 0000000..3ab639e --- /dev/null +++ b/instafeed/vendor/evenement/evenement/examples/benchmark-emit.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +const ITERATIONS = 10000000; + +use Evenement\EventEmitter; + +require __DIR__.'/../vendor/autoload.php'; + +$emitter = new EventEmitter(); + +$emitter->on('event', function ($a, $b, $c) {}); + +$start = microtime(true); +for ($i = 0; $i < ITERATIONS; $i++) { + $emitter->emit('event', [1, 2, 3]); +} +$time = microtime(true) - $start; + +echo 'Emitting ', number_format(ITERATIONS), ' events took: ', number_format($time, 2), 's', PHP_EOL; diff --git a/instafeed/vendor/evenement/evenement/examples/benchmark-remove-listener-once.php b/instafeed/vendor/evenement/evenement/examples/benchmark-remove-listener-once.php new file mode 100755 index 0000000..414be3b --- /dev/null +++ b/instafeed/vendor/evenement/evenement/examples/benchmark-remove-listener-once.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +ini_set('memory_limit', '512M'); + +const ITERATIONS = 100000; + +use Evenement\EventEmitter; + +require __DIR__.'/../vendor/autoload.php'; + +$emitter = new EventEmitter(); + +$listeners = []; +for ($i = 0; $i < ITERATIONS; $i++) { + $listeners[] = function ($a, $b, $c) {}; +} + +$start = microtime(true); +foreach ($listeners as $listener) { + $emitter->once('event', $listener); +} +$time = microtime(true) - $start; +echo 'Adding ', number_format(ITERATIONS), ' once listeners took: ', number_format($time, 2), 's', PHP_EOL; + +$start = microtime(true); +foreach ($listeners as $listener) { + $emitter->removeListener('event', $listener); +} +$time = microtime(true) - $start; +echo 'Removing ', number_format(ITERATIONS), ' once listeners took: ', number_format($time, 2), 's', PHP_EOL; diff --git a/instafeed/vendor/evenement/evenement/phpunit.xml.dist b/instafeed/vendor/evenement/evenement/phpunit.xml.dist new file mode 100755 index 0000000..70bc693 --- /dev/null +++ b/instafeed/vendor/evenement/evenement/phpunit.xml.dist @@ -0,0 +1,24 @@ + + + + + + ./tests/Evenement/ + + + + + + ./src/ + + + diff --git a/instafeed/vendor/evenement/evenement/src/Evenement/EventEmitter.php b/instafeed/vendor/evenement/evenement/src/Evenement/EventEmitter.php new file mode 100755 index 0000000..db189b9 --- /dev/null +++ b/instafeed/vendor/evenement/evenement/src/Evenement/EventEmitter.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Evenement; + +class EventEmitter implements EventEmitterInterface +{ + use EventEmitterTrait; +} diff --git a/instafeed/vendor/evenement/evenement/src/Evenement/EventEmitterInterface.php b/instafeed/vendor/evenement/evenement/src/Evenement/EventEmitterInterface.php new file mode 100755 index 0000000..310631a --- /dev/null +++ b/instafeed/vendor/evenement/evenement/src/Evenement/EventEmitterInterface.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 Evenement; + +interface EventEmitterInterface +{ + public function on($event, callable $listener); + public function once($event, callable $listener); + public function removeListener($event, callable $listener); + public function removeAllListeners($event = null); + public function listeners($event = null); + public function emit($event, array $arguments = []); +} diff --git a/instafeed/vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php b/instafeed/vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php new file mode 100755 index 0000000..a78e65c --- /dev/null +++ b/instafeed/vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php @@ -0,0 +1,135 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Evenement; + +use InvalidArgumentException; + +trait EventEmitterTrait +{ + protected $listeners = []; + protected $onceListeners = []; + + public function on($event, callable $listener) + { + if ($event === null) { + throw new InvalidArgumentException('event name must not be null'); + } + + if (!isset($this->listeners[$event])) { + $this->listeners[$event] = []; + } + + $this->listeners[$event][] = $listener; + + return $this; + } + + public function once($event, callable $listener) + { + if ($event === null) { + throw new InvalidArgumentException('event name must not be null'); + } + + if (!isset($this->onceListeners[$event])) { + $this->onceListeners[$event] = []; + } + + $this->onceListeners[$event][] = $listener; + + return $this; + } + + public function removeListener($event, callable $listener) + { + if ($event === null) { + throw new InvalidArgumentException('event name must not be null'); + } + + if (isset($this->listeners[$event])) { + $index = \array_search($listener, $this->listeners[$event], true); + if (false !== $index) { + unset($this->listeners[$event][$index]); + if (\count($this->listeners[$event]) === 0) { + unset($this->listeners[$event]); + } + } + } + + if (isset($this->onceListeners[$event])) { + $index = \array_search($listener, $this->onceListeners[$event], true); + if (false !== $index) { + unset($this->onceListeners[$event][$index]); + if (\count($this->onceListeners[$event]) === 0) { + unset($this->onceListeners[$event]); + } + } + } + } + + public function removeAllListeners($event = null) + { + if ($event !== null) { + unset($this->listeners[$event]); + } else { + $this->listeners = []; + } + + if ($event !== null) { + unset($this->onceListeners[$event]); + } else { + $this->onceListeners = []; + } + } + + public function listeners($event = null): array + { + if ($event === null) { + $events = []; + $eventNames = \array_unique( + \array_merge(\array_keys($this->listeners), \array_keys($this->onceListeners)) + ); + foreach ($eventNames as $eventName) { + $events[$eventName] = \array_merge( + isset($this->listeners[$eventName]) ? $this->listeners[$eventName] : [], + isset($this->onceListeners[$eventName]) ? $this->onceListeners[$eventName] : [] + ); + } + return $events; + } + + return \array_merge( + isset($this->listeners[$event]) ? $this->listeners[$event] : [], + isset($this->onceListeners[$event]) ? $this->onceListeners[$event] : [] + ); + } + + public function emit($event, array $arguments = []) + { + if ($event === null) { + throw new InvalidArgumentException('event name must not be null'); + } + + if (isset($this->listeners[$event])) { + foreach ($this->listeners[$event] as $listener) { + $listener(...$arguments); + } + } + + if (isset($this->onceListeners[$event])) { + $listeners = $this->onceListeners[$event]; + unset($this->onceListeners[$event]); + foreach ($listeners as $listener) { + $listener(...$arguments); + } + } + } +} diff --git a/instafeed/vendor/evenement/evenement/tests/Evenement/Tests/EventEmitterTest.php b/instafeed/vendor/evenement/evenement/tests/Evenement/Tests/EventEmitterTest.php new file mode 100755 index 0000000..28f3011 --- /dev/null +++ b/instafeed/vendor/evenement/evenement/tests/Evenement/Tests/EventEmitterTest.php @@ -0,0 +1,438 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Evenement\Tests; + +use Evenement\EventEmitter; +use InvalidArgumentException; +use PHPUnit\Framework\TestCase; + +class EventEmitterTest extends TestCase +{ + private $emitter; + + public function setUp() + { + $this->emitter = new EventEmitter(); + } + + public function testAddListenerWithLambda() + { + $this->emitter->on('foo', function () {}); + } + + public function testAddListenerWithMethod() + { + $listener = new Listener(); + $this->emitter->on('foo', [$listener, 'onFoo']); + } + + public function testAddListenerWithStaticMethod() + { + $this->emitter->on('bar', ['Evenement\Tests\Listener', 'onBar']); + } + + public function testAddListenerWithInvalidListener() + { + try { + $this->emitter->on('foo', 'not a callable'); + $this->fail(); + } catch (\Exception $e) { + } catch (\TypeError $e) { + } + } + + public function testOnce() + { + $listenerCalled = 0; + + $this->emitter->once('foo', function () use (&$listenerCalled) { + $listenerCalled++; + }); + + $this->assertSame(0, $listenerCalled); + + $this->emitter->emit('foo'); + + $this->assertSame(1, $listenerCalled); + + $this->emitter->emit('foo'); + + $this->assertSame(1, $listenerCalled); + } + + public function testOnceWithArguments() + { + $capturedArgs = []; + + $this->emitter->once('foo', function ($a, $b) use (&$capturedArgs) { + $capturedArgs = array($a, $b); + }); + + $this->emitter->emit('foo', array('a', 'b')); + + $this->assertSame(array('a', 'b'), $capturedArgs); + } + + public function testEmitWithoutArguments() + { + $listenerCalled = false; + + $this->emitter->on('foo', function () use (&$listenerCalled) { + $listenerCalled = true; + }); + + $this->assertSame(false, $listenerCalled); + $this->emitter->emit('foo'); + $this->assertSame(true, $listenerCalled); + } + + public function testEmitWithOneArgument() + { + $test = $this; + + $listenerCalled = false; + + $this->emitter->on('foo', function ($value) use (&$listenerCalled, $test) { + $listenerCalled = true; + + $test->assertSame('bar', $value); + }); + + $this->assertSame(false, $listenerCalled); + $this->emitter->emit('foo', ['bar']); + $this->assertSame(true, $listenerCalled); + } + + public function testEmitWithTwoArguments() + { + $test = $this; + + $listenerCalled = false; + + $this->emitter->on('foo', function ($arg1, $arg2) use (&$listenerCalled, $test) { + $listenerCalled = true; + + $test->assertSame('bar', $arg1); + $test->assertSame('baz', $arg2); + }); + + $this->assertSame(false, $listenerCalled); + $this->emitter->emit('foo', ['bar', 'baz']); + $this->assertSame(true, $listenerCalled); + } + + public function testEmitWithNoListeners() + { + $this->emitter->emit('foo'); + $this->emitter->emit('foo', ['bar']); + $this->emitter->emit('foo', ['bar', 'baz']); + } + + public function testEmitWithTwoListeners() + { + $listenersCalled = 0; + + $this->emitter->on('foo', function () use (&$listenersCalled) { + $listenersCalled++; + }); + + $this->emitter->on('foo', function () use (&$listenersCalled) { + $listenersCalled++; + }); + + $this->assertSame(0, $listenersCalled); + $this->emitter->emit('foo'); + $this->assertSame(2, $listenersCalled); + } + + public function testRemoveListenerMatching() + { + $listenersCalled = 0; + + $listener = function () use (&$listenersCalled) { + $listenersCalled++; + }; + + $this->emitter->on('foo', $listener); + $this->emitter->removeListener('foo', $listener); + + $this->assertSame(0, $listenersCalled); + $this->emitter->emit('foo'); + $this->assertSame(0, $listenersCalled); + } + + public function testRemoveListenerNotMatching() + { + $listenersCalled = 0; + + $listener = function () use (&$listenersCalled) { + $listenersCalled++; + }; + + $this->emitter->on('foo', $listener); + $this->emitter->removeListener('bar', $listener); + + $this->assertSame(0, $listenersCalled); + $this->emitter->emit('foo'); + $this->assertSame(1, $listenersCalled); + } + + public function testRemoveAllListenersMatching() + { + $listenersCalled = 0; + + $this->emitter->on('foo', function () use (&$listenersCalled) { + $listenersCalled++; + }); + + $this->emitter->removeAllListeners('foo'); + + $this->assertSame(0, $listenersCalled); + $this->emitter->emit('foo'); + $this->assertSame(0, $listenersCalled); + } + + public function testRemoveAllListenersNotMatching() + { + $listenersCalled = 0; + + $this->emitter->on('foo', function () use (&$listenersCalled) { + $listenersCalled++; + }); + + $this->emitter->removeAllListeners('bar'); + + $this->assertSame(0, $listenersCalled); + $this->emitter->emit('foo'); + $this->assertSame(1, $listenersCalled); + } + + public function testRemoveAllListenersWithoutArguments() + { + $listenersCalled = 0; + + $this->emitter->on('foo', function () use (&$listenersCalled) { + $listenersCalled++; + }); + + $this->emitter->on('bar', function () use (&$listenersCalled) { + $listenersCalled++; + }); + + $this->emitter->removeAllListeners(); + + $this->assertSame(0, $listenersCalled); + $this->emitter->emit('foo'); + $this->emitter->emit('bar'); + $this->assertSame(0, $listenersCalled); + } + + public function testCallablesClosure() + { + $calledWith = null; + + $this->emitter->on('foo', function ($data) use (&$calledWith) { + $calledWith = $data; + }); + + $this->emitter->emit('foo', ['bar']); + + self::assertSame('bar', $calledWith); + } + + public function testCallablesClass() + { + $listener = new Listener(); + $this->emitter->on('foo', [$listener, 'onFoo']); + + $this->emitter->emit('foo', ['bar']); + + self::assertSame(['bar'], $listener->getData()); + } + + + public function testCallablesClassInvoke() + { + $listener = new Listener(); + $this->emitter->on('foo', $listener); + + $this->emitter->emit('foo', ['bar']); + + self::assertSame(['bar'], $listener->getMagicData()); + } + + public function testCallablesStaticClass() + { + $this->emitter->on('foo', '\Evenement\Tests\Listener::onBar'); + + $this->emitter->emit('foo', ['bar']); + + self::assertSame(['bar'], Listener::getStaticData()); + } + + public function testCallablesFunction() + { + $this->emitter->on('foo', '\Evenement\Tests\setGlobalTestData'); + + $this->emitter->emit('foo', ['bar']); + + self::assertSame('bar', $GLOBALS['evenement-evenement-test-data']); + + unset($GLOBALS['evenement-evenement-test-data']); + } + + public function testListeners() + { + $onA = function () {}; + $onB = function () {}; + $onC = function () {}; + $onceA = function () {}; + $onceB = function () {}; + $onceC = function () {}; + + self::assertCount(0, $this->emitter->listeners('event')); + $this->emitter->on('event', $onA); + self::assertCount(1, $this->emitter->listeners('event')); + self::assertSame([$onA], $this->emitter->listeners('event')); + $this->emitter->once('event', $onceA); + self::assertCount(2, $this->emitter->listeners('event')); + self::assertSame([$onA, $onceA], $this->emitter->listeners('event')); + $this->emitter->once('event', $onceB); + self::assertCount(3, $this->emitter->listeners('event')); + self::assertSame([$onA, $onceA, $onceB], $this->emitter->listeners('event')); + $this->emitter->on('event', $onB); + self::assertCount(4, $this->emitter->listeners('event')); + self::assertSame([$onA, $onB, $onceA, $onceB], $this->emitter->listeners('event')); + $this->emitter->removeListener('event', $onceA); + self::assertCount(3, $this->emitter->listeners('event')); + self::assertSame([$onA, $onB, $onceB], $this->emitter->listeners('event')); + $this->emitter->once('event', $onceC); + self::assertCount(4, $this->emitter->listeners('event')); + self::assertSame([$onA, $onB, $onceB, $onceC], $this->emitter->listeners('event')); + $this->emitter->on('event', $onC); + self::assertCount(5, $this->emitter->listeners('event')); + self::assertSame([$onA, $onB, $onC, $onceB, $onceC], $this->emitter->listeners('event')); + $this->emitter->once('event', $onceA); + self::assertCount(6, $this->emitter->listeners('event')); + self::assertSame([$onA, $onB, $onC, $onceB, $onceC, $onceA], $this->emitter->listeners('event')); + $this->emitter->removeListener('event', $onB); + self::assertCount(5, $this->emitter->listeners('event')); + self::assertSame([$onA, $onC, $onceB, $onceC, $onceA], $this->emitter->listeners('event')); + $this->emitter->emit('event'); + self::assertCount(2, $this->emitter->listeners('event')); + self::assertSame([$onA, $onC], $this->emitter->listeners('event')); + } + + public function testOnceCallIsNotRemovedWhenWorkingOverOnceListeners() + { + $aCalled = false; + $aCallable = function () use (&$aCalled) { + $aCalled = true; + }; + $bCalled = false; + $bCallable = function () use (&$bCalled, $aCallable) { + $bCalled = true; + $this->emitter->once('event', $aCallable); + }; + $this->emitter->once('event', $bCallable); + + self::assertFalse($aCalled); + self::assertFalse($bCalled); + $this->emitter->emit('event'); + + self::assertFalse($aCalled); + self::assertTrue($bCalled); + $this->emitter->emit('event'); + + self::assertTrue($aCalled); + self::assertTrue($bCalled); + } + + public function testEventNameMustBeStringOn() + { + self::expectException(InvalidArgumentException::class); + self::expectExceptionMessage('event name must not be null'); + + $this->emitter->on(null, function () {}); + } + + public function testEventNameMustBeStringOnce() + { + self::expectException(InvalidArgumentException::class); + self::expectExceptionMessage('event name must not be null'); + + $this->emitter->once(null, function () {}); + } + + public function testEventNameMustBeStringRemoveListener() + { + self::expectException(InvalidArgumentException::class); + self::expectExceptionMessage('event name must not be null'); + + $this->emitter->removeListener(null, function () {}); + } + + public function testEventNameMustBeStringEmit() + { + self::expectException(InvalidArgumentException::class); + self::expectExceptionMessage('event name must not be null'); + + $this->emitter->emit(null); + } + + public function testListenersGetAll() + { + $a = function () {}; + $b = function () {}; + $c = function () {}; + $d = function () {}; + + $this->emitter->once('event2', $c); + $this->emitter->on('event', $a); + $this->emitter->once('event', $b); + $this->emitter->on('event', $c); + $this->emitter->once('event', $d); + + self::assertSame( + [ + 'event' => [ + $a, + $c, + $b, + $d, + ], + 'event2' => [ + $c, + ], + ], + $this->emitter->listeners() + ); + } + + public function testOnceNestedCallRegression() + { + $first = 0; + $second = 0; + + $this->emitter->once('event', function () use (&$first, &$second) { + $first++; + $this->emitter->once('event', function () use (&$second) { + $second++; + }); + $this->emitter->emit('event'); + }); + $this->emitter->emit('event'); + + self::assertSame(1, $first); + self::assertSame(1, $second); + } +} diff --git a/instafeed/vendor/evenement/evenement/tests/Evenement/Tests/Listener.php b/instafeed/vendor/evenement/evenement/tests/Evenement/Tests/Listener.php new file mode 100755 index 0000000..df17424 --- /dev/null +++ b/instafeed/vendor/evenement/evenement/tests/Evenement/Tests/Listener.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Evenement\Tests; + +class Listener +{ + private $data = []; + + private $magicData = []; + + private static $staticData = []; + + public function onFoo($data) + { + $this->data[] = $data; + } + + public function __invoke($data) + { + $this->magicData[] = $data; + } + + public static function onBar($data) + { + self::$staticData[] = $data; + } + + public function getData() + { + return $this->data; + } + + public function getMagicData() + { + return $this->magicData; + } + + public static function getStaticData() + { + return self::$staticData; + } +} diff --git a/instafeed/vendor/evenement/evenement/tests/Evenement/Tests/functions.php b/instafeed/vendor/evenement/evenement/tests/Evenement/Tests/functions.php new file mode 100755 index 0000000..7f11f5b --- /dev/null +++ b/instafeed/vendor/evenement/evenement/tests/Evenement/Tests/functions.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Evenement\Tests; + +function setGlobalTestData($data) +{ + $GLOBALS['evenement-evenement-test-data'] = $data; +} diff --git a/instafeed/vendor/guzzlehttp/guzzle/.php_cs b/instafeed/vendor/guzzlehttp/guzzle/.php_cs new file mode 100755 index 0000000..a8ace8a --- /dev/null +++ b/instafeed/vendor/guzzlehttp/guzzle/.php_cs @@ -0,0 +1,21 @@ +setRiskyAllowed(true) + ->setRules([ + '@PSR2' => true, + 'array_syntax' => ['syntax' => 'short'], + 'declare_strict_types' => false, + 'concat_space' => ['spacing'=>'one'], + // 'ordered_imports' => true, + // 'phpdoc_align' => ['align'=>'vertical'], + // 'native_function_invocation' => true, + ]) + ->setFinder( + PhpCsFixer\Finder::create() + ->in(__DIR__.'/src') + ->name('*.php') + ) +; + +return $config; diff --git a/instafeed/vendor/guzzlehttp/guzzle/CHANGELOG.md b/instafeed/vendor/guzzlehttp/guzzle/CHANGELOG.md new file mode 100755 index 0000000..6555749 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/guzzle/CHANGELOG.md @@ -0,0 +1,1304 @@ +# Change Log + +## 6.4.1 - 2019-10-23 + +* No `guzzle.phar` was created in 6.4.0 due expired API token. This release will fix that +* Added `parent::__construct()` to `FileCookieJar` and `SessionCookieJar` + +## 6.4.0 - 2019-10-23 + +* Improvement: Improved error messages when using curl < 7.21.2 [#2108](https://github.com/guzzle/guzzle/pull/2108) +* Fix: Test if response is readable before returning a summary in `RequestException::getResponseBodySummary()` [#2081](https://github.com/guzzle/guzzle/pull/2081) +* Fix: Add support for GUZZLE_CURL_SELECT_TIMEOUT environment variable [#2161](https://github.com/guzzle/guzzle/pull/2161) +* Improvement: Added `GuzzleHttp\Exception\InvalidArgumentException` [#2163](https://github.com/guzzle/guzzle/pull/2163) +* Improvement: Added `GuzzleHttp\_current_time()` to use `hrtime()` if that function exists. [#2242](https://github.com/guzzle/guzzle/pull/2242) +* Improvement: Added curl's `appconnect_time` in `TransferStats` [#2284](https://github.com/guzzle/guzzle/pull/2284) +* Improvement: Make GuzzleException extend Throwable wherever it's available [#2273](https://github.com/guzzle/guzzle/pull/2273) +* Fix: Prevent concurrent writes to file when saving `CookieJar` [#2335](https://github.com/guzzle/guzzle/pull/2335) +* Improvement: Update `MockHandler` so we can test transfer time [#2362](https://github.com/guzzle/guzzle/pull/2362) + +## 6.3.3 - 2018-04-22 + +* Fix: Default headers when decode_content is specified + + +## 6.3.2 - 2018-03-26 + +* Fix: Release process + + +## 6.3.1 - 2018-03-26 + +* Bug fix: Parsing 0 epoch expiry times in cookies [#2014](https://github.com/guzzle/guzzle/pull/2014) +* Improvement: Better ConnectException detection [#2012](https://github.com/guzzle/guzzle/pull/2012) +* Bug fix: Malformed domain that contains a "/" [#1999](https://github.com/guzzle/guzzle/pull/1999) +* Bug fix: Undefined offset when a cookie has no first key-value pair [#1998](https://github.com/guzzle/guzzle/pull/1998) +* Improvement: Support PHPUnit 6 [#1953](https://github.com/guzzle/guzzle/pull/1953) +* Bug fix: Support empty headers [#1915](https://github.com/guzzle/guzzle/pull/1915) +* Bug fix: Ignore case during header modifications [#1916](https://github.com/guzzle/guzzle/pull/1916) + ++ Minor code cleanups, documentation fixes and clarifications. + + +## 6.3.0 - 2017-06-22 + +* Feature: force IP resolution (ipv4 or ipv6) [#1608](https://github.com/guzzle/guzzle/pull/1608), [#1659](https://github.com/guzzle/guzzle/pull/1659) +* Improvement: Don't include summary in exception message when body is empty [#1621](https://github.com/guzzle/guzzle/pull/1621) +* Improvement: Handle `on_headers` option in MockHandler [#1580](https://github.com/guzzle/guzzle/pull/1580) +* Improvement: Added SUSE Linux CA path [#1609](https://github.com/guzzle/guzzle/issues/1609) +* Improvement: Use class reference for getting the name of the class instead of using hardcoded strings [#1641](https://github.com/guzzle/guzzle/pull/1641) +* Feature: Added `read_timeout` option [#1611](https://github.com/guzzle/guzzle/pull/1611) +* Bug fix: PHP 7.x fixes [#1685](https://github.com/guzzle/guzzle/pull/1685), [#1686](https://github.com/guzzle/guzzle/pull/1686), [#1811](https://github.com/guzzle/guzzle/pull/1811) +* Deprecation: BadResponseException instantiation without a response [#1642](https://github.com/guzzle/guzzle/pull/1642) +* Feature: Added NTLM auth [#1569](https://github.com/guzzle/guzzle/pull/1569) +* Feature: Track redirect HTTP status codes [#1711](https://github.com/guzzle/guzzle/pull/1711) +* Improvement: Check handler type during construction [#1745](https://github.com/guzzle/guzzle/pull/1745) +* Improvement: Always include the Content-Length if there's a body [#1721](https://github.com/guzzle/guzzle/pull/1721) +* Feature: Added convenience method to access a cookie by name [#1318](https://github.com/guzzle/guzzle/pull/1318) +* Bug fix: Fill `CURLOPT_CAPATH` and `CURLOPT_CAINFO` properly [#1684](https://github.com/guzzle/guzzle/pull/1684) +* Improvement: Use `\GuzzleHttp\Promise\rejection_for` function instead of object init [#1827](https://github.com/guzzle/guzzle/pull/1827) + + ++ Minor code cleanups, documentation fixes and clarifications. + +## 6.2.3 - 2017-02-28 + +* Fix deprecations with guzzle/psr7 version 1.4 + +## 6.2.2 - 2016-10-08 + +* Allow to pass nullable Response to delay callable +* Only add scheme when host is present +* Fix drain case where content-length is the literal string zero +* Obfuscate in-URL credentials in exceptions + +## 6.2.1 - 2016-07-18 + +* Address HTTP_PROXY security vulnerability, CVE-2016-5385: + https://httpoxy.org/ +* Fixing timeout bug with StreamHandler: + https://github.com/guzzle/guzzle/pull/1488 +* Only read up to `Content-Length` in PHP StreamHandler to avoid timeouts when + a server does not honor `Connection: close`. +* Ignore URI fragment when sending requests. + +## 6.2.0 - 2016-03-21 + +* Feature: added `GuzzleHttp\json_encode` and `GuzzleHttp\json_decode`. + https://github.com/guzzle/guzzle/pull/1389 +* Bug fix: Fix sleep calculation when waiting for delayed requests. + https://github.com/guzzle/guzzle/pull/1324 +* Feature: More flexible history containers. + https://github.com/guzzle/guzzle/pull/1373 +* Bug fix: defer sink stream opening in StreamHandler. + https://github.com/guzzle/guzzle/pull/1377 +* Bug fix: do not attempt to escape cookie values. + https://github.com/guzzle/guzzle/pull/1406 +* Feature: report original content encoding and length on decoded responses. + https://github.com/guzzle/guzzle/pull/1409 +* Bug fix: rewind seekable request bodies before dispatching to cURL. + https://github.com/guzzle/guzzle/pull/1422 +* Bug fix: provide an empty string to `http_build_query` for HHVM workaround. + https://github.com/guzzle/guzzle/pull/1367 + +## 6.1.1 - 2015-11-22 + +* Bug fix: Proxy::wrapSync() now correctly proxies to the appropriate handler + https://github.com/guzzle/guzzle/commit/911bcbc8b434adce64e223a6d1d14e9a8f63e4e4 +* Feature: HandlerStack is now more generic. + https://github.com/guzzle/guzzle/commit/f2102941331cda544745eedd97fc8fd46e1ee33e +* Bug fix: setting verify to false in the StreamHandler now disables peer + verification. https://github.com/guzzle/guzzle/issues/1256 +* Feature: Middleware now uses an exception factory, including more error + context. https://github.com/guzzle/guzzle/pull/1282 +* Feature: better support for disabled functions. + https://github.com/guzzle/guzzle/pull/1287 +* Bug fix: fixed regression where MockHandler was not using `sink`. + https://github.com/guzzle/guzzle/pull/1292 + +## 6.1.0 - 2015-09-08 + +* Feature: Added the `on_stats` request option to provide access to transfer + statistics for requests. https://github.com/guzzle/guzzle/pull/1202 +* Feature: Added the ability to persist session cookies in CookieJars. + https://github.com/guzzle/guzzle/pull/1195 +* Feature: Some compatibility updates for Google APP Engine + https://github.com/guzzle/guzzle/pull/1216 +* Feature: Added support for NO_PROXY to prevent the use of a proxy based on + a simple set of rules. https://github.com/guzzle/guzzle/pull/1197 +* Feature: Cookies can now contain square brackets. + https://github.com/guzzle/guzzle/pull/1237 +* Bug fix: Now correctly parsing `=` inside of quotes in Cookies. + https://github.com/guzzle/guzzle/pull/1232 +* Bug fix: Cusotm cURL options now correctly override curl options of the + same name. https://github.com/guzzle/guzzle/pull/1221 +* Bug fix: Content-Type header is now added when using an explicitly provided + multipart body. https://github.com/guzzle/guzzle/pull/1218 +* Bug fix: Now ignoring Set-Cookie headers that have no name. +* Bug fix: Reason phrase is no longer cast to an int in some cases in the + cURL handler. https://github.com/guzzle/guzzle/pull/1187 +* Bug fix: Remove the Authorization header when redirecting if the Host + header changes. https://github.com/guzzle/guzzle/pull/1207 +* Bug fix: Cookie path matching fixes + https://github.com/guzzle/guzzle/issues/1129 +* Bug fix: Fixing the cURL `body_as_string` setting + https://github.com/guzzle/guzzle/pull/1201 +* Bug fix: quotes are no longer stripped when parsing cookies. + https://github.com/guzzle/guzzle/issues/1172 +* Bug fix: `form_params` and `query` now always uses the `&` separator. + https://github.com/guzzle/guzzle/pull/1163 +* Bug fix: Adding a Content-Length to PHP stream wrapper requests if not set. + https://github.com/guzzle/guzzle/pull/1189 + +## 6.0.2 - 2015-07-04 + +* Fixed a memory leak in the curl handlers in which references to callbacks + were not being removed by `curl_reset`. +* Cookies are now extracted properly before redirects. +* Cookies now allow more character ranges. +* Decoded Content-Encoding responses are now modified to correctly reflect + their state if the encoding was automatically removed by a handler. This + means that the `Content-Encoding` header may be removed an the + `Content-Length` modified to reflect the message size after removing the + encoding. +* Added a more explicit error message when trying to use `form_params` and + `multipart` in the same request. +* Several fixes for HHVM support. +* Functions are now conditionally required using an additional level of + indirection to help with global Composer installations. + +## 6.0.1 - 2015-05-27 + +* Fixed a bug with serializing the `query` request option where the `&` + separator was missing. +* Added a better error message for when `body` is provided as an array. Please + use `form_params` or `multipart` instead. +* Various doc fixes. + +## 6.0.0 - 2015-05-26 + +* See the UPGRADING.md document for more information. +* Added `multipart` and `form_params` request options. +* Added `synchronous` request option. +* Added the `on_headers` request option. +* Fixed `expect` handling. +* No longer adding default middlewares in the client ctor. These need to be + present on the provided handler in order to work. +* Requests are no longer initiated when sending async requests with the + CurlMultiHandler. This prevents unexpected recursion from requests completing + while ticking the cURL loop. +* Removed the semantics of setting `default` to `true`. This is no longer + required now that the cURL loop is not ticked for async requests. +* Added request and response logging middleware. +* No longer allowing self signed certificates when using the StreamHandler. +* Ensuring that `sink` is valid if saving to a file. +* Request exceptions now include a "handler context" which provides handler + specific contextual information. +* Added `GuzzleHttp\RequestOptions` to allow request options to be applied + using constants. +* `$maxHandles` has been removed from CurlMultiHandler. +* `MultipartPostBody` is now part of the `guzzlehttp/psr7` package. + +## 5.3.0 - 2015-05-19 + +* Mock now supports `save_to` +* Marked `AbstractRequestEvent::getTransaction()` as public. +* Fixed a bug in which multiple headers using different casing would overwrite + previous headers in the associative array. +* Added `Utils::getDefaultHandler()` +* Marked `GuzzleHttp\Client::getDefaultUserAgent` as deprecated. +* URL scheme is now always lowercased. + +## 6.0.0-beta.1 + +* Requires PHP >= 5.5 +* Updated to use PSR-7 + * Requires immutable messages, which basically means an event based system + owned by a request instance is no longer possible. + * Utilizing the [Guzzle PSR-7 package](https://github.com/guzzle/psr7). + * Removed the dependency on `guzzlehttp/streams`. These stream abstractions + are available in the `guzzlehttp/psr7` package under the `GuzzleHttp\Psr7` + namespace. +* Added middleware and handler system + * Replaced the Guzzle event and subscriber system with a middleware system. + * No longer depends on RingPHP, but rather places the HTTP handlers directly + in Guzzle, operating on PSR-7 messages. + * Retry logic is now encapsulated in `GuzzleHttp\Middleware::retry`, which + means the `guzzlehttp/retry-subscriber` is now obsolete. + * Mocking responses is now handled using `GuzzleHttp\Handler\MockHandler`. +* Asynchronous responses + * No longer supports the `future` request option to send an async request. + Instead, use one of the `*Async` methods of a client (e.g., `requestAsync`, + `getAsync`, etc.). + * Utilizing `GuzzleHttp\Promise` instead of React's promise library to avoid + recursion required by chaining and forwarding react promises. See + https://github.com/guzzle/promises + * Added `requestAsync` and `sendAsync` to send request asynchronously. + * Added magic methods for `getAsync()`, `postAsync()`, etc. to send requests + asynchronously. +* Request options + * POST and form updates + * Added the `form_fields` and `form_files` request options. + * Removed the `GuzzleHttp\Post` namespace. + * The `body` request option no longer accepts an array for POST requests. + * The `exceptions` request option has been deprecated in favor of the + `http_errors` request options. + * The `save_to` request option has been deprecated in favor of `sink` request + option. +* Clients no longer accept an array of URI template string and variables for + URI variables. You will need to expand URI templates before passing them + into a client constructor or request method. +* Client methods `get()`, `post()`, `put()`, `patch()`, `options()`, etc. are + now magic methods that will send synchronous requests. +* Replaced `Utils.php` with plain functions in `functions.php`. +* Removed `GuzzleHttp\Collection`. +* Removed `GuzzleHttp\BatchResults`. Batched pool results are now returned as + an array. +* Removed `GuzzleHttp\Query`. Query string handling is now handled using an + associative array passed into the `query` request option. The query string + is serialized using PHP's `http_build_query`. If you need more control, you + can pass the query string in as a string. +* `GuzzleHttp\QueryParser` has been replaced with the + `GuzzleHttp\Psr7\parse_query`. + +## 5.2.0 - 2015-01-27 + +* Added `AppliesHeadersInterface` to make applying headers to a request based + on the body more generic and not specific to `PostBodyInterface`. +* Reduced the number of stack frames needed to send requests. +* Nested futures are now resolved in the client rather than the RequestFsm +* Finishing state transitions is now handled in the RequestFsm rather than the + RingBridge. +* Added a guard in the Pool class to not use recursion for request retries. + +## 5.1.0 - 2014-12-19 + +* Pool class no longer uses recursion when a request is intercepted. +* The size of a Pool can now be dynamically adjusted using a callback. + See https://github.com/guzzle/guzzle/pull/943. +* Setting a request option to `null` when creating a request with a client will + ensure that the option is not set. This allows you to overwrite default + request options on a per-request basis. + See https://github.com/guzzle/guzzle/pull/937. +* Added the ability to limit which protocols are allowed for redirects by + specifying a `protocols` array in the `allow_redirects` request option. +* Nested futures due to retries are now resolved when waiting for synchronous + responses. See https://github.com/guzzle/guzzle/pull/947. +* `"0"` is now an allowed URI path. See + https://github.com/guzzle/guzzle/pull/935. +* `Query` no longer typehints on the `$query` argument in the constructor, + allowing for strings and arrays. +* Exceptions thrown in the `end` event are now correctly wrapped with Guzzle + specific exceptions if necessary. + +## 5.0.3 - 2014-11-03 + +This change updates query strings so that they are treated as un-encoded values +by default where the value represents an un-encoded value to send over the +wire. A Query object then encodes the value before sending over the wire. This +means that even value query string values (e.g., ":") are url encoded. This +makes the Query class match PHP's http_build_query function. However, if you +want to send requests over the wire using valid query string characters that do +not need to be encoded, then you can provide a string to Url::setQuery() and +pass true as the second argument to specify that the query string is a raw +string that should not be parsed or encoded (unless a call to getQuery() is +subsequently made, forcing the query-string to be converted into a Query +object). + +## 5.0.2 - 2014-10-30 + +* Added a trailing `\r\n` to multipart/form-data payloads. See + https://github.com/guzzle/guzzle/pull/871 +* Added a `GuzzleHttp\Pool::send()` convenience method to match the docs. +* Status codes are now returned as integers. See + https://github.com/guzzle/guzzle/issues/881 +* No longer overwriting an existing `application/x-www-form-urlencoded` header + when sending POST requests, allowing for customized headers. See + https://github.com/guzzle/guzzle/issues/877 +* Improved path URL serialization. + + * No longer double percent-encoding characters in the path or query string if + they are already encoded. + * Now properly encoding the supplied path to a URL object, instead of only + encoding ' ' and '?'. + * Note: This has been changed in 5.0.3 to now encode query string values by + default unless the `rawString` argument is provided when setting the query + string on a URL: Now allowing many more characters to be present in the + query string without being percent encoded. See http://tools.ietf.org/html/rfc3986#appendix-A + +## 5.0.1 - 2014-10-16 + +Bugfix release. + +* Fixed an issue where connection errors still returned response object in + error and end events event though the response is unusable. This has been + corrected so that a response is not returned in the `getResponse` method of + these events if the response did not complete. https://github.com/guzzle/guzzle/issues/867 +* Fixed an issue where transfer statistics were not being populated in the + RingBridge. https://github.com/guzzle/guzzle/issues/866 + +## 5.0.0 - 2014-10-12 + +Adding support for non-blocking responses and some minor API cleanup. + +### New Features + +* Added support for non-blocking responses based on `guzzlehttp/guzzle-ring`. +* Added a public API for creating a default HTTP adapter. +* Updated the redirect plugin to be non-blocking so that redirects are sent + concurrently. Other plugins like this can now be updated to be non-blocking. +* Added a "progress" event so that you can get upload and download progress + events. +* Added `GuzzleHttp\Pool` which implements FutureInterface and transfers + requests concurrently using a capped pool size as efficiently as possible. +* Added `hasListeners()` to EmitterInterface. +* Removed `GuzzleHttp\ClientInterface::sendAll` and marked + `GuzzleHttp\Client::sendAll` as deprecated (it's still there, just not the + recommended way). + +### Breaking changes + +The breaking changes in this release are relatively minor. The biggest thing to +look out for is that request and response objects no longer implement fluent +interfaces. + +* Removed the fluent interfaces (i.e., `return $this`) from requests, + responses, `GuzzleHttp\Collection`, `GuzzleHttp\Url`, + `GuzzleHttp\Query`, `GuzzleHttp\Post\PostBody`, and + `GuzzleHttp\Cookie\SetCookie`. This blog post provides a good outline of + why I did this: http://ocramius.github.io/blog/fluent-interfaces-are-evil/. + This also makes the Guzzle message interfaces compatible with the current + PSR-7 message proposal. +* Removed "functions.php", so that Guzzle is truly PSR-4 compliant. Except + for the HTTP request functions from function.php, these functions are now + implemented in `GuzzleHttp\Utils` using camelCase. `GuzzleHttp\json_decode` + moved to `GuzzleHttp\Utils::jsonDecode`. `GuzzleHttp\get_path` moved to + `GuzzleHttp\Utils::getPath`. `GuzzleHttp\set_path` moved to + `GuzzleHttp\Utils::setPath`. `GuzzleHttp\batch` should now be + `GuzzleHttp\Pool::batch`, which returns an `objectStorage`. Using functions.php + caused problems for many users: they aren't PSR-4 compliant, require an + explicit include, and needed an if-guard to ensure that the functions are not + declared multiple times. +* Rewrote adapter layer. + * Removing all classes from `GuzzleHttp\Adapter`, these are now + implemented as callables that are stored in `GuzzleHttp\Ring\Client`. + * Removed the concept of "parallel adapters". Sending requests serially or + concurrently is now handled using a single adapter. + * Moved `GuzzleHttp\Adapter\Transaction` to `GuzzleHttp\Transaction`. The + Transaction object now exposes the request, response, and client as public + properties. The getters and setters have been removed. +* Removed the "headers" event. This event was only useful for changing the + body a response once the headers of the response were known. You can implement + a similar behavior in a number of ways. One example might be to use a + FnStream that has access to the transaction being sent. For example, when the + first byte is written, you could check if the response headers match your + expectations, and if so, change the actual stream body that is being + written to. +* Removed the `asArray` parameter from + `GuzzleHttp\Message\MessageInterface::getHeader`. If you want to get a header + value as an array, then use the newly added `getHeaderAsArray()` method of + `MessageInterface`. This change makes the Guzzle interfaces compatible with + the PSR-7 interfaces. +* `GuzzleHttp\Message\MessageFactory` no longer allows subclasses to add + custom request options using double-dispatch (this was an implementation + detail). Instead, you should now provide an associative array to the + constructor which is a mapping of the request option name mapping to a + function that applies the option value to a request. +* Removed the concept of "throwImmediately" from exceptions and error events. + This control mechanism was used to stop a transfer of concurrent requests + from completing. This can now be handled by throwing the exception or by + cancelling a pool of requests or each outstanding future request individually. +* Updated to "GuzzleHttp\Streams" 3.0. + * `GuzzleHttp\Stream\StreamInterface::getContents()` no longer accepts a + `maxLen` parameter. This update makes the Guzzle streams project + compatible with the current PSR-7 proposal. + * `GuzzleHttp\Stream\Stream::__construct`, + `GuzzleHttp\Stream\Stream::factory`, and + `GuzzleHttp\Stream\Utils::create` no longer accept a size in the second + argument. They now accept an associative array of options, including the + "size" key and "metadata" key which can be used to provide custom metadata. + +## 4.2.2 - 2014-09-08 + +* Fixed a memory leak in the CurlAdapter when reusing cURL handles. +* No longer using `request_fulluri` in stream adapter proxies. +* Relative redirects are now based on the last response, not the first response. + +## 4.2.1 - 2014-08-19 + +* Ensuring that the StreamAdapter does not always add a Content-Type header +* Adding automated github releases with a phar and zip + +## 4.2.0 - 2014-08-17 + +* Now merging in default options using a case-insensitive comparison. + Closes https://github.com/guzzle/guzzle/issues/767 +* Added the ability to automatically decode `Content-Encoding` response bodies + using the `decode_content` request option. This is set to `true` by default + to decode the response body if it comes over the wire with a + `Content-Encoding`. Set this value to `false` to disable decoding the + response content, and pass a string to provide a request `Accept-Encoding` + header and turn on automatic response decoding. This feature now allows you + to pass an `Accept-Encoding` header in the headers of a request but still + disable automatic response decoding. + Closes https://github.com/guzzle/guzzle/issues/764 +* Added the ability to throw an exception immediately when transferring + requests in parallel. Closes https://github.com/guzzle/guzzle/issues/760 +* Updating guzzlehttp/streams dependency to ~2.1 +* No longer utilizing the now deprecated namespaced methods from the stream + package. + +## 4.1.8 - 2014-08-14 + +* Fixed an issue in the CurlFactory that caused setting the `stream=false` + request option to throw an exception. + See: https://github.com/guzzle/guzzle/issues/769 +* TransactionIterator now calls rewind on the inner iterator. + See: https://github.com/guzzle/guzzle/pull/765 +* You can now set the `Content-Type` header to `multipart/form-data` + when creating POST requests to force multipart bodies. + See https://github.com/guzzle/guzzle/issues/768 + +## 4.1.7 - 2014-08-07 + +* Fixed an error in the HistoryPlugin that caused the same request and response + to be logged multiple times when an HTTP protocol error occurs. +* Ensuring that cURL does not add a default Content-Type when no Content-Type + has been supplied by the user. This prevents the adapter layer from modifying + the request that is sent over the wire after any listeners may have already + put the request in a desired state (e.g., signed the request). +* Throwing an exception when you attempt to send requests that have the + "stream" set to true in parallel using the MultiAdapter. +* Only calling curl_multi_select when there are active cURL handles. This was + previously changed and caused performance problems on some systems due to PHP + always selecting until the maximum select timeout. +* Fixed a bug where multipart/form-data POST fields were not correctly + aggregated (e.g., values with "&"). + +## 4.1.6 - 2014-08-03 + +* Added helper methods to make it easier to represent messages as strings, + including getting the start line and getting headers as a string. + +## 4.1.5 - 2014-08-02 + +* Automatically retrying cURL "Connection died, retrying a fresh connect" + errors when possible. +* cURL implementation cleanup +* Allowing multiple event subscriber listeners to be registered per event by + passing an array of arrays of listener configuration. + +## 4.1.4 - 2014-07-22 + +* Fixed a bug that caused multi-part POST requests with more than one field to + serialize incorrectly. +* Paths can now be set to "0" +* `ResponseInterface::xml` now accepts a `libxml_options` option and added a + missing default argument that was required when parsing XML response bodies. +* A `save_to` stream is now created lazily, which means that files are not + created on disk unless a request succeeds. + +## 4.1.3 - 2014-07-15 + +* Various fixes to multipart/form-data POST uploads +* Wrapping function.php in an if-statement to ensure Guzzle can be used + globally and in a Composer install +* Fixed an issue with generating and merging in events to an event array +* POST headers are only applied before sending a request to allow you to change + the query aggregator used before uploading +* Added much more robust query string parsing +* Fixed various parsing and normalization issues with URLs +* Fixing an issue where multi-valued headers were not being utilized correctly + in the StreamAdapter + +## 4.1.2 - 2014-06-18 + +* Added support for sending payloads with GET requests + +## 4.1.1 - 2014-06-08 + +* Fixed an issue related to using custom message factory options in subclasses +* Fixed an issue with nested form fields in a multi-part POST +* Fixed an issue with using the `json` request option for POST requests +* Added `ToArrayInterface` to `GuzzleHttp\Cookie\CookieJar` + +## 4.1.0 - 2014-05-27 + +* Added a `json` request option to easily serialize JSON payloads. +* Added a `GuzzleHttp\json_decode()` wrapper to safely parse JSON. +* Added `setPort()` and `getPort()` to `GuzzleHttp\Message\RequestInterface`. +* Added the ability to provide an emitter to a client in the client constructor. +* Added the ability to persist a cookie session using $_SESSION. +* Added a trait that can be used to add event listeners to an iterator. +* Removed request method constants from RequestInterface. +* Fixed warning when invalid request start-lines are received. +* Updated MessageFactory to work with custom request option methods. +* Updated cacert bundle to latest build. + +4.0.2 (2014-04-16) +------------------ + +* Proxy requests using the StreamAdapter now properly use request_fulluri (#632) +* Added the ability to set scalars as POST fields (#628) + +## 4.0.1 - 2014-04-04 + +* The HTTP status code of a response is now set as the exception code of + RequestException objects. +* 303 redirects will now correctly switch from POST to GET requests. +* The default parallel adapter of a client now correctly uses the MultiAdapter. +* HasDataTrait now initializes the internal data array as an empty array so + that the toArray() method always returns an array. + +## 4.0.0 - 2014-03-29 + +* For more information on the 4.0 transition, see: + http://mtdowling.com/blog/2014/03/15/guzzle-4-rc/ +* For information on changes and upgrading, see: + https://github.com/guzzle/guzzle/blob/master/UPGRADING.md#3x-to-40 +* Added `GuzzleHttp\batch()` as a convenience function for sending requests in + parallel without needing to write asynchronous code. +* Restructured how events are added to `GuzzleHttp\ClientInterface::sendAll()`. + You can now pass a callable or an array of associative arrays where each + associative array contains the "fn", "priority", and "once" keys. + +## 4.0.0.rc-2 - 2014-03-25 + +* Removed `getConfig()` and `setConfig()` from clients to avoid confusion + around whether things like base_url, message_factory, etc. should be able to + be retrieved or modified. +* Added `getDefaultOption()` and `setDefaultOption()` to ClientInterface +* functions.php functions were renamed using snake_case to match PHP idioms +* Added support for `HTTP_PROXY`, `HTTPS_PROXY`, and + `GUZZLE_CURL_SELECT_TIMEOUT` environment variables +* Added the ability to specify custom `sendAll()` event priorities +* Added the ability to specify custom stream context options to the stream + adapter. +* Added a functions.php function for `get_path()` and `set_path()` +* CurlAdapter and MultiAdapter now use a callable to generate curl resources +* MockAdapter now properly reads a body and emits a `headers` event +* Updated Url class to check if a scheme and host are set before adding ":" + and "//". This allows empty Url (e.g., "") to be serialized as "". +* Parsing invalid XML no longer emits warnings +* Curl classes now properly throw AdapterExceptions +* Various performance optimizations +* Streams are created with the faster `Stream\create()` function +* Marked deprecation_proxy() as internal +* Test server is now a collection of static methods on a class + +## 4.0.0-rc.1 - 2014-03-15 + +* See https://github.com/guzzle/guzzle/blob/master/UPGRADING.md#3x-to-40 + +## 3.8.1 - 2014-01-28 + +* Bug: Always using GET requests when redirecting from a 303 response +* Bug: CURLOPT_SSL_VERIFYHOST is now correctly set to false when setting `$certificateAuthority` to false in + `Guzzle\Http\ClientInterface::setSslVerification()` +* Bug: RedirectPlugin now uses strict RFC 3986 compliance when combining a base URL with a relative URL +* Bug: The body of a request can now be set to `"0"` +* Sending PHP stream requests no longer forces `HTTP/1.0` +* Adding more information to ExceptionCollection exceptions so that users have more context, including a stack trace of + each sub-exception +* Updated the `$ref` attribute in service descriptions to merge over any existing parameters of a schema (rather than + clobbering everything). +* Merging URLs will now use the query string object from the relative URL (thus allowing custom query aggregators) +* Query strings are now parsed in a way that they do no convert empty keys with no value to have a dangling `=`. + For example `foo&bar=baz` is now correctly parsed and recognized as `foo&bar=baz` rather than `foo=&bar=baz`. +* Now properly escaping the regular expression delimiter when matching Cookie domains. +* Network access is now disabled when loading XML documents + +## 3.8.0 - 2013-12-05 + +* Added the ability to define a POST name for a file +* JSON response parsing now properly walks additionalProperties +* cURL error code 18 is now retried automatically in the BackoffPlugin +* Fixed a cURL error when URLs contain fragments +* Fixed an issue in the BackoffPlugin retry event where it was trying to access all exceptions as if they were + CurlExceptions +* CURLOPT_PROGRESS function fix for PHP 5.5 (69fcc1e) +* Added the ability for Guzzle to work with older versions of cURL that do not support `CURLOPT_TIMEOUT_MS` +* Fixed a bug that was encountered when parsing empty header parameters +* UriTemplate now has a `setRegex()` method to match the docs +* The `debug` request parameter now checks if it is truthy rather than if it exists +* Setting the `debug` request parameter to true shows verbose cURL output instead of using the LogPlugin +* Added the ability to combine URLs using strict RFC 3986 compliance +* Command objects can now return the validation errors encountered by the command +* Various fixes to cache revalidation (#437 and 29797e5) +* Various fixes to the AsyncPlugin +* Cleaned up build scripts + +## 3.7.4 - 2013-10-02 + +* Bug fix: 0 is now an allowed value in a description parameter that has a default value (#430) +* Bug fix: SchemaFormatter now returns an integer when formatting to a Unix timestamp + (see https://github.com/aws/aws-sdk-php/issues/147) +* Bug fix: Cleaned up and fixed URL dot segment removal to properly resolve internal dots +* Minimum PHP version is now properly specified as 5.3.3 (up from 5.3.2) (#420) +* Updated the bundled cacert.pem (#419) +* OauthPlugin now supports adding authentication to headers or query string (#425) + +## 3.7.3 - 2013-09-08 + +* Added the ability to get the exception associated with a request/command when using `MultiTransferException` and + `CommandTransferException`. +* Setting `additionalParameters` of a response to false is now honored when parsing responses with a service description +* Schemas are only injected into response models when explicitly configured. +* No longer guessing Content-Type based on the path of a request. Content-Type is now only guessed based on the path of + an EntityBody. +* Bug fix: ChunkedIterator can now properly chunk a \Traversable as well as an \Iterator. +* Bug fix: FilterIterator now relies on `\Iterator` instead of `\Traversable`. +* Bug fix: Gracefully handling malformed responses in RequestMediator::writeResponseBody() +* Bug fix: Replaced call to canCache with canCacheRequest in the CallbackCanCacheStrategy of the CachePlugin +* Bug fix: Visiting XML attributes first before visiting XML children when serializing requests +* Bug fix: Properly parsing headers that contain commas contained in quotes +* Bug fix: mimetype guessing based on a filename is now case-insensitive + +## 3.7.2 - 2013-08-02 + +* Bug fix: Properly URL encoding paths when using the PHP-only version of the UriTemplate expander + See https://github.com/guzzle/guzzle/issues/371 +* Bug fix: Cookie domains are now matched correctly according to RFC 6265 + See https://github.com/guzzle/guzzle/issues/377 +* Bug fix: GET parameters are now used when calculating an OAuth signature +* Bug fix: Fixed an issue with cache revalidation where the If-None-Match header was being double quoted +* `Guzzle\Common\AbstractHasDispatcher::dispatch()` now returns the event that was dispatched +* `Guzzle\Http\QueryString::factory()` now guesses the most appropriate query aggregator to used based on the input. + See https://github.com/guzzle/guzzle/issues/379 +* Added a way to add custom domain objects to service description parsing using the `operation.parse_class` event. See + https://github.com/guzzle/guzzle/pull/380 +* cURL multi cleanup and optimizations + +## 3.7.1 - 2013-07-05 + +* Bug fix: Setting default options on a client now works +* Bug fix: Setting options on HEAD requests now works. See #352 +* Bug fix: Moving stream factory before send event to before building the stream. See #353 +* Bug fix: Cookies no longer match on IP addresses per RFC 6265 +* Bug fix: Correctly parsing header parameters that are in `<>` and quotes +* Added `cert` and `ssl_key` as request options +* `Host` header can now diverge from the host part of a URL if the header is set manually +* `Guzzle\Service\Command\LocationVisitor\Request\XmlVisitor` was rewritten to change from using SimpleXML to XMLWriter +* OAuth parameters are only added via the plugin if they aren't already set +* Exceptions are now thrown when a URL cannot be parsed +* Returning `false` if `Guzzle\Http\EntityBody::getContentMd5()` fails +* Not setting a `Content-MD5` on a command if calculating the Content-MD5 fails via the CommandContentMd5Plugin + +## 3.7.0 - 2013-06-10 + +* See UPGRADING.md for more information on how to upgrade. +* Requests now support the ability to specify an array of $options when creating a request to more easily modify a + request. You can pass a 'request.options' configuration setting to a client to apply default request options to + every request created by a client (e.g. default query string variables, headers, curl options, etc.). +* Added a static facade class that allows you to use Guzzle with static methods and mount the class to `\Guzzle`. + See `Guzzle\Http\StaticClient::mount`. +* Added `command.request_options` to `Guzzle\Service\Command\AbstractCommand` to pass request options to requests + created by a command (e.g. custom headers, query string variables, timeout settings, etc.). +* Stream size in `Guzzle\Stream\PhpStreamRequestFactory` will now be set if Content-Length is returned in the + headers of a response +* Added `Guzzle\Common\Collection::setPath($path, $value)` to set a value into an array using a nested key + (e.g. `$collection->setPath('foo/baz/bar', 'test'); echo $collection['foo']['bar']['bar'];`) +* ServiceBuilders now support storing and retrieving arbitrary data +* CachePlugin can now purge all resources for a given URI +* CachePlugin can automatically purge matching cached items when a non-idempotent request is sent to a resource +* CachePlugin now uses the Vary header to determine if a resource is a cache hit +* `Guzzle\Http\Message\Response` now implements `\Serializable` +* Added `Guzzle\Cache\CacheAdapterFactory::fromCache()` to more easily create cache adapters +* `Guzzle\Service\ClientInterface::execute()` now accepts an array, single command, or Traversable +* Fixed a bug in `Guzzle\Http\Message\Header\Link::addLink()` +* Better handling of calculating the size of a stream in `Guzzle\Stream\Stream` using fstat() and caching the size +* `Guzzle\Common\Exception\ExceptionCollection` now creates a more readable exception message +* Fixing BC break: Added back the MonologLogAdapter implementation rather than extending from PsrLog so that older + Symfony users can still use the old version of Monolog. +* Fixing BC break: Added the implementation back in for `Guzzle\Http\Message\AbstractMessage::getTokenizedHeader()`. + Now triggering an E_USER_DEPRECATED warning when used. Use `$message->getHeader()->parseParams()`. +* Several performance improvements to `Guzzle\Common\Collection` +* Added an `$options` argument to the end of the following methods of `Guzzle\Http\ClientInterface`: + createRequest, head, delete, put, patch, post, options, prepareRequest +* Added an `$options` argument to the end of `Guzzle\Http\Message\Request\RequestFactoryInterface::createRequest()` +* Added an `applyOptions()` method to `Guzzle\Http\Message\Request\RequestFactoryInterface` +* Changed `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $body = null)` to + `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $options = array())`. You can still pass in a + resource, string, or EntityBody into the $options parameter to specify the download location of the response. +* Changed `Guzzle\Common\Collection::__construct($data)` to no longer accepts a null value for `$data` but a + default `array()` +* Added `Guzzle\Stream\StreamInterface::isRepeatable` +* Removed `Guzzle\Http\ClientInterface::setDefaultHeaders(). Use + $client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. or + $client->getConfig()->setPath('request.options/headers', array('header_name' => 'value'))`. +* Removed `Guzzle\Http\ClientInterface::getDefaultHeaders(). Use $client->getConfig()->getPath('request.options/headers')`. +* Removed `Guzzle\Http\ClientInterface::expandTemplate()` +* Removed `Guzzle\Http\ClientInterface::setRequestFactory()` +* Removed `Guzzle\Http\ClientInterface::getCurlMulti()` +* Removed `Guzzle\Http\Message\RequestInterface::canCache` +* Removed `Guzzle\Http\Message\RequestInterface::setIsRedirect` +* Removed `Guzzle\Http\Message\RequestInterface::isRedirect` +* Made `Guzzle\Http\Client::expandTemplate` and `getUriTemplate` protected methods. +* You can now enable E_USER_DEPRECATED warnings to see if you are using a deprecated method by setting + `Guzzle\Common\Version::$emitWarnings` to true. +* Marked `Guzzle\Http\Message\Request::isResponseBodyRepeatable()` as deprecated. Use + `$request->getResponseBody()->isRepeatable()` instead. +* Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use + `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. +* Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use + `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. +* Marked `Guzzle\Http\Message\Request::setIsRedirect()` as deprecated. Use the HistoryPlugin instead. +* Marked `Guzzle\Http\Message\Request::isRedirect()` as deprecated. Use the HistoryPlugin instead. +* Marked `Guzzle\Cache\CacheAdapterFactory::factory()` as deprecated +* Marked 'command.headers', 'command.response_body' and 'command.on_complete' as deprecated for AbstractCommand. + These will work through Guzzle 4.0 +* Marked 'request.params' for `Guzzle\Http\Client` as deprecated. Use [request.options][params]. +* Marked `Guzzle\Service\Client::enableMagicMethods()` as deprecated. Magic methods can no longer be disabled on a Guzzle\Service\Client. +* Marked `Guzzle\Service\Client::getDefaultHeaders()` as deprecated. Use $client->getConfig()->getPath('request.options/headers')`. +* Marked `Guzzle\Service\Client::setDefaultHeaders()` as deprecated. Use $client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. +* Marked `Guzzle\Parser\Url\UrlParser` as deprecated. Just use PHP's `parse_url()` and percent encode your UTF-8. +* Marked `Guzzle\Common\Collection::inject()` as deprecated. +* Marked `Guzzle\Plugin\CurlAuth\CurlAuthPlugin` as deprecated. Use `$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest');` +* CacheKeyProviderInterface and DefaultCacheKeyProvider are no longer used. All of this logic is handled in a + CacheStorageInterface. These two objects and interface will be removed in a future version. +* Always setting X-cache headers on cached responses +* Default cache TTLs are now handled by the CacheStorageInterface of a CachePlugin +* `CacheStorageInterface::cache($key, Response $response, $ttl = null)` has changed to `cache(RequestInterface + $request, Response $response);` +* `CacheStorageInterface::fetch($key)` has changed to `fetch(RequestInterface $request);` +* `CacheStorageInterface::delete($key)` has changed to `delete(RequestInterface $request);` +* Added `CacheStorageInterface::purge($url)` +* `DefaultRevalidation::__construct(CacheKeyProviderInterface $cacheKey, CacheStorageInterface $cache, CachePlugin + $plugin)` has changed to `DefaultRevalidation::__construct(CacheStorageInterface $cache, + CanCacheStrategyInterface $canCache = null)` +* Added `RevalidationInterface::shouldRevalidate(RequestInterface $request, Response $response)` + +## 3.6.0 - 2013-05-29 + +* ServiceDescription now implements ToArrayInterface +* Added command.hidden_params to blacklist certain headers from being treated as additionalParameters +* Guzzle can now correctly parse incomplete URLs +* Mixed casing of headers are now forced to be a single consistent casing across all values for that header. +* Messages internally use a HeaderCollection object to delegate handling case-insensitive header resolution +* Removed the whole changedHeader() function system of messages because all header changes now go through addHeader(). +* Specific header implementations can be created for complex headers. When a message creates a header, it uses a + HeaderFactory which can map specific headers to specific header classes. There is now a Link header and + CacheControl header implementation. +* Removed from interface: Guzzle\Http\ClientInterface::setUriTemplate +* Removed from interface: Guzzle\Http\ClientInterface::setCurlMulti() +* Removed Guzzle\Http\Message\Request::receivedRequestHeader() and implemented this functionality in + Guzzle\Http\Curl\RequestMediator +* Removed the optional $asString parameter from MessageInterface::getHeader(). Just cast the header to a string. +* Removed the optional $tryChunkedTransfer option from Guzzle\Http\Message\EntityEnclosingRequestInterface +* Removed the $asObjects argument from Guzzle\Http\Message\MessageInterface::getHeaders() +* Removed Guzzle\Parser\ParserRegister::get(). Use getParser() +* Removed Guzzle\Parser\ParserRegister::set(). Use registerParser(). +* All response header helper functions return a string rather than mixing Header objects and strings inconsistently +* Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc. are managed by Guzzle + directly via interfaces +* Removed the injecting of a request object onto a response object. The methods to get and set a request still exist + but are a no-op until removed. +* Most classes that used to require a `Guzzle\Service\Command\CommandInterface` typehint now request a + `Guzzle\Service\Command\ArrayCommandInterface`. +* Added `Guzzle\Http\Message\RequestInterface::startResponse()` to the RequestInterface to handle injecting a response + on a request while the request is still being transferred +* The ability to case-insensitively search for header values +* Guzzle\Http\Message\Header::hasExactHeader +* Guzzle\Http\Message\Header::raw. Use getAll() +* Deprecated cache control specific methods on Guzzle\Http\Message\AbstractMessage. Use the CacheControl header object + instead. +* `Guzzle\Service\Command\CommandInterface` now extends from ToArrayInterface and ArrayAccess +* Added the ability to cast Model objects to a string to view debug information. + +## 3.5.0 - 2013-05-13 + +* Bug: Fixed a regression so that request responses are parsed only once per oncomplete event rather than multiple times +* Bug: Better cleanup of one-time events across the board (when an event is meant to fire once, it will now remove + itself from the EventDispatcher) +* Bug: `Guzzle\Log\MessageFormatter` now properly writes "total_time" and "connect_time" values +* Bug: Cloning an EntityEnclosingRequest now clones the EntityBody too +* Bug: Fixed an undefined index error when parsing nested JSON responses with a sentAs parameter that reference a + non-existent key +* Bug: All __call() method arguments are now required (helps with mocking frameworks) +* Deprecating Response::getRequest() and now using a shallow clone of a request object to remove a circular reference + to help with refcount based garbage collection of resources created by sending a request +* Deprecating ZF1 cache and log adapters. These will be removed in the next major version. +* Deprecating `Response::getPreviousResponse()` (method signature still exists, but it's deprecated). Use the + HistoryPlugin for a history. +* Added a `responseBody` alias for the `response_body` location +* Refactored internals to no longer rely on Response::getRequest() +* HistoryPlugin can now be cast to a string +* HistoryPlugin now logs transactions rather than requests and responses to more accurately keep track of the requests + and responses that are sent over the wire +* Added `getEffectiveUrl()` and `getRedirectCount()` to Response objects + +## 3.4.3 - 2013-04-30 + +* Bug fix: Fixing bug introduced in 3.4.2 where redirect responses are duplicated on the final redirected response +* Added a check to re-extract the temp cacert bundle from the phar before sending each request + +## 3.4.2 - 2013-04-29 + +* Bug fix: Stream objects now work correctly with "a" and "a+" modes +* Bug fix: Removing `Transfer-Encoding: chunked` header when a Content-Length is present +* Bug fix: AsyncPlugin no longer forces HEAD requests +* Bug fix: DateTime timezones are now properly handled when using the service description schema formatter +* Bug fix: CachePlugin now properly handles stale-if-error directives when a request to the origin server fails +* Setting a response on a request will write to the custom request body from the response body if one is specified +* LogPlugin now writes to php://output when STDERR is undefined +* Added the ability to set multiple POST files for the same key in a single call +* application/x-www-form-urlencoded POSTs now use the utf-8 charset by default +* Added the ability to queue CurlExceptions to the MockPlugin +* Cleaned up how manual responses are queued on requests (removed "queued_response" and now using request.before_send) +* Configuration loading now allows remote files + +## 3.4.1 - 2013-04-16 + +* Large refactoring to how CurlMulti handles work. There is now a proxy that sits in front of a pool of CurlMulti + handles. This greatly simplifies the implementation, fixes a couple bugs, and provides a small performance boost. +* Exceptions are now properly grouped when sending requests in parallel +* Redirects are now properly aggregated when a multi transaction fails +* Redirects now set the response on the original object even in the event of a failure +* Bug fix: Model names are now properly set even when using $refs +* Added support for PHP 5.5's CurlFile to prevent warnings with the deprecated @ syntax +* Added support for oauth_callback in OAuth signatures +* Added support for oauth_verifier in OAuth signatures +* Added support to attempt to retrieve a command first literally, then ucfirst, the with inflection + +## 3.4.0 - 2013-04-11 + +* Bug fix: URLs are now resolved correctly based on http://tools.ietf.org/html/rfc3986#section-5.2. #289 +* Bug fix: Absolute URLs with a path in a service description will now properly override the base URL. #289 +* Bug fix: Parsing a query string with a single PHP array value will now result in an array. #263 +* Bug fix: Better normalization of the User-Agent header to prevent duplicate headers. #264. +* Bug fix: Added `number` type to service descriptions. +* Bug fix: empty parameters are removed from an OAuth signature +* Bug fix: Revalidating a cache entry prefers the Last-Modified over the Date header +* Bug fix: Fixed "array to string" error when validating a union of types in a service description +* Bug fix: Removed code that attempted to determine the size of a stream when data is written to the stream +* Bug fix: Not including an `oauth_token` if the value is null in the OauthPlugin. +* Bug fix: Now correctly aggregating successful requests and failed requests in CurlMulti when a redirect occurs. +* The new default CURLOPT_TIMEOUT setting has been increased to 150 seconds so that Guzzle works on poor connections. +* Added a feature to EntityEnclosingRequest::setBody() that will automatically set the Content-Type of the request if + the Content-Type can be determined based on the entity body or the path of the request. +* Added the ability to overwrite configuration settings in a client when grabbing a throwaway client from a builder. +* Added support for a PSR-3 LogAdapter. +* Added a `command.after_prepare` event +* Added `oauth_callback` parameter to the OauthPlugin +* Added the ability to create a custom stream class when using a stream factory +* Added a CachingEntityBody decorator +* Added support for `additionalParameters` in service descriptions to define how custom parameters are serialized. +* The bundled SSL certificate is now provided in the phar file and extracted when running Guzzle from a phar. +* You can now send any EntityEnclosingRequest with POST fields or POST files and cURL will handle creating bodies +* POST requests using a custom entity body are now treated exactly like PUT requests but with a custom cURL method. This + means that the redirect behavior of POST requests with custom bodies will not be the same as POST requests that use + POST fields or files (the latter is only used when emulating a form POST in the browser). +* Lots of cleanup to CurlHandle::factory and RequestFactory::createRequest + +## 3.3.1 - 2013-03-10 + +* Added the ability to create PHP streaming responses from HTTP requests +* Bug fix: Running any filters when parsing response headers with service descriptions +* Bug fix: OauthPlugin fixes to allow for multi-dimensional array signing, and sorting parameters before signing +* Bug fix: Removed the adding of default empty arrays and false Booleans to responses in order to be consistent across + response location visitors. +* Bug fix: Removed the possibility of creating configuration files with circular dependencies +* RequestFactory::create() now uses the key of a POST file when setting the POST file name +* Added xmlAllowEmpty to serialize an XML body even if no XML specific parameters are set + +## 3.3.0 - 2013-03-03 + +* A large number of performance optimizations have been made +* Bug fix: Added 'wb' as a valid write mode for streams +* Bug fix: `Guzzle\Http\Message\Response::json()` now allows scalar values to be returned +* Bug fix: Fixed bug in `Guzzle\Http\Message\Response` where wrapping quotes were stripped from `getEtag()` +* BC: Removed `Guzzle\Http\Utils` class +* BC: Setting a service description on a client will no longer modify the client's command factories. +* BC: Emitting IO events from a RequestMediator is now a parameter that must be set in a request's curl options using + the 'emit_io' key. This was previously set under a request's parameters using 'curl.emit_io' +* BC: `Guzzle\Stream\Stream::getWrapper()` and `Guzzle\Stream\Stream::getSteamType()` are no longer converted to + lowercase +* Operation parameter objects are now lazy loaded internally +* Added ErrorResponsePlugin that can throw errors for responses defined in service description operations' errorResponses +* Added support for instantiating responseType=class responseClass classes. Classes must implement + `Guzzle\Service\Command\ResponseClassInterface` +* Added support for additionalProperties for top-level parameters in responseType=model responseClasses. These + additional properties also support locations and can be used to parse JSON responses where the outermost part of the + JSON is an array +* Added support for nested renaming of JSON models (rename sentAs to name) +* CachePlugin + * Added support for stale-if-error so that the CachePlugin can now serve stale content from the cache on error + * Debug headers can now added to cached response in the CachePlugin + +## 3.2.0 - 2013-02-14 + +* CurlMulti is no longer reused globally. A new multi object is created per-client. This helps to isolate clients. +* URLs with no path no longer contain a "/" by default +* Guzzle\Http\QueryString does no longer manages the leading "?". This is now handled in Guzzle\Http\Url. +* BadResponseException no longer includes the full request and response message +* Adding setData() to Guzzle\Service\Description\ServiceDescriptionInterface +* Adding getResponseBody() to Guzzle\Http\Message\RequestInterface +* Various updates to classes to use ServiceDescriptionInterface type hints rather than ServiceDescription +* Header values can now be normalized into distinct values when multiple headers are combined with a comma separated list +* xmlEncoding can now be customized for the XML declaration of a XML service description operation +* Guzzle\Http\QueryString now uses Guzzle\Http\QueryAggregator\QueryAggregatorInterface objects to add custom value + aggregation and no longer uses callbacks +* The URL encoding implementation of Guzzle\Http\QueryString can now be customized +* Bug fix: Filters were not always invoked for array service description parameters +* Bug fix: Redirects now use a target response body rather than a temporary response body +* Bug fix: The default exponential backoff BackoffPlugin was not giving when the request threshold was exceeded +* Bug fix: Guzzle now takes the first found value when grabbing Cache-Control directives + +## 3.1.2 - 2013-01-27 + +* Refactored how operation responses are parsed. Visitors now include a before() method responsible for parsing the + response body. For example, the XmlVisitor now parses the XML response into an array in the before() method. +* Fixed an issue where cURL would not automatically decompress responses when the Accept-Encoding header was sent +* CURLOPT_SSL_VERIFYHOST is never set to 1 because it is deprecated (see 5e0ff2ef20f839e19d1eeb298f90ba3598784444) +* Fixed a bug where redirect responses were not chained correctly using getPreviousResponse() +* Setting default headers on a client after setting the user-agent will not erase the user-agent setting + +## 3.1.1 - 2013-01-20 + +* Adding wildcard support to Guzzle\Common\Collection::getPath() +* Adding alias support to ServiceBuilder configs +* Adding Guzzle\Service\Resource\CompositeResourceIteratorFactory and cleaning up factory interface + +## 3.1.0 - 2013-01-12 + +* BC: CurlException now extends from RequestException rather than BadResponseException +* BC: Renamed Guzzle\Plugin\Cache\CanCacheStrategyInterface::canCache() to canCacheRequest() and added CanCacheResponse() +* Added getData to ServiceDescriptionInterface +* Added context array to RequestInterface::setState() +* Bug: Removing hard dependency on the BackoffPlugin from Guzzle\Http +* Bug: Adding required content-type when JSON request visitor adds JSON to a command +* Bug: Fixing the serialization of a service description with custom data +* Made it easier to deal with exceptions thrown when transferring commands or requests in parallel by providing + an array of successful and failed responses +* Moved getPath from Guzzle\Service\Resource\Model to Guzzle\Common\Collection +* Added Guzzle\Http\IoEmittingEntityBody +* Moved command filtration from validators to location visitors +* Added `extends` attributes to service description parameters +* Added getModels to ServiceDescriptionInterface + +## 3.0.7 - 2012-12-19 + +* Fixing phar detection when forcing a cacert to system if null or true +* Allowing filename to be passed to `Guzzle\Http\Message\Request::setResponseBody()` +* Cleaning up `Guzzle\Common\Collection::inject` method +* Adding a response_body location to service descriptions + +## 3.0.6 - 2012-12-09 + +* CurlMulti performance improvements +* Adding setErrorResponses() to Operation +* composer.json tweaks + +## 3.0.5 - 2012-11-18 + +* Bug: Fixing an infinite recursion bug caused from revalidating with the CachePlugin +* Bug: Response body can now be a string containing "0" +* Bug: Using Guzzle inside of a phar uses system by default but now allows for a custom cacert +* Bug: QueryString::fromString now properly parses query string parameters that contain equal signs +* Added support for XML attributes in service description responses +* DefaultRequestSerializer now supports array URI parameter values for URI template expansion +* Added better mimetype guessing to requests and post files + +## 3.0.4 - 2012-11-11 + +* Bug: Fixed a bug when adding multiple cookies to a request to use the correct glue value +* Bug: Cookies can now be added that have a name, domain, or value set to "0" +* Bug: Using the system cacert bundle when using the Phar +* Added json and xml methods to Response to make it easier to parse JSON and XML response data into data structures +* Enhanced cookie jar de-duplication +* Added the ability to enable strict cookie jars that throw exceptions when invalid cookies are added +* Added setStream to StreamInterface to actually make it possible to implement custom rewind behavior for entity bodies +* Added the ability to create any sort of hash for a stream rather than just an MD5 hash + +## 3.0.3 - 2012-11-04 + +* Implementing redirects in PHP rather than cURL +* Added PECL URI template extension and using as default parser if available +* Bug: Fixed Content-Length parsing of Response factory +* Adding rewind() method to entity bodies and streams. Allows for custom rewinding of non-repeatable streams. +* Adding ToArrayInterface throughout library +* Fixing OauthPlugin to create unique nonce values per request + +## 3.0.2 - 2012-10-25 + +* Magic methods are enabled by default on clients +* Magic methods return the result of a command +* Service clients no longer require a base_url option in the factory +* Bug: Fixed an issue with URI templates where null template variables were being expanded + +## 3.0.1 - 2012-10-22 + +* Models can now be used like regular collection objects by calling filter, map, etc. +* Models no longer require a Parameter structure or initial data in the constructor +* Added a custom AppendIterator to get around a PHP bug with the `\AppendIterator` + +## 3.0.0 - 2012-10-15 + +* Rewrote service description format to be based on Swagger + * Now based on JSON schema + * Added nested input structures and nested response models + * Support for JSON and XML input and output models + * Renamed `commands` to `operations` + * Removed dot class notation + * Removed custom types +* Broke the project into smaller top-level namespaces to be more component friendly +* Removed support for XML configs and descriptions. Use arrays or JSON files. +* Removed the Validation component and Inspector +* Moved all cookie code to Guzzle\Plugin\Cookie +* Magic methods on a Guzzle\Service\Client now return the command un-executed. +* Calling getResult() or getResponse() on a command will lazily execute the command if needed. +* Now shipping with cURL's CA certs and using it by default +* Added previousResponse() method to response objects +* No longer sending Accept and Accept-Encoding headers on every request +* Only sending an Expect header by default when a payload is greater than 1MB +* Added/moved client options: + * curl.blacklist to curl.option.blacklist + * Added ssl.certificate_authority +* Added a Guzzle\Iterator component +* Moved plugins from Guzzle\Http\Plugin to Guzzle\Plugin +* Added a more robust backoff retry strategy (replaced the ExponentialBackoffPlugin) +* Added a more robust caching plugin +* Added setBody to response objects +* Updating LogPlugin to use a more flexible MessageFormatter +* Added a completely revamped build process +* Cleaning up Collection class and removing default values from the get method +* Fixed ZF2 cache adapters + +## 2.8.8 - 2012-10-15 + +* Bug: Fixed a cookie issue that caused dot prefixed domains to not match where popular browsers did + +## 2.8.7 - 2012-09-30 + +* Bug: Fixed config file aliases for JSON includes +* Bug: Fixed cookie bug on a request object by using CookieParser to parse cookies on requests +* Bug: Removing the path to a file when sending a Content-Disposition header on a POST upload +* Bug: Hardening request and response parsing to account for missing parts +* Bug: Fixed PEAR packaging +* Bug: Fixed Request::getInfo +* Bug: Fixed cases where CURLM_CALL_MULTI_PERFORM return codes were causing curl transactions to fail +* Adding the ability for the namespace Iterator factory to look in multiple directories +* Added more getters/setters/removers from service descriptions +* Added the ability to remove POST fields from OAuth signatures +* OAuth plugin now supports 2-legged OAuth + +## 2.8.6 - 2012-09-05 + +* Added the ability to modify and build service descriptions +* Added the use of visitors to apply parameters to locations in service descriptions using the dynamic command +* Added a `json` parameter location +* Now allowing dot notation for classes in the CacheAdapterFactory +* Using the union of two arrays rather than an array_merge when extending service builder services and service params +* Ensuring that a service is a string before doing strpos() checks on it when substituting services for references + in service builder config files. +* Services defined in two different config files that include one another will by default replace the previously + defined service, but you can now create services that extend themselves and merge their settings over the previous +* The JsonLoader now supports aliasing filenames with different filenames. This allows you to alias something like + '_default' with a default JSON configuration file. + +## 2.8.5 - 2012-08-29 + +* Bug: Suppressed empty arrays from URI templates +* Bug: Added the missing $options argument from ServiceDescription::factory to enable caching +* Added support for HTTP responses that do not contain a reason phrase in the start-line +* AbstractCommand commands are now invokable +* Added a way to get the data used when signing an Oauth request before a request is sent + +## 2.8.4 - 2012-08-15 + +* Bug: Custom delay time calculations are no longer ignored in the ExponentialBackoffPlugin +* Added the ability to transfer entity bodies as a string rather than streamed. This gets around curl error 65. Set `body_as_string` in a request's curl options to enable. +* Added a StreamInterface, EntityBodyInterface, and added ftell() to Guzzle\Common\Stream +* Added an AbstractEntityBodyDecorator and a ReadLimitEntityBody decorator to transfer only a subset of a decorated stream +* Stream and EntityBody objects will now return the file position to the previous position after a read required operation (e.g. getContentMd5()) +* Added additional response status codes +* Removed SSL information from the default User-Agent header +* DELETE requests can now send an entity body +* Added an EventDispatcher to the ExponentialBackoffPlugin and added an ExponentialBackoffLogger to log backoff retries +* Added the ability of the MockPlugin to consume mocked request bodies +* LogPlugin now exposes request and response objects in the extras array + +## 2.8.3 - 2012-07-30 + +* Bug: Fixed a case where empty POST requests were sent as GET requests +* Bug: Fixed a bug in ExponentialBackoffPlugin that caused fatal errors when retrying an EntityEnclosingRequest that does not have a body +* Bug: Setting the response body of a request to null after completing a request, not when setting the state of a request to new +* Added multiple inheritance to service description commands +* Added an ApiCommandInterface and added `getParamNames()` and `hasParam()` +* Removed the default 2mb size cutoff from the Md5ValidatorPlugin so that it now defaults to validating everything +* Changed CurlMulti::perform to pass a smaller timeout to CurlMulti::executeHandles + +## 2.8.2 - 2012-07-24 + +* Bug: Query string values set to 0 are no longer dropped from the query string +* Bug: A Collection object is no longer created each time a call is made to `Guzzle\Service\Command\AbstractCommand::getRequestHeaders()` +* Bug: `+` is now treated as an encoded space when parsing query strings +* QueryString and Collection performance improvements +* Allowing dot notation for class paths in filters attribute of a service descriptions + +## 2.8.1 - 2012-07-16 + +* Loosening Event Dispatcher dependency +* POST redirects can now be customized using CURLOPT_POSTREDIR + +## 2.8.0 - 2012-07-15 + +* BC: Guzzle\Http\Query + * Query strings with empty variables will always show an equal sign unless the variable is set to QueryString::BLANK (e.g. ?acl= vs ?acl) + * Changed isEncodingValues() and isEncodingFields() to isUrlEncoding() + * Changed setEncodeValues(bool) and setEncodeFields(bool) to useUrlEncoding(bool) + * Changed the aggregation functions of QueryString to be static methods + * Can now use fromString() with querystrings that have a leading ? +* cURL configuration values can be specified in service descriptions using `curl.` prefixed parameters +* Content-Length is set to 0 before emitting the request.before_send event when sending an empty request body +* Cookies are no longer URL decoded by default +* Bug: URI template variables set to null are no longer expanded + +## 2.7.2 - 2012-07-02 + +* BC: Moving things to get ready for subtree splits. Moving Inflection into Common. Moving Guzzle\Http\Parser to Guzzle\Parser. +* BC: Removing Guzzle\Common\Batch\Batch::count() and replacing it with isEmpty() +* CachePlugin now allows for a custom request parameter function to check if a request can be cached +* Bug fix: CachePlugin now only caches GET and HEAD requests by default +* Bug fix: Using header glue when transferring headers over the wire +* Allowing deeply nested arrays for composite variables in URI templates +* Batch divisors can now return iterators or arrays + +## 2.7.1 - 2012-06-26 + +* Minor patch to update version number in UA string +* Updating build process + +## 2.7.0 - 2012-06-25 + +* BC: Inflection classes moved to Guzzle\Inflection. No longer static methods. Can now inject custom inflectors into classes. +* BC: Removed magic setX methods from commands +* BC: Magic methods mapped to service description commands are now inflected in the command factory rather than the client __call() method +* Verbose cURL options are no longer enabled by default. Set curl.debug to true on a client to enable. +* Bug: Now allowing colons in a response start-line (e.g. HTTP/1.1 503 Service Unavailable: Back-end server is at capacity) +* Guzzle\Service\Resource\ResourceIteratorApplyBatched now internally uses the Guzzle\Common\Batch namespace +* Added Guzzle\Service\Plugin namespace and a PluginCollectionPlugin +* Added the ability to set POST fields and files in a service description +* Guzzle\Http\EntityBody::factory() now accepts objects with a __toString() method +* Adding a command.before_prepare event to clients +* Added BatchClosureTransfer and BatchClosureDivisor +* BatchTransferException now includes references to the batch divisor and transfer strategies +* Fixed some tests so that they pass more reliably +* Added Guzzle\Common\Log\ArrayLogAdapter + +## 2.6.6 - 2012-06-10 + +* BC: Removing Guzzle\Http\Plugin\BatchQueuePlugin +* BC: Removing Guzzle\Service\Command\CommandSet +* Adding generic batching system (replaces the batch queue plugin and command set) +* Updating ZF cache and log adapters and now using ZF's composer repository +* Bug: Setting the name of each ApiParam when creating through an ApiCommand +* Adding result_type, result_doc, deprecated, and doc_url to service descriptions +* Bug: Changed the default cookie header casing back to 'Cookie' + +## 2.6.5 - 2012-06-03 + +* BC: Renaming Guzzle\Http\Message\RequestInterface::getResourceUri() to getResource() +* BC: Removing unused AUTH_BASIC and AUTH_DIGEST constants from +* BC: Guzzle\Http\Cookie is now used to manage Set-Cookie data, not Cookie data +* BC: Renaming methods in the CookieJarInterface +* Moving almost all cookie logic out of the CookiePlugin and into the Cookie or CookieJar implementations +* Making the default glue for HTTP headers ';' instead of ',' +* Adding a removeValue to Guzzle\Http\Message\Header +* Adding getCookies() to request interface. +* Making it easier to add event subscribers to HasDispatcherInterface classes. Can now directly call addSubscriber() + +## 2.6.4 - 2012-05-30 + +* BC: Cleaning up how POST files are stored in EntityEnclosingRequest objects. Adding PostFile class. +* BC: Moving ApiCommand specific functionality from the Inspector and on to the ApiCommand +* Bug: Fixing magic method command calls on clients +* Bug: Email constraint only validates strings +* Bug: Aggregate POST fields when POST files are present in curl handle +* Bug: Fixing default User-Agent header +* Bug: Only appending or prepending parameters in commands if they are specified +* Bug: Not requiring response reason phrases or status codes to match a predefined list of codes +* Allowing the use of dot notation for class namespaces when using instance_of constraint +* Added any_match validation constraint +* Added an AsyncPlugin +* Passing request object to the calculateWait method of the ExponentialBackoffPlugin +* Allowing the result of a command object to be changed +* Parsing location and type sub values when instantiating a service description rather than over and over at runtime + +## 2.6.3 - 2012-05-23 + +* [BC] Guzzle\Common\FromConfigInterface no longer requires any config options. +* [BC] Refactoring how POST files are stored on an EntityEnclosingRequest. They are now separate from POST fields. +* You can now use an array of data when creating PUT request bodies in the request factory. +* Removing the requirement that HTTPS requests needed a Cache-Control: public directive to be cacheable. +* [Http] Adding support for Content-Type in multipart POST uploads per upload +* [Http] Added support for uploading multiple files using the same name (foo[0], foo[1]) +* Adding more POST data operations for easier manipulation of POST data. +* You can now set empty POST fields. +* The body of a request is only shown on EntityEnclosingRequest objects that do not use POST files. +* Split the Guzzle\Service\Inspector::validateConfig method into two methods. One to initialize when a command is created, and one to validate. +* CS updates + +## 2.6.2 - 2012-05-19 + +* [Http] Better handling of nested scope requests in CurlMulti. Requests are now always prepares in the send() method rather than the addRequest() method. + +## 2.6.1 - 2012-05-19 + +* [BC] Removing 'path' support in service descriptions. Use 'uri'. +* [BC] Guzzle\Service\Inspector::parseDocBlock is now protected. Adding getApiParamsForClass() with cache. +* [BC] Removing Guzzle\Common\NullObject. Use https://github.com/mtdowling/NullObject if you need it. +* [BC] Removing Guzzle\Common\XmlElement. +* All commands, both dynamic and concrete, have ApiCommand objects. +* Adding a fix for CurlMulti so that if all of the connections encounter some sort of curl error, then the loop exits. +* Adding checks to EntityEnclosingRequest so that empty POST files and fields are ignored. +* Making the method signature of Guzzle\Service\Builder\ServiceBuilder::factory more flexible. + +## 2.6.0 - 2012-05-15 + +* [BC] Moving Guzzle\Service\Builder to Guzzle\Service\Builder\ServiceBuilder +* [BC] Executing a Command returns the result of the command rather than the command +* [BC] Moving all HTTP parsing logic to Guzzle\Http\Parsers. Allows for faster C implementations if needed. +* [BC] Changing the Guzzle\Http\Message\Response::setProtocol() method to accept a protocol and version in separate args. +* [BC] Moving ResourceIterator* to Guzzle\Service\Resource +* [BC] Completely refactored ResourceIterators to iterate over a cloned command object +* [BC] Moved Guzzle\Http\UriTemplate to Guzzle\Http\Parser\UriTemplate\UriTemplate +* [BC] Guzzle\Guzzle is now deprecated +* Moving Guzzle\Common\Guzzle::inject to Guzzle\Common\Collection::inject +* Adding Guzzle\Version class to give version information about Guzzle +* Adding Guzzle\Http\Utils class to provide getDefaultUserAgent() and getHttpDate() +* Adding Guzzle\Curl\CurlVersion to manage caching curl_version() data +* ServiceDescription and ServiceBuilder are now cacheable using similar configs +* Changing the format of XML and JSON service builder configs. Backwards compatible. +* Cleaned up Cookie parsing +* Trimming the default Guzzle User-Agent header +* Adding a setOnComplete() method to Commands that is called when a command completes +* Keeping track of requests that were mocked in the MockPlugin +* Fixed a caching bug in the CacheAdapterFactory +* Inspector objects can be injected into a Command object +* Refactoring a lot of code and tests to be case insensitive when dealing with headers +* Adding Guzzle\Http\Message\HeaderComparison for easy comparison of HTTP headers using a DSL +* Adding the ability to set global option overrides to service builder configs +* Adding the ability to include other service builder config files from within XML and JSON files +* Moving the parseQuery method out of Url and on to QueryString::fromString() as a static factory method. + +## 2.5.0 - 2012-05-08 + +* Major performance improvements +* [BC] Simplifying Guzzle\Common\Collection. Please check to see if you are using features that are now deprecated. +* [BC] Using a custom validation system that allows a flyweight implementation for much faster validation. No longer using Symfony2 Validation component. +* [BC] No longer supporting "{{ }}" for injecting into command or UriTemplates. Use "{}" +* Added the ability to passed parameters to all requests created by a client +* Added callback functionality to the ExponentialBackoffPlugin +* Using microtime in ExponentialBackoffPlugin to allow more granular backoff strategies. +* Rewinding request stream bodies when retrying requests +* Exception is thrown when JSON response body cannot be decoded +* Added configurable magic method calls to clients and commands. This is off by default. +* Fixed a defect that added a hash to every parsed URL part +* Fixed duplicate none generation for OauthPlugin. +* Emitting an event each time a client is generated by a ServiceBuilder +* Using an ApiParams object instead of a Collection for parameters of an ApiCommand +* cache.* request parameters should be renamed to params.cache.* +* Added the ability to set arbitrary curl options on requests (disable_wire, progress, etc.). See CurlHandle. +* Added the ability to disable type validation of service descriptions +* ServiceDescriptions and ServiceBuilders are now Serializable diff --git a/instafeed/vendor/guzzlehttp/guzzle/Dockerfile b/instafeed/vendor/guzzlehttp/guzzle/Dockerfile new file mode 100755 index 0000000..f6a0952 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/guzzle/Dockerfile @@ -0,0 +1,18 @@ +FROM composer:latest as setup + +RUN mkdir /guzzle + +WORKDIR /guzzle + +RUN set -xe \ + && composer init --name=guzzlehttp/test --description="Simple project for testing Guzzle scripts" --author="Márk Sági-Kazár " --no-interaction \ + && composer require guzzlehttp/guzzle + + +FROM php:7.3 + +RUN mkdir /guzzle + +WORKDIR /guzzle + +COPY --from=setup /guzzle /guzzle diff --git a/instafeed/vendor/guzzlehttp/guzzle/LICENSE b/instafeed/vendor/guzzlehttp/guzzle/LICENSE new file mode 100755 index 0000000..50a177b --- /dev/null +++ b/instafeed/vendor/guzzlehttp/guzzle/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2011-2018 Michael Dowling, https://github.com/mtdowling + +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/instafeed/vendor/guzzlehttp/guzzle/README.md b/instafeed/vendor/guzzlehttp/guzzle/README.md new file mode 100755 index 0000000..a5ef18a --- /dev/null +++ b/instafeed/vendor/guzzlehttp/guzzle/README.md @@ -0,0 +1,90 @@ +Guzzle, PHP HTTP client +======================= + +[![Latest Version](https://img.shields.io/github/release/guzzle/guzzle.svg?style=flat-square)](https://github.com/guzzle/guzzle/releases) +[![Build Status](https://img.shields.io/travis/guzzle/guzzle.svg?style=flat-square)](https://travis-ci.org/guzzle/guzzle) +[![Total Downloads](https://img.shields.io/packagist/dt/guzzlehttp/guzzle.svg?style=flat-square)](https://packagist.org/packages/guzzlehttp/guzzle) + +Guzzle is a PHP HTTP client that makes it easy to send HTTP requests and +trivial to integrate with web services. + +- Simple interface for building query strings, POST requests, streaming large + uploads, streaming large downloads, using HTTP cookies, uploading JSON data, + etc... +- Can send both synchronous and asynchronous requests using the same interface. +- Uses PSR-7 interfaces for requests, responses, and streams. This allows you + to utilize other PSR-7 compatible libraries with Guzzle. +- Abstracts away the underlying HTTP transport, allowing you to write + environment and transport agnostic code; i.e., no hard dependency on cURL, + PHP streams, sockets, or non-blocking event loops. +- Middleware system allows you to augment and compose client behavior. + +```php +$client = new \GuzzleHttp\Client(); +$response = $client->request('GET', 'https://api.github.com/repos/guzzle/guzzle'); + +echo $response->getStatusCode(); # 200 +echo $response->getHeaderLine('content-type'); # 'application/json; charset=utf8' +echo $response->getBody(); # '{"id": 1420053, "name": "guzzle", ...}' + +# Send an asynchronous request. +$request = new \GuzzleHttp\Psr7\Request('GET', 'http://httpbin.org'); +$promise = $client->sendAsync($request)->then(function ($response) { + echo 'I completed! ' . $response->getBody(); +}); + +$promise->wait(); +``` + +## Help and docs + +- [Documentation](http://guzzlephp.org/) +- [Stack Overflow](http://stackoverflow.com/questions/tagged/guzzle) +- [Gitter](https://gitter.im/guzzle/guzzle) + + +## Installing Guzzle + +The recommended way to install Guzzle is through +[Composer](http://getcomposer.org). + +```bash +# Install Composer +curl -sS https://getcomposer.org/installer | php +``` + +Next, run the Composer command to install the latest stable version of Guzzle: + +```bash +composer require guzzlehttp/guzzle +``` + +After installing, you need to require Composer's autoloader: + +```php +require 'vendor/autoload.php'; +``` + +You can then later update Guzzle using composer: + + ```bash +composer update + ``` + + +## Version Guidance + +| Version | Status | Packagist | Namespace | Repo | Docs | PSR-7 | PHP Version | +|---------|------------|---------------------|--------------|---------------------|---------------------|-------|-------------| +| 3.x | EOL | `guzzle/guzzle` | `Guzzle` | [v3][guzzle-3-repo] | [v3][guzzle-3-docs] | No | >= 5.3.3 | +| 4.x | EOL | `guzzlehttp/guzzle` | `GuzzleHttp` | [v4][guzzle-4-repo] | N/A | No | >= 5.4 | +| 5.x | Maintained | `guzzlehttp/guzzle` | `GuzzleHttp` | [v5][guzzle-5-repo] | [v5][guzzle-5-docs] | No | >= 5.4 | +| 6.x | Latest | `guzzlehttp/guzzle` | `GuzzleHttp` | [v6][guzzle-6-repo] | [v6][guzzle-6-docs] | Yes | >= 5.5 | + +[guzzle-3-repo]: https://github.com/guzzle/guzzle3 +[guzzle-4-repo]: https://github.com/guzzle/guzzle/tree/4.x +[guzzle-5-repo]: https://github.com/guzzle/guzzle/tree/5.3 +[guzzle-6-repo]: https://github.com/guzzle/guzzle +[guzzle-3-docs]: http://guzzle3.readthedocs.org +[guzzle-5-docs]: http://guzzle.readthedocs.org/en/5.3/ +[guzzle-6-docs]: http://guzzle.readthedocs.org/en/latest/ diff --git a/instafeed/vendor/guzzlehttp/guzzle/UPGRADING.md b/instafeed/vendor/guzzlehttp/guzzle/UPGRADING.md new file mode 100755 index 0000000..91d1dcc --- /dev/null +++ b/instafeed/vendor/guzzlehttp/guzzle/UPGRADING.md @@ -0,0 +1,1203 @@ +Guzzle Upgrade Guide +==================== + +5.0 to 6.0 +---------- + +Guzzle now uses [PSR-7](http://www.php-fig.org/psr/psr-7/) for HTTP messages. +Due to the fact that these messages are immutable, this prompted a refactoring +of Guzzle to use a middleware based system rather than an event system. Any +HTTP message interaction (e.g., `GuzzleHttp\Message\Request`) need to be +updated to work with the new immutable PSR-7 request and response objects. Any +event listeners or subscribers need to be updated to become middleware +functions that wrap handlers (or are injected into a +`GuzzleHttp\HandlerStack`). + +- Removed `GuzzleHttp\BatchResults` +- Removed `GuzzleHttp\Collection` +- Removed `GuzzleHttp\HasDataTrait` +- Removed `GuzzleHttp\ToArrayInterface` +- The `guzzlehttp/streams` dependency has been removed. Stream functionality + is now present in the `GuzzleHttp\Psr7` namespace provided by the + `guzzlehttp/psr7` package. +- Guzzle no longer uses ReactPHP promises and now uses the + `guzzlehttp/promises` library. We use a custom promise library for three + significant reasons: + 1. React promises (at the time of writing this) are recursive. Promise + chaining and promise resolution will eventually blow the stack. Guzzle + promises are not recursive as they use a sort of trampolining technique. + Note: there has been movement in the React project to modify promises to + no longer utilize recursion. + 2. Guzzle needs to have the ability to synchronously block on a promise to + wait for a result. Guzzle promises allows this functionality (and does + not require the use of recursion). + 3. Because we need to be able to wait on a result, doing so using React + promises requires wrapping react promises with RingPHP futures. This + overhead is no longer needed, reducing stack sizes, reducing complexity, + and improving performance. +- `GuzzleHttp\Mimetypes` has been moved to a function in + `GuzzleHttp\Psr7\mimetype_from_extension` and + `GuzzleHttp\Psr7\mimetype_from_filename`. +- `GuzzleHttp\Query` and `GuzzleHttp\QueryParser` have been removed. Query + strings must now be passed into request objects as strings, or provided to + the `query` request option when creating requests with clients. The `query` + option uses PHP's `http_build_query` to convert an array to a string. If you + need a different serialization technique, you will need to pass the query + string in as a string. There are a couple helper functions that will make + working with query strings easier: `GuzzleHttp\Psr7\parse_query` and + `GuzzleHttp\Psr7\build_query`. +- Guzzle no longer has a dependency on RingPHP. Due to the use of a middleware + system based on PSR-7, using RingPHP and it's middleware system as well adds + more complexity than the benefits it provides. All HTTP handlers that were + present in RingPHP have been modified to work directly with PSR-7 messages + and placed in the `GuzzleHttp\Handler` namespace. This significantly reduces + complexity in Guzzle, removes a dependency, and improves performance. RingPHP + will be maintained for Guzzle 5 support, but will no longer be a part of + Guzzle 6. +- As Guzzle now uses a middleware based systems the event system and RingPHP + integration has been removed. Note: while the event system has been removed, + it is possible to add your own type of event system that is powered by the + middleware system. + - Removed the `Event` namespace. + - Removed the `Subscriber` namespace. + - Removed `Transaction` class + - Removed `RequestFsm` + - Removed `RingBridge` + - `GuzzleHttp\Subscriber\Cookie` is now provided by + `GuzzleHttp\Middleware::cookies` + - `GuzzleHttp\Subscriber\HttpError` is now provided by + `GuzzleHttp\Middleware::httpError` + - `GuzzleHttp\Subscriber\History` is now provided by + `GuzzleHttp\Middleware::history` + - `GuzzleHttp\Subscriber\Mock` is now provided by + `GuzzleHttp\Handler\MockHandler` + - `GuzzleHttp\Subscriber\Prepare` is now provided by + `GuzzleHttp\PrepareBodyMiddleware` + - `GuzzleHttp\Subscriber\Redirect` is now provided by + `GuzzleHttp\RedirectMiddleware` +- Guzzle now uses `Psr\Http\Message\UriInterface` (implements in + `GuzzleHttp\Psr7\Uri`) for URI support. `GuzzleHttp\Url` is now gone. +- Static functions in `GuzzleHttp\Utils` have been moved to namespaced + functions under the `GuzzleHttp` namespace. This requires either a Composer + based autoloader or you to include functions.php. +- `GuzzleHttp\ClientInterface::getDefaultOption` has been renamed to + `GuzzleHttp\ClientInterface::getConfig`. +- `GuzzleHttp\ClientInterface::setDefaultOption` has been removed. +- The `json` and `xml` methods of response objects has been removed. With the + migration to strictly adhering to PSR-7 as the interface for Guzzle messages, + adding methods to message interfaces would actually require Guzzle messages + to extend from PSR-7 messages rather then work with them directly. + +## Migrating to middleware + +The change to PSR-7 unfortunately required significant refactoring to Guzzle +due to the fact that PSR-7 messages are immutable. Guzzle 5 relied on an event +system from plugins. The event system relied on mutability of HTTP messages and +side effects in order to work. With immutable messages, you have to change your +workflow to become more about either returning a value (e.g., functional +middlewares) or setting a value on an object. Guzzle v6 has chosen the +functional middleware approach. + +Instead of using the event system to listen for things like the `before` event, +you now create a stack based middleware function that intercepts a request on +the way in and the promise of the response on the way out. This is a much +simpler and more predictable approach than the event system and works nicely +with PSR-7 middleware. Due to the use of promises, the middleware system is +also asynchronous. + +v5: + +```php +use GuzzleHttp\Event\BeforeEvent; +$client = new GuzzleHttp\Client(); +// Get the emitter and listen to the before event. +$client->getEmitter()->on('before', function (BeforeEvent $e) { + // Guzzle v5 events relied on mutation + $e->getRequest()->setHeader('X-Foo', 'Bar'); +}); +``` + +v6: + +In v6, you can modify the request before it is sent using the `mapRequest` +middleware. The idiomatic way in v6 to modify the request/response lifecycle is +to setup a handler middleware stack up front and inject the handler into a +client. + +```php +use GuzzleHttp\Middleware; +// Create a handler stack that has all of the default middlewares attached +$handler = GuzzleHttp\HandlerStack::create(); +// Push the handler onto the handler stack +$handler->push(Middleware::mapRequest(function (RequestInterface $request) { + // Notice that we have to return a request object + return $request->withHeader('X-Foo', 'Bar'); +})); +// Inject the handler into the client +$client = new GuzzleHttp\Client(['handler' => $handler]); +``` + +## POST Requests + +This version added the [`form_params`](http://guzzle.readthedocs.org/en/latest/request-options.html#form_params) +and `multipart` request options. `form_params` is an associative array of +strings or array of strings and is used to serialize an +`application/x-www-form-urlencoded` POST request. The +[`multipart`](http://guzzle.readthedocs.org/en/latest/request-options.html#multipart) +option is now used to send a multipart/form-data POST request. + +`GuzzleHttp\Post\PostFile` has been removed. Use the `multipart` option to add +POST files to a multipart/form-data request. + +The `body` option no longer accepts an array to send POST requests. Please use +`multipart` or `form_params` instead. + +The `base_url` option has been renamed to `base_uri`. + +4.x to 5.0 +---------- + +## Rewritten Adapter Layer + +Guzzle now uses [RingPHP](http://ringphp.readthedocs.org/en/latest) to send +HTTP requests. The `adapter` option in a `GuzzleHttp\Client` constructor +is still supported, but it has now been renamed to `handler`. Instead of +passing a `GuzzleHttp\Adapter\AdapterInterface`, you must now pass a PHP +`callable` that follows the RingPHP specification. + +## Removed Fluent Interfaces + +[Fluent interfaces were removed](http://ocramius.github.io/blog/fluent-interfaces-are-evil) +from the following classes: + +- `GuzzleHttp\Collection` +- `GuzzleHttp\Url` +- `GuzzleHttp\Query` +- `GuzzleHttp\Post\PostBody` +- `GuzzleHttp\Cookie\SetCookie` + +## Removed functions.php + +Removed "functions.php", so that Guzzle is truly PSR-4 compliant. The following +functions can be used as replacements. + +- `GuzzleHttp\json_decode` -> `GuzzleHttp\Utils::jsonDecode` +- `GuzzleHttp\get_path` -> `GuzzleHttp\Utils::getPath` +- `GuzzleHttp\Utils::setPath` -> `GuzzleHttp\set_path` +- `GuzzleHttp\Pool::batch` -> `GuzzleHttp\batch`. This function is, however, + deprecated in favor of using `GuzzleHttp\Pool::batch()`. + +The "procedural" global client has been removed with no replacement (e.g., +`GuzzleHttp\get()`, `GuzzleHttp\post()`, etc.). Use a `GuzzleHttp\Client` +object as a replacement. + +## `throwImmediately` has been removed + +The concept of "throwImmediately" has been removed from exceptions and error +events. This control mechanism was used to stop a transfer of concurrent +requests from completing. This can now be handled by throwing the exception or +by cancelling a pool of requests or each outstanding future request +individually. + +## headers event has been removed + +Removed the "headers" event. This event was only useful for changing the +body a response once the headers of the response were known. You can implement +a similar behavior in a number of ways. One example might be to use a +FnStream that has access to the transaction being sent. For example, when the +first byte is written, you could check if the response headers match your +expectations, and if so, change the actual stream body that is being +written to. + +## Updates to HTTP Messages + +Removed the `asArray` parameter from +`GuzzleHttp\Message\MessageInterface::getHeader`. If you want to get a header +value as an array, then use the newly added `getHeaderAsArray()` method of +`MessageInterface`. This change makes the Guzzle interfaces compatible with +the PSR-7 interfaces. + +3.x to 4.0 +---------- + +## Overarching changes: + +- Now requires PHP 5.4 or greater. +- No longer requires cURL to send requests. +- Guzzle no longer wraps every exception it throws. Only exceptions that are + recoverable are now wrapped by Guzzle. +- Various namespaces have been removed or renamed. +- No longer requiring the Symfony EventDispatcher. A custom event dispatcher + based on the Symfony EventDispatcher is + now utilized in `GuzzleHttp\Event\EmitterInterface` (resulting in significant + speed and functionality improvements). + +Changes per Guzzle 3.x namespace are described below. + +## Batch + +The `Guzzle\Batch` namespace has been removed. This is best left to +third-parties to implement on top of Guzzle's core HTTP library. + +## Cache + +The `Guzzle\Cache` namespace has been removed. (Todo: No suitable replacement +has been implemented yet, but hoping to utilize a PSR cache interface). + +## Common + +- Removed all of the wrapped exceptions. It's better to use the standard PHP + library for unrecoverable exceptions. +- `FromConfigInterface` has been removed. +- `Guzzle\Common\Version` has been removed. The VERSION constant can be found + at `GuzzleHttp\ClientInterface::VERSION`. + +### Collection + +- `getAll` has been removed. Use `toArray` to convert a collection to an array. +- `inject` has been removed. +- `keySearch` has been removed. +- `getPath` no longer supports wildcard expressions. Use something better like + JMESPath for this. +- `setPath` now supports appending to an existing array via the `[]` notation. + +### Events + +Guzzle no longer requires Symfony's EventDispatcher component. Guzzle now uses +`GuzzleHttp\Event\Emitter`. + +- `Symfony\Component\EventDispatcher\EventDispatcherInterface` is replaced by + `GuzzleHttp\Event\EmitterInterface`. +- `Symfony\Component\EventDispatcher\EventDispatcher` is replaced by + `GuzzleHttp\Event\Emitter`. +- `Symfony\Component\EventDispatcher\Event` is replaced by + `GuzzleHttp\Event\Event`, and Guzzle now has an EventInterface in + `GuzzleHttp\Event\EventInterface`. +- `AbstractHasDispatcher` has moved to a trait, `HasEmitterTrait`, and + `HasDispatcherInterface` has moved to `HasEmitterInterface`. Retrieving the + event emitter of a request, client, etc. now uses the `getEmitter` method + rather than the `getDispatcher` method. + +#### Emitter + +- Use the `once()` method to add a listener that automatically removes itself + the first time it is invoked. +- Use the `listeners()` method to retrieve a list of event listeners rather than + the `getListeners()` method. +- Use `emit()` instead of `dispatch()` to emit an event from an emitter. +- Use `attach()` instead of `addSubscriber()` and `detach()` instead of + `removeSubscriber()`. + +```php +$mock = new Mock(); +// 3.x +$request->getEventDispatcher()->addSubscriber($mock); +$request->getEventDispatcher()->removeSubscriber($mock); +// 4.x +$request->getEmitter()->attach($mock); +$request->getEmitter()->detach($mock); +``` + +Use the `on()` method to add a listener rather than the `addListener()` method. + +```php +// 3.x +$request->getEventDispatcher()->addListener('foo', function (Event $event) { /* ... */ } ); +// 4.x +$request->getEmitter()->on('foo', function (Event $event, $name) { /* ... */ } ); +``` + +## Http + +### General changes + +- The cacert.pem certificate has been moved to `src/cacert.pem`. +- Added the concept of adapters that are used to transfer requests over the + wire. +- Simplified the event system. +- Sending requests in parallel is still possible, but batching is no longer a + concept of the HTTP layer. Instead, you must use the `complete` and `error` + events to asynchronously manage parallel request transfers. +- `Guzzle\Http\Url` has moved to `GuzzleHttp\Url`. +- `Guzzle\Http\QueryString` has moved to `GuzzleHttp\Query`. +- QueryAggregators have been rewritten so that they are simply callable + functions. +- `GuzzleHttp\StaticClient` has been removed. Use the functions provided in + `functions.php` for an easy to use static client instance. +- Exceptions in `GuzzleHttp\Exception` have been updated to all extend from + `GuzzleHttp\Exception\TransferException`. + +### Client + +Calling methods like `get()`, `post()`, `head()`, etc. no longer create and +return a request, but rather creates a request, sends the request, and returns +the response. + +```php +// 3.0 +$request = $client->get('/'); +$response = $request->send(); + +// 4.0 +$response = $client->get('/'); + +// or, to mirror the previous behavior +$request = $client->createRequest('GET', '/'); +$response = $client->send($request); +``` + +`GuzzleHttp\ClientInterface` has changed. + +- The `send` method no longer accepts more than one request. Use `sendAll` to + send multiple requests in parallel. +- `setUserAgent()` has been removed. Use a default request option instead. You + could, for example, do something like: + `$client->setConfig('defaults/headers/User-Agent', 'Foo/Bar ' . $client::getDefaultUserAgent())`. +- `setSslVerification()` has been removed. Use default request options instead, + like `$client->setConfig('defaults/verify', true)`. + +`GuzzleHttp\Client` has changed. + +- The constructor now accepts only an associative array. You can include a + `base_url` string or array to use a URI template as the base URL of a client. + You can also specify a `defaults` key that is an associative array of default + request options. You can pass an `adapter` to use a custom adapter, + `batch_adapter` to use a custom adapter for sending requests in parallel, or + a `message_factory` to change the factory used to create HTTP requests and + responses. +- The client no longer emits a `client.create_request` event. +- Creating requests with a client no longer automatically utilize a URI + template. You must pass an array into a creational method (e.g., + `createRequest`, `get`, `put`, etc.) in order to expand a URI template. + +### Messages + +Messages no longer have references to their counterparts (i.e., a request no +longer has a reference to it's response, and a response no loger has a +reference to its request). This association is now managed through a +`GuzzleHttp\Adapter\TransactionInterface` object. You can get references to +these transaction objects using request events that are emitted over the +lifecycle of a request. + +#### Requests with a body + +- `GuzzleHttp\Message\EntityEnclosingRequest` and + `GuzzleHttp\Message\EntityEnclosingRequestInterface` have been removed. The + separation between requests that contain a body and requests that do not + contain a body has been removed, and now `GuzzleHttp\Message\RequestInterface` + handles both use cases. +- Any method that previously accepts a `GuzzleHttp\Response` object now accept a + `GuzzleHttp\Message\ResponseInterface`. +- `GuzzleHttp\Message\RequestFactoryInterface` has been renamed to + `GuzzleHttp\Message\MessageFactoryInterface`. This interface is used to create + both requests and responses and is implemented in + `GuzzleHttp\Message\MessageFactory`. +- POST field and file methods have been removed from the request object. You + must now use the methods made available to `GuzzleHttp\Post\PostBodyInterface` + to control the format of a POST body. Requests that are created using a + standard `GuzzleHttp\Message\MessageFactoryInterface` will automatically use + a `GuzzleHttp\Post\PostBody` body if the body was passed as an array or if + the method is POST and no body is provided. + +```php +$request = $client->createRequest('POST', '/'); +$request->getBody()->setField('foo', 'bar'); +$request->getBody()->addFile(new PostFile('file_key', fopen('/path/to/content', 'r'))); +``` + +#### Headers + +- `GuzzleHttp\Message\Header` has been removed. Header values are now simply + represented by an array of values or as a string. Header values are returned + as a string by default when retrieving a header value from a message. You can + pass an optional argument of `true` to retrieve a header value as an array + of strings instead of a single concatenated string. +- `GuzzleHttp\PostFile` and `GuzzleHttp\PostFileInterface` have been moved to + `GuzzleHttp\Post`. This interface has been simplified and now allows the + addition of arbitrary headers. +- Custom headers like `GuzzleHttp\Message\Header\Link` have been removed. Most + of the custom headers are now handled separately in specific + subscribers/plugins, and `GuzzleHttp\Message\HeaderValues::parseParams()` has + been updated to properly handle headers that contain parameters (like the + `Link` header). + +#### Responses + +- `GuzzleHttp\Message\Response::getInfo()` and + `GuzzleHttp\Message\Response::setInfo()` have been removed. Use the event + system to retrieve this type of information. +- `GuzzleHttp\Message\Response::getRawHeaders()` has been removed. +- `GuzzleHttp\Message\Response::getMessage()` has been removed. +- `GuzzleHttp\Message\Response::calculateAge()` and other cache specific + methods have moved to the CacheSubscriber. +- Header specific helper functions like `getContentMd5()` have been removed. + Just use `getHeader('Content-MD5')` instead. +- `GuzzleHttp\Message\Response::setRequest()` and + `GuzzleHttp\Message\Response::getRequest()` have been removed. Use the event + system to work with request and response objects as a transaction. +- `GuzzleHttp\Message\Response::getRedirectCount()` has been removed. Use the + Redirect subscriber instead. +- `GuzzleHttp\Message\Response::isSuccessful()` and other related methods have + been removed. Use `getStatusCode()` instead. + +#### Streaming responses + +Streaming requests can now be created by a client directly, returning a +`GuzzleHttp\Message\ResponseInterface` object that contains a body stream +referencing an open PHP HTTP stream. + +```php +// 3.0 +use Guzzle\Stream\PhpStreamRequestFactory; +$request = $client->get('/'); +$factory = new PhpStreamRequestFactory(); +$stream = $factory->fromRequest($request); +$data = $stream->read(1024); + +// 4.0 +$response = $client->get('/', ['stream' => true]); +// Read some data off of the stream in the response body +$data = $response->getBody()->read(1024); +``` + +#### Redirects + +The `configureRedirects()` method has been removed in favor of a +`allow_redirects` request option. + +```php +// Standard redirects with a default of a max of 5 redirects +$request = $client->createRequest('GET', '/', ['allow_redirects' => true]); + +// Strict redirects with a custom number of redirects +$request = $client->createRequest('GET', '/', [ + 'allow_redirects' => ['max' => 5, 'strict' => true] +]); +``` + +#### EntityBody + +EntityBody interfaces and classes have been removed or moved to +`GuzzleHttp\Stream`. All classes and interfaces that once required +`GuzzleHttp\EntityBodyInterface` now require +`GuzzleHttp\Stream\StreamInterface`. Creating a new body for a request no +longer uses `GuzzleHttp\EntityBody::factory` but now uses +`GuzzleHttp\Stream\Stream::factory` or even better: +`GuzzleHttp\Stream\create()`. + +- `Guzzle\Http\EntityBodyInterface` is now `GuzzleHttp\Stream\StreamInterface` +- `Guzzle\Http\EntityBody` is now `GuzzleHttp\Stream\Stream` +- `Guzzle\Http\CachingEntityBody` is now `GuzzleHttp\Stream\CachingStream` +- `Guzzle\Http\ReadLimitEntityBody` is now `GuzzleHttp\Stream\LimitStream` +- `Guzzle\Http\IoEmittyinEntityBody` has been removed. + +#### Request lifecycle events + +Requests previously submitted a large number of requests. The number of events +emitted over the lifecycle of a request has been significantly reduced to make +it easier to understand how to extend the behavior of a request. All events +emitted during the lifecycle of a request now emit a custom +`GuzzleHttp\Event\EventInterface` object that contains context providing +methods and a way in which to modify the transaction at that specific point in +time (e.g., intercept the request and set a response on the transaction). + +- `request.before_send` has been renamed to `before` and now emits a + `GuzzleHttp\Event\BeforeEvent` +- `request.complete` has been renamed to `complete` and now emits a + `GuzzleHttp\Event\CompleteEvent`. +- `request.sent` has been removed. Use `complete`. +- `request.success` has been removed. Use `complete`. +- `error` is now an event that emits a `GuzzleHttp\Event\ErrorEvent`. +- `request.exception` has been removed. Use `error`. +- `request.receive.status_line` has been removed. +- `curl.callback.progress` has been removed. Use a custom `StreamInterface` to + maintain a status update. +- `curl.callback.write` has been removed. Use a custom `StreamInterface` to + intercept writes. +- `curl.callback.read` has been removed. Use a custom `StreamInterface` to + intercept reads. + +`headers` is a new event that is emitted after the response headers of a +request have been received before the body of the response is downloaded. This +event emits a `GuzzleHttp\Event\HeadersEvent`. + +You can intercept a request and inject a response using the `intercept()` event +of a `GuzzleHttp\Event\BeforeEvent`, `GuzzleHttp\Event\CompleteEvent`, and +`GuzzleHttp\Event\ErrorEvent` event. + +See: http://docs.guzzlephp.org/en/latest/events.html + +## Inflection + +The `Guzzle\Inflection` namespace has been removed. This is not a core concern +of Guzzle. + +## Iterator + +The `Guzzle\Iterator` namespace has been removed. + +- `Guzzle\Iterator\AppendIterator`, `Guzzle\Iterator\ChunkedIterator`, and + `Guzzle\Iterator\MethodProxyIterator` are nice, but not a core requirement of + Guzzle itself. +- `Guzzle\Iterator\FilterIterator` is no longer needed because an equivalent + class is shipped with PHP 5.4. +- `Guzzle\Iterator\MapIterator` is not really needed when using PHP 5.5 because + it's easier to just wrap an iterator in a generator that maps values. + +For a replacement of these iterators, see https://github.com/nikic/iter + +## Log + +The LogPlugin has moved to https://github.com/guzzle/log-subscriber. The +`Guzzle\Log` namespace has been removed. Guzzle now relies on +`Psr\Log\LoggerInterface` for all logging. The MessageFormatter class has been +moved to `GuzzleHttp\Subscriber\Log\Formatter`. + +## Parser + +The `Guzzle\Parser` namespace has been removed. This was previously used to +make it possible to plug in custom parsers for cookies, messages, URI +templates, and URLs; however, this level of complexity is not needed in Guzzle +so it has been removed. + +- Cookie: Cookie parsing logic has been moved to + `GuzzleHttp\Cookie\SetCookie::fromString`. +- Message: Message parsing logic for both requests and responses has been moved + to `GuzzleHttp\Message\MessageFactory::fromMessage`. Message parsing is only + used in debugging or deserializing messages, so it doesn't make sense for + Guzzle as a library to add this level of complexity to parsing messages. +- UriTemplate: URI template parsing has been moved to + `GuzzleHttp\UriTemplate`. The Guzzle library will automatically use the PECL + URI template library if it is installed. +- Url: URL parsing is now performed in `GuzzleHttp\Url::fromString` (previously + it was `Guzzle\Http\Url::factory()`). If custom URL parsing is necessary, + then developers are free to subclass `GuzzleHttp\Url`. + +## Plugin + +The `Guzzle\Plugin` namespace has been renamed to `GuzzleHttp\Subscriber`. +Several plugins are shipping with the core Guzzle library under this namespace. + +- `GuzzleHttp\Subscriber\Cookie`: Replaces the old CookiePlugin. Cookie jar + code has moved to `GuzzleHttp\Cookie`. +- `GuzzleHttp\Subscriber\History`: Replaces the old HistoryPlugin. +- `GuzzleHttp\Subscriber\HttpError`: Throws errors when a bad HTTP response is + received. +- `GuzzleHttp\Subscriber\Mock`: Replaces the old MockPlugin. +- `GuzzleHttp\Subscriber\Prepare`: Prepares the body of a request just before + sending. This subscriber is attached to all requests by default. +- `GuzzleHttp\Subscriber\Redirect`: Replaces the RedirectPlugin. + +The following plugins have been removed (third-parties are free to re-implement +these if needed): + +- `GuzzleHttp\Plugin\Async` has been removed. +- `GuzzleHttp\Plugin\CurlAuth` has been removed. +- `GuzzleHttp\Plugin\ErrorResponse\ErrorResponsePlugin` has been removed. This + functionality should instead be implemented with event listeners that occur + after normal response parsing occurs in the guzzle/command package. + +The following plugins are not part of the core Guzzle package, but are provided +in separate repositories: + +- `Guzzle\Http\Plugin\BackoffPlugin` has been rewritten to be much simpler + to build custom retry policies using simple functions rather than various + chained classes. See: https://github.com/guzzle/retry-subscriber +- `Guzzle\Http\Plugin\Cache\CachePlugin` has moved to + https://github.com/guzzle/cache-subscriber +- `Guzzle\Http\Plugin\Log\LogPlugin` has moved to + https://github.com/guzzle/log-subscriber +- `Guzzle\Http\Plugin\Md5\Md5Plugin` has moved to + https://github.com/guzzle/message-integrity-subscriber +- `Guzzle\Http\Plugin\Mock\MockPlugin` has moved to + `GuzzleHttp\Subscriber\MockSubscriber`. +- `Guzzle\Http\Plugin\Oauth\OauthPlugin` has moved to + https://github.com/guzzle/oauth-subscriber + +## Service + +The service description layer of Guzzle has moved into two separate packages: + +- http://github.com/guzzle/command Provides a high level abstraction over web + services by representing web service operations using commands. +- http://github.com/guzzle/guzzle-services Provides an implementation of + guzzle/command that provides request serialization and response parsing using + Guzzle service descriptions. + +## Stream + +Stream have moved to a separate package available at +https://github.com/guzzle/streams. + +`Guzzle\Stream\StreamInterface` has been given a large update to cleanly take +on the responsibilities of `Guzzle\Http\EntityBody` and +`Guzzle\Http\EntityBodyInterface` now that they have been removed. The number +of methods implemented by the `StreamInterface` has been drastically reduced to +allow developers to more easily extend and decorate stream behavior. + +## Removed methods from StreamInterface + +- `getStream` and `setStream` have been removed to better encapsulate streams. +- `getMetadata` and `setMetadata` have been removed in favor of + `GuzzleHttp\Stream\MetadataStreamInterface`. +- `getWrapper`, `getWrapperData`, `getStreamType`, and `getUri` have all been + removed. This data is accessible when + using streams that implement `GuzzleHttp\Stream\MetadataStreamInterface`. +- `rewind` has been removed. Use `seek(0)` for a similar behavior. + +## Renamed methods + +- `detachStream` has been renamed to `detach`. +- `feof` has been renamed to `eof`. +- `ftell` has been renamed to `tell`. +- `readLine` has moved from an instance method to a static class method of + `GuzzleHttp\Stream\Stream`. + +## Metadata streams + +`GuzzleHttp\Stream\MetadataStreamInterface` has been added to denote streams +that contain additional metadata accessible via `getMetadata()`. +`GuzzleHttp\Stream\StreamInterface::getMetadata` and +`GuzzleHttp\Stream\StreamInterface::setMetadata` have been removed. + +## StreamRequestFactory + +The entire concept of the StreamRequestFactory has been removed. The way this +was used in Guzzle 3 broke the actual interface of sending streaming requests +(instead of getting back a Response, you got a StreamInterface). Streaming +PHP requests are now implemented through the `GuzzleHttp\Adapter\StreamAdapter`. + +3.6 to 3.7 +---------- + +### Deprecations + +- You can now enable E_USER_DEPRECATED warnings to see if you are using any deprecated methods.: + +```php +\Guzzle\Common\Version::$emitWarnings = true; +``` + +The following APIs and options have been marked as deprecated: + +- Marked `Guzzle\Http\Message\Request::isResponseBodyRepeatable()` as deprecated. Use `$request->getResponseBody()->isRepeatable()` instead. +- Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. +- Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. +- Marked `Guzzle\Http\Message\Request::setIsRedirect()` as deprecated. Use the HistoryPlugin instead. +- Marked `Guzzle\Http\Message\Request::isRedirect()` as deprecated. Use the HistoryPlugin instead. +- Marked `Guzzle\Cache\CacheAdapterFactory::factory()` as deprecated +- Marked `Guzzle\Service\Client::enableMagicMethods()` as deprecated. Magic methods can no longer be disabled on a Guzzle\Service\Client. +- Marked `Guzzle\Parser\Url\UrlParser` as deprecated. Just use PHP's `parse_url()` and percent encode your UTF-8. +- Marked `Guzzle\Common\Collection::inject()` as deprecated. +- Marked `Guzzle\Plugin\CurlAuth\CurlAuthPlugin` as deprecated. Use + `$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest|NTLM|Any'));` or + `$client->setDefaultOption('auth', array('user', 'pass', 'Basic|Digest|NTLM|Any'));` + +3.7 introduces `request.options` as a parameter for a client configuration and as an optional argument to all creational +request methods. When paired with a client's configuration settings, these options allow you to specify default settings +for various aspects of a request. Because these options make other previous configuration options redundant, several +configuration options and methods of a client and AbstractCommand have been deprecated. + +- Marked `Guzzle\Service\Client::getDefaultHeaders()` as deprecated. Use `$client->getDefaultOption('headers')`. +- Marked `Guzzle\Service\Client::setDefaultHeaders()` as deprecated. Use `$client->setDefaultOption('headers/{header_name}', 'value')`. +- Marked 'request.params' for `Guzzle\Http\Client` as deprecated. Use `$client->setDefaultOption('params/{param_name}', 'value')` +- Marked 'command.headers', 'command.response_body' and 'command.on_complete' as deprecated for AbstractCommand. These will work through Guzzle 4.0 + + $command = $client->getCommand('foo', array( + 'command.headers' => array('Test' => '123'), + 'command.response_body' => '/path/to/file' + )); + + // Should be changed to: + + $command = $client->getCommand('foo', array( + 'command.request_options' => array( + 'headers' => array('Test' => '123'), + 'save_as' => '/path/to/file' + ) + )); + +### Interface changes + +Additions and changes (you will need to update any implementations or subclasses you may have created): + +- Added an `$options` argument to the end of the following methods of `Guzzle\Http\ClientInterface`: + createRequest, head, delete, put, patch, post, options, prepareRequest +- Added an `$options` argument to the end of `Guzzle\Http\Message\Request\RequestFactoryInterface::createRequest()` +- Added an `applyOptions()` method to `Guzzle\Http\Message\Request\RequestFactoryInterface` +- Changed `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $body = null)` to + `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $options = array())`. You can still pass in a + resource, string, or EntityBody into the $options parameter to specify the download location of the response. +- Changed `Guzzle\Common\Collection::__construct($data)` to no longer accepts a null value for `$data` but a + default `array()` +- Added `Guzzle\Stream\StreamInterface::isRepeatable` +- Made `Guzzle\Http\Client::expandTemplate` and `getUriTemplate` protected methods. + +The following methods were removed from interfaces. All of these methods are still available in the concrete classes +that implement them, but you should update your code to use alternative methods: + +- Removed `Guzzle\Http\ClientInterface::setDefaultHeaders(). Use + `$client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. or + `$client->getConfig()->setPath('request.options/headers', array('header_name' => 'value'))` or + `$client->setDefaultOption('headers/{header_name}', 'value')`. or + `$client->setDefaultOption('headers', array('header_name' => 'value'))`. +- Removed `Guzzle\Http\ClientInterface::getDefaultHeaders(). Use `$client->getConfig()->getPath('request.options/headers')`. +- Removed `Guzzle\Http\ClientInterface::expandTemplate()`. This is an implementation detail. +- Removed `Guzzle\Http\ClientInterface::setRequestFactory()`. This is an implementation detail. +- Removed `Guzzle\Http\ClientInterface::getCurlMulti()`. This is a very specific implementation detail. +- Removed `Guzzle\Http\Message\RequestInterface::canCache`. Use the CachePlugin. +- Removed `Guzzle\Http\Message\RequestInterface::setIsRedirect`. Use the HistoryPlugin. +- Removed `Guzzle\Http\Message\RequestInterface::isRedirect`. Use the HistoryPlugin. + +### Cache plugin breaking changes + +- CacheKeyProviderInterface and DefaultCacheKeyProvider are no longer used. All of this logic is handled in a + CacheStorageInterface. These two objects and interface will be removed in a future version. +- Always setting X-cache headers on cached responses +- Default cache TTLs are now handled by the CacheStorageInterface of a CachePlugin +- `CacheStorageInterface::cache($key, Response $response, $ttl = null)` has changed to `cache(RequestInterface + $request, Response $response);` +- `CacheStorageInterface::fetch($key)` has changed to `fetch(RequestInterface $request);` +- `CacheStorageInterface::delete($key)` has changed to `delete(RequestInterface $request);` +- Added `CacheStorageInterface::purge($url)` +- `DefaultRevalidation::__construct(CacheKeyProviderInterface $cacheKey, CacheStorageInterface $cache, CachePlugin + $plugin)` has changed to `DefaultRevalidation::__construct(CacheStorageInterface $cache, + CanCacheStrategyInterface $canCache = null)` +- Added `RevalidationInterface::shouldRevalidate(RequestInterface $request, Response $response)` + +3.5 to 3.6 +---------- + +* Mixed casing of headers are now forced to be a single consistent casing across all values for that header. +* Messages internally use a HeaderCollection object to delegate handling case-insensitive header resolution +* Removed the whole changedHeader() function system of messages because all header changes now go through addHeader(). + For example, setHeader() first removes the header using unset on a HeaderCollection and then calls addHeader(). + Keeping the Host header and URL host in sync is now handled by overriding the addHeader method in Request. +* Specific header implementations can be created for complex headers. When a message creates a header, it uses a + HeaderFactory which can map specific headers to specific header classes. There is now a Link header and + CacheControl header implementation. +* Moved getLinks() from Response to just be used on a Link header object. + +If you previously relied on Guzzle\Http\Message\Header::raw(), then you will need to update your code to use the +HeaderInterface (e.g. toArray(), getAll(), etc.). + +### Interface changes + +* Removed from interface: Guzzle\Http\ClientInterface::setUriTemplate +* Removed from interface: Guzzle\Http\ClientInterface::setCurlMulti() +* Removed Guzzle\Http\Message\Request::receivedRequestHeader() and implemented this functionality in + Guzzle\Http\Curl\RequestMediator +* Removed the optional $asString parameter from MessageInterface::getHeader(). Just cast the header to a string. +* Removed the optional $tryChunkedTransfer option from Guzzle\Http\Message\EntityEnclosingRequestInterface +* Removed the $asObjects argument from Guzzle\Http\Message\MessageInterface::getHeaders() + +### Removed deprecated functions + +* Removed Guzzle\Parser\ParserRegister::get(). Use getParser() +* Removed Guzzle\Parser\ParserRegister::set(). Use registerParser(). + +### Deprecations + +* The ability to case-insensitively search for header values +* Guzzle\Http\Message\Header::hasExactHeader +* Guzzle\Http\Message\Header::raw. Use getAll() +* Deprecated cache control specific methods on Guzzle\Http\Message\AbstractMessage. Use the CacheControl header object + instead. + +### Other changes + +* All response header helper functions return a string rather than mixing Header objects and strings inconsistently +* Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc. are managed by Guzzle + directly via interfaces +* Removed the injecting of a request object onto a response object. The methods to get and set a request still exist + but are a no-op until removed. +* Most classes that used to require a `Guzzle\Service\Command\CommandInterface` typehint now request a + `Guzzle\Service\Command\ArrayCommandInterface`. +* Added `Guzzle\Http\Message\RequestInterface::startResponse()` to the RequestInterface to handle injecting a response + on a request while the request is still being transferred +* `Guzzle\Service\Command\CommandInterface` now extends from ToArrayInterface and ArrayAccess + +3.3 to 3.4 +---------- + +Base URLs of a client now follow the rules of http://tools.ietf.org/html/rfc3986#section-5.2.2 when merging URLs. + +3.2 to 3.3 +---------- + +### Response::getEtag() quote stripping removed + +`Guzzle\Http\Message\Response::getEtag()` no longer strips quotes around the ETag response header + +### Removed `Guzzle\Http\Utils` + +The `Guzzle\Http\Utils` class was removed. This class was only used for testing. + +### Stream wrapper and type + +`Guzzle\Stream\Stream::getWrapper()` and `Guzzle\Stream\Stream::getStreamType()` are no longer converted to lowercase. + +### curl.emit_io became emit_io + +Emitting IO events from a RequestMediator is now a parameter that must be set in a request's curl options using the +'emit_io' key. This was previously set under a request's parameters using 'curl.emit_io' + +3.1 to 3.2 +---------- + +### CurlMulti is no longer reused globally + +Before 3.2, the same CurlMulti object was reused globally for each client. This can cause issue where plugins added +to a single client can pollute requests dispatched from other clients. + +If you still wish to reuse the same CurlMulti object with each client, then you can add a listener to the +ServiceBuilder's `service_builder.create_client` event to inject a custom CurlMulti object into each client as it is +created. + +```php +$multi = new Guzzle\Http\Curl\CurlMulti(); +$builder = Guzzle\Service\Builder\ServiceBuilder::factory('/path/to/config.json'); +$builder->addListener('service_builder.create_client', function ($event) use ($multi) { + $event['client']->setCurlMulti($multi); +} +}); +``` + +### No default path + +URLs no longer have a default path value of '/' if no path was specified. + +Before: + +```php +$request = $client->get('http://www.foo.com'); +echo $request->getUrl(); +// >> http://www.foo.com/ +``` + +After: + +```php +$request = $client->get('http://www.foo.com'); +echo $request->getUrl(); +// >> http://www.foo.com +``` + +### Less verbose BadResponseException + +The exception message for `Guzzle\Http\Exception\BadResponseException` no longer contains the full HTTP request and +response information. You can, however, get access to the request and response object by calling `getRequest()` or +`getResponse()` on the exception object. + +### Query parameter aggregation + +Multi-valued query parameters are no longer aggregated using a callback function. `Guzzle\Http\Query` now has a +setAggregator() method that accepts a `Guzzle\Http\QueryAggregator\QueryAggregatorInterface` object. This object is +responsible for handling the aggregation of multi-valued query string variables into a flattened hash. + +2.8 to 3.x +---------- + +### Guzzle\Service\Inspector + +Change `\Guzzle\Service\Inspector::fromConfig` to `\Guzzle\Common\Collection::fromConfig` + +**Before** + +```php +use Guzzle\Service\Inspector; + +class YourClient extends \Guzzle\Service\Client +{ + public static function factory($config = array()) + { + $default = array(); + $required = array('base_url', 'username', 'api_key'); + $config = Inspector::fromConfig($config, $default, $required); + + $client = new self( + $config->get('base_url'), + $config->get('username'), + $config->get('api_key') + ); + $client->setConfig($config); + + $client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'client.json')); + + return $client; + } +``` + +**After** + +```php +use Guzzle\Common\Collection; + +class YourClient extends \Guzzle\Service\Client +{ + public static function factory($config = array()) + { + $default = array(); + $required = array('base_url', 'username', 'api_key'); + $config = Collection::fromConfig($config, $default, $required); + + $client = new self( + $config->get('base_url'), + $config->get('username'), + $config->get('api_key') + ); + $client->setConfig($config); + + $client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'client.json')); + + return $client; + } +``` + +### Convert XML Service Descriptions to JSON + +**Before** + +```xml + + + + + + Get a list of groups + + + Uses a search query to get a list of groups + + + + Create a group + + + + + Delete a group by ID + + + + + + + Update a group + + + + + + +``` + +**After** + +```json +{ + "name": "Zendesk REST API v2", + "apiVersion": "2012-12-31", + "description":"Provides access to Zendesk views, groups, tickets, ticket fields, and users", + "operations": { + "list_groups": { + "httpMethod":"GET", + "uri": "groups.json", + "summary": "Get a list of groups" + }, + "search_groups":{ + "httpMethod":"GET", + "uri": "search.json?query=\"{query} type:group\"", + "summary": "Uses a search query to get a list of groups", + "parameters":{ + "query":{ + "location": "uri", + "description":"Zendesk Search Query", + "type": "string", + "required": true + } + } + }, + "create_group": { + "httpMethod":"POST", + "uri": "groups.json", + "summary": "Create a group", + "parameters":{ + "data": { + "type": "array", + "location": "body", + "description":"Group JSON", + "filters": "json_encode", + "required": true + }, + "Content-Type":{ + "type": "string", + "location":"header", + "static": "application/json" + } + } + }, + "delete_group": { + "httpMethod":"DELETE", + "uri": "groups/{id}.json", + "summary": "Delete a group", + "parameters":{ + "id":{ + "location": "uri", + "description":"Group to delete by ID", + "type": "integer", + "required": true + } + } + }, + "get_group": { + "httpMethod":"GET", + "uri": "groups/{id}.json", + "summary": "Get a ticket", + "parameters":{ + "id":{ + "location": "uri", + "description":"Group to get by ID", + "type": "integer", + "required": true + } + } + }, + "update_group": { + "httpMethod":"PUT", + "uri": "groups/{id}.json", + "summary": "Update a group", + "parameters":{ + "id": { + "location": "uri", + "description":"Group to update by ID", + "type": "integer", + "required": true + }, + "data": { + "type": "array", + "location": "body", + "description":"Group JSON", + "filters": "json_encode", + "required": true + }, + "Content-Type":{ + "type": "string", + "location":"header", + "static": "application/json" + } + } + } +} +``` + +### Guzzle\Service\Description\ServiceDescription + +Commands are now called Operations + +**Before** + +```php +use Guzzle\Service\Description\ServiceDescription; + +$sd = new ServiceDescription(); +$sd->getCommands(); // @returns ApiCommandInterface[] +$sd->hasCommand($name); +$sd->getCommand($name); // @returns ApiCommandInterface|null +$sd->addCommand($command); // @param ApiCommandInterface $command +``` + +**After** + +```php +use Guzzle\Service\Description\ServiceDescription; + +$sd = new ServiceDescription(); +$sd->getOperations(); // @returns OperationInterface[] +$sd->hasOperation($name); +$sd->getOperation($name); // @returns OperationInterface|null +$sd->addOperation($operation); // @param OperationInterface $operation +``` + +### Guzzle\Common\Inflection\Inflector + +Namespace is now `Guzzle\Inflection\Inflector` + +### Guzzle\Http\Plugin + +Namespace is now `Guzzle\Plugin`. Many other changes occur within this namespace and are detailed in their own sections below. + +### Guzzle\Http\Plugin\LogPlugin and Guzzle\Common\Log + +Now `Guzzle\Plugin\Log\LogPlugin` and `Guzzle\Log` respectively. + +**Before** + +```php +use Guzzle\Common\Log\ClosureLogAdapter; +use Guzzle\Http\Plugin\LogPlugin; + +/** @var \Guzzle\Http\Client */ +$client; + +// $verbosity is an integer indicating desired message verbosity level +$client->addSubscriber(new LogPlugin(new ClosureLogAdapter(function($m) { echo $m; }, $verbosity = LogPlugin::LOG_VERBOSE); +``` + +**After** + +```php +use Guzzle\Log\ClosureLogAdapter; +use Guzzle\Log\MessageFormatter; +use Guzzle\Plugin\Log\LogPlugin; + +/** @var \Guzzle\Http\Client */ +$client; + +// $format is a string indicating desired message format -- @see MessageFormatter +$client->addSubscriber(new LogPlugin(new ClosureLogAdapter(function($m) { echo $m; }, $format = MessageFormatter::DEBUG_FORMAT); +``` + +### Guzzle\Http\Plugin\CurlAuthPlugin + +Now `Guzzle\Plugin\CurlAuth\CurlAuthPlugin`. + +### Guzzle\Http\Plugin\ExponentialBackoffPlugin + +Now `Guzzle\Plugin\Backoff\BackoffPlugin`, and other changes. + +**Before** + +```php +use Guzzle\Http\Plugin\ExponentialBackoffPlugin; + +$backoffPlugin = new ExponentialBackoffPlugin($maxRetries, array_merge( + ExponentialBackoffPlugin::getDefaultFailureCodes(), array(429) + )); + +$client->addSubscriber($backoffPlugin); +``` + +**After** + +```php +use Guzzle\Plugin\Backoff\BackoffPlugin; +use Guzzle\Plugin\Backoff\HttpBackoffStrategy; + +// Use convenient factory method instead -- see implementation for ideas of what +// you can do with chaining backoff strategies +$backoffPlugin = BackoffPlugin::getExponentialBackoff($maxRetries, array_merge( + HttpBackoffStrategy::getDefaultFailureCodes(), array(429) + )); +$client->addSubscriber($backoffPlugin); +``` + +### Known Issues + +#### [BUG] Accept-Encoding header behavior changed unintentionally. + +(See #217) (Fixed in 09daeb8c666fb44499a0646d655a8ae36456575e) + +In version 2.8 setting the `Accept-Encoding` header would set the CURLOPT_ENCODING option, which permitted cURL to +properly handle gzip/deflate compressed responses from the server. In versions affected by this bug this does not happen. +See issue #217 for a workaround, or use a version containing the fix. diff --git a/instafeed/vendor/guzzlehttp/guzzle/composer.json b/instafeed/vendor/guzzlehttp/guzzle/composer.json new file mode 100755 index 0000000..c553257 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/guzzle/composer.json @@ -0,0 +1,58 @@ +{ + "name": "guzzlehttp/guzzle", + "type": "library", + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "framework", + "http", + "rest", + "web service", + "curl", + "client", + "HTTP client" + ], + "homepage": "http://guzzlephp.org/", + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.5", + "ext-json": "*", + "guzzlehttp/promises": "^1.0", + "guzzlehttp/psr7": "^1.6.1" + }, + "require-dev": { + "ext-curl": "*", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", + "psr/log": "^1.1" + }, + "suggest": { + "psr/log": "Required for using the Log middleware" + }, + "config": { + "sort-packages": true + }, + "extra": { + "branch-alias": { + "dev-master": "6.3-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "autoload-dev": { + "psr-4": { + "GuzzleHttp\\Tests\\": "tests/" + } + } +} diff --git a/instafeed/vendor/guzzlehttp/guzzle/phpstan.neon.dist b/instafeed/vendor/guzzlehttp/guzzle/phpstan.neon.dist new file mode 100755 index 0000000..4ef4192 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/guzzle/phpstan.neon.dist @@ -0,0 +1,9 @@ +parameters: + level: 1 + paths: + - src + + ignoreErrors: + - + message: '#Function uri_template not found#' + path: %currentWorkingDirectory%/src/functions.php diff --git a/instafeed/vendor/guzzlehttp/guzzle/src/Client.php b/instafeed/vendor/guzzlehttp/guzzle/src/Client.php new file mode 100755 index 0000000..0f43c71 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/guzzle/src/Client.php @@ -0,0 +1,422 @@ + 'http://www.foo.com/1.0/', + * 'timeout' => 0, + * 'allow_redirects' => false, + * 'proxy' => '192.168.16.1:10' + * ]); + * + * Client configuration settings include the following options: + * + * - handler: (callable) Function that transfers HTTP requests over the + * wire. The function is called with a Psr7\Http\Message\RequestInterface + * and array of transfer options, and must return a + * GuzzleHttp\Promise\PromiseInterface that is fulfilled with a + * Psr7\Http\Message\ResponseInterface on success. "handler" is a + * constructor only option that cannot be overridden in per/request + * options. If no handler is provided, a default handler will be created + * that enables all of the request options below by attaching all of the + * default middleware to the handler. + * - base_uri: (string|UriInterface) Base URI of the client that is merged + * into relative URIs. Can be a string or instance of UriInterface. + * - **: any request option + * + * @param array $config Client configuration settings. + * + * @see \GuzzleHttp\RequestOptions for a list of available request options. + */ + public function __construct(array $config = []) + { + if (!isset($config['handler'])) { + $config['handler'] = HandlerStack::create(); + } elseif (!is_callable($config['handler'])) { + throw new \InvalidArgumentException('handler must be a callable'); + } + + // Convert the base_uri to a UriInterface + if (isset($config['base_uri'])) { + $config['base_uri'] = Psr7\uri_for($config['base_uri']); + } + + $this->configureDefaults($config); + } + + public function __call($method, $args) + { + if (count($args) < 1) { + throw new \InvalidArgumentException('Magic request methods require a URI and optional options array'); + } + + $uri = $args[0]; + $opts = isset($args[1]) ? $args[1] : []; + + return substr($method, -5) === 'Async' + ? $this->requestAsync(substr($method, 0, -5), $uri, $opts) + : $this->request($method, $uri, $opts); + } + + public function sendAsync(RequestInterface $request, array $options = []) + { + // Merge the base URI into the request URI if needed. + $options = $this->prepareDefaults($options); + + return $this->transfer( + $request->withUri($this->buildUri($request->getUri(), $options), $request->hasHeader('Host')), + $options + ); + } + + public function send(RequestInterface $request, array $options = []) + { + $options[RequestOptions::SYNCHRONOUS] = true; + return $this->sendAsync($request, $options)->wait(); + } + + public function requestAsync($method, $uri = '', array $options = []) + { + $options = $this->prepareDefaults($options); + // Remove request modifying parameter because it can be done up-front. + $headers = isset($options['headers']) ? $options['headers'] : []; + $body = isset($options['body']) ? $options['body'] : null; + $version = isset($options['version']) ? $options['version'] : '1.1'; + // Merge the URI into the base URI. + $uri = $this->buildUri($uri, $options); + if (is_array($body)) { + $this->invalidBody(); + } + $request = new Psr7\Request($method, $uri, $headers, $body, $version); + // Remove the option so that they are not doubly-applied. + unset($options['headers'], $options['body'], $options['version']); + + return $this->transfer($request, $options); + } + + public function request($method, $uri = '', array $options = []) + { + $options[RequestOptions::SYNCHRONOUS] = true; + return $this->requestAsync($method, $uri, $options)->wait(); + } + + public function getConfig($option = null) + { + return $option === null + ? $this->config + : (isset($this->config[$option]) ? $this->config[$option] : null); + } + + private function buildUri($uri, array $config) + { + // for BC we accept null which would otherwise fail in uri_for + $uri = Psr7\uri_for($uri === null ? '' : $uri); + + if (isset($config['base_uri'])) { + $uri = Psr7\UriResolver::resolve(Psr7\uri_for($config['base_uri']), $uri); + } + + return $uri->getScheme() === '' && $uri->getHost() !== '' ? $uri->withScheme('http') : $uri; + } + + /** + * Configures the default options for a client. + * + * @param array $config + */ + private function configureDefaults(array $config) + { + $defaults = [ + 'allow_redirects' => RedirectMiddleware::$defaultSettings, + 'http_errors' => true, + 'decode_content' => true, + 'verify' => true, + 'cookies' => false + ]; + + // Use the standard Linux HTTP_PROXY and HTTPS_PROXY if set. + + // We can only trust the HTTP_PROXY environment variable in a CLI + // process due to the fact that PHP has no reliable mechanism to + // get environment variables that start with "HTTP_". + if (php_sapi_name() == 'cli' && getenv('HTTP_PROXY')) { + $defaults['proxy']['http'] = getenv('HTTP_PROXY'); + } + + if ($proxy = getenv('HTTPS_PROXY')) { + $defaults['proxy']['https'] = $proxy; + } + + if ($noProxy = getenv('NO_PROXY')) { + $cleanedNoProxy = str_replace(' ', '', $noProxy); + $defaults['proxy']['no'] = explode(',', $cleanedNoProxy); + } + + $this->config = $config + $defaults; + + if (!empty($config['cookies']) && $config['cookies'] === true) { + $this->config['cookies'] = new CookieJar(); + } + + // Add the default user-agent header. + if (!isset($this->config['headers'])) { + $this->config['headers'] = ['User-Agent' => default_user_agent()]; + } else { + // Add the User-Agent header if one was not already set. + foreach (array_keys($this->config['headers']) as $name) { + if (strtolower($name) === 'user-agent') { + return; + } + } + $this->config['headers']['User-Agent'] = default_user_agent(); + } + } + + /** + * Merges default options into the array. + * + * @param array $options Options to modify by reference + * + * @return array + */ + private function prepareDefaults(array $options) + { + $defaults = $this->config; + + if (!empty($defaults['headers'])) { + // Default headers are only added if they are not present. + $defaults['_conditional'] = $defaults['headers']; + unset($defaults['headers']); + } + + // Special handling for headers is required as they are added as + // conditional headers and as headers passed to a request ctor. + if (array_key_exists('headers', $options)) { + // Allows default headers to be unset. + if ($options['headers'] === null) { + $defaults['_conditional'] = null; + unset($options['headers']); + } elseif (!is_array($options['headers'])) { + throw new \InvalidArgumentException('headers must be an array'); + } + } + + // Shallow merge defaults underneath options. + $result = $options + $defaults; + + // Remove null values. + foreach ($result as $k => $v) { + if ($v === null) { + unset($result[$k]); + } + } + + return $result; + } + + /** + * Transfers the given request and applies request options. + * + * The URI of the request is not modified and the request options are used + * as-is without merging in default options. + * + * @param RequestInterface $request + * @param array $options + * + * @return Promise\PromiseInterface + */ + private function transfer(RequestInterface $request, array $options) + { + // save_to -> sink + if (isset($options['save_to'])) { + $options['sink'] = $options['save_to']; + unset($options['save_to']); + } + + // exceptions -> http_errors + if (isset($options['exceptions'])) { + $options['http_errors'] = $options['exceptions']; + unset($options['exceptions']); + } + + $request = $this->applyOptions($request, $options); + $handler = $options['handler']; + + try { + return Promise\promise_for($handler($request, $options)); + } catch (\Exception $e) { + return Promise\rejection_for($e); + } + } + + /** + * Applies the array of request options to a request. + * + * @param RequestInterface $request + * @param array $options + * + * @return RequestInterface + */ + private function applyOptions(RequestInterface $request, array &$options) + { + $modify = [ + 'set_headers' => [], + ]; + + if (isset($options['headers'])) { + $modify['set_headers'] = $options['headers']; + unset($options['headers']); + } + + if (isset($options['form_params'])) { + if (isset($options['multipart'])) { + throw new \InvalidArgumentException('You cannot use ' + . 'form_params and multipart at the same time. Use the ' + . 'form_params option if you want to send application/' + . 'x-www-form-urlencoded requests, and the multipart ' + . 'option to send multipart/form-data requests.'); + } + $options['body'] = http_build_query($options['form_params'], '', '&'); + unset($options['form_params']); + // Ensure that we don't have the header in different case and set the new value. + $options['_conditional'] = Psr7\_caseless_remove(['Content-Type'], $options['_conditional']); + $options['_conditional']['Content-Type'] = 'application/x-www-form-urlencoded'; + } + + if (isset($options['multipart'])) { + $options['body'] = new Psr7\MultipartStream($options['multipart']); + unset($options['multipart']); + } + + if (isset($options['json'])) { + $options['body'] = \GuzzleHttp\json_encode($options['json']); + unset($options['json']); + // Ensure that we don't have the header in different case and set the new value. + $options['_conditional'] = Psr7\_caseless_remove(['Content-Type'], $options['_conditional']); + $options['_conditional']['Content-Type'] = 'application/json'; + } + + if (!empty($options['decode_content']) + && $options['decode_content'] !== true + ) { + // Ensure that we don't have the header in different case and set the new value. + $options['_conditional'] = Psr7\_caseless_remove(['Accept-Encoding'], $options['_conditional']); + $modify['set_headers']['Accept-Encoding'] = $options['decode_content']; + } + + if (isset($options['body'])) { + if (is_array($options['body'])) { + $this->invalidBody(); + } + $modify['body'] = Psr7\stream_for($options['body']); + unset($options['body']); + } + + if (!empty($options['auth']) && is_array($options['auth'])) { + $value = $options['auth']; + $type = isset($value[2]) ? strtolower($value[2]) : 'basic'; + switch ($type) { + case 'basic': + // Ensure that we don't have the header in different case and set the new value. + $modify['set_headers'] = Psr7\_caseless_remove(['Authorization'], $modify['set_headers']); + $modify['set_headers']['Authorization'] = 'Basic ' + . base64_encode("$value[0]:$value[1]"); + break; + case 'digest': + // @todo: Do not rely on curl + $options['curl'][CURLOPT_HTTPAUTH] = CURLAUTH_DIGEST; + $options['curl'][CURLOPT_USERPWD] = "$value[0]:$value[1]"; + break; + case 'ntlm': + $options['curl'][CURLOPT_HTTPAUTH] = CURLAUTH_NTLM; + $options['curl'][CURLOPT_USERPWD] = "$value[0]:$value[1]"; + break; + } + } + + if (isset($options['query'])) { + $value = $options['query']; + if (is_array($value)) { + $value = http_build_query($value, null, '&', PHP_QUERY_RFC3986); + } + if (!is_string($value)) { + throw new \InvalidArgumentException('query must be a string or array'); + } + $modify['query'] = $value; + unset($options['query']); + } + + // Ensure that sink is not an invalid value. + if (isset($options['sink'])) { + // TODO: Add more sink validation? + if (is_bool($options['sink'])) { + throw new \InvalidArgumentException('sink must not be a boolean'); + } + } + + $request = Psr7\modify_request($request, $modify); + if ($request->getBody() instanceof Psr7\MultipartStream) { + // Use a multipart/form-data POST if a Content-Type is not set. + // Ensure that we don't have the header in different case and set the new value. + $options['_conditional'] = Psr7\_caseless_remove(['Content-Type'], $options['_conditional']); + $options['_conditional']['Content-Type'] = 'multipart/form-data; boundary=' + . $request->getBody()->getBoundary(); + } + + // Merge in conditional headers if they are not present. + if (isset($options['_conditional'])) { + // Build up the changes so it's in a single clone of the message. + $modify = []; + foreach ($options['_conditional'] as $k => $v) { + if (!$request->hasHeader($k)) { + $modify['set_headers'][$k] = $v; + } + } + $request = Psr7\modify_request($request, $modify); + // Don't pass this internal value along to middleware/handlers. + unset($options['_conditional']); + } + + return $request; + } + + private function invalidBody() + { + throw new \InvalidArgumentException('Passing in the "body" request ' + . 'option as an array to send a POST request has been deprecated. ' + . 'Please use the "form_params" request option to send a ' + . 'application/x-www-form-urlencoded request, or the "multipart" ' + . 'request option to send a multipart/form-data request.'); + } +} diff --git a/instafeed/vendor/guzzlehttp/guzzle/src/ClientInterface.php b/instafeed/vendor/guzzlehttp/guzzle/src/ClientInterface.php new file mode 100755 index 0000000..5b37085 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/guzzle/src/ClientInterface.php @@ -0,0 +1,84 @@ +strictMode = $strictMode; + + foreach ($cookieArray as $cookie) { + if (!($cookie instanceof SetCookie)) { + $cookie = new SetCookie($cookie); + } + $this->setCookie($cookie); + } + } + + /** + * Create a new Cookie jar from an associative array and domain. + * + * @param array $cookies Cookies to create the jar from + * @param string $domain Domain to set the cookies to + * + * @return self + */ + public static function fromArray(array $cookies, $domain) + { + $cookieJar = new self(); + foreach ($cookies as $name => $value) { + $cookieJar->setCookie(new SetCookie([ + 'Domain' => $domain, + 'Name' => $name, + 'Value' => $value, + 'Discard' => true + ])); + } + + return $cookieJar; + } + + /** + * @deprecated + */ + public static function getCookieValue($value) + { + return $value; + } + + /** + * Evaluate if this cookie should be persisted to storage + * that survives between requests. + * + * @param SetCookie $cookie Being evaluated. + * @param bool $allowSessionCookies If we should persist session cookies + * @return bool + */ + public static function shouldPersist( + SetCookie $cookie, + $allowSessionCookies = false + ) { + if ($cookie->getExpires() || $allowSessionCookies) { + if (!$cookie->getDiscard()) { + return true; + } + } + + return false; + } + + /** + * Finds and returns the cookie based on the name + * + * @param string $name cookie name to search for + * @return SetCookie|null cookie that was found or null if not found + */ + public function getCookieByName($name) + { + // don't allow a null name + if ($name === null) { + return null; + } + foreach ($this->cookies as $cookie) { + if ($cookie->getName() !== null && strcasecmp($cookie->getName(), $name) === 0) { + return $cookie; + } + } + } + + public function toArray() + { + return array_map(function (SetCookie $cookie) { + return $cookie->toArray(); + }, $this->getIterator()->getArrayCopy()); + } + + public function clear($domain = null, $path = null, $name = null) + { + if (!$domain) { + $this->cookies = []; + return; + } elseif (!$path) { + $this->cookies = array_filter( + $this->cookies, + function (SetCookie $cookie) use ($domain) { + return !$cookie->matchesDomain($domain); + } + ); + } elseif (!$name) { + $this->cookies = array_filter( + $this->cookies, + function (SetCookie $cookie) use ($path, $domain) { + return !($cookie->matchesPath($path) && + $cookie->matchesDomain($domain)); + } + ); + } else { + $this->cookies = array_filter( + $this->cookies, + function (SetCookie $cookie) use ($path, $domain, $name) { + return !($cookie->getName() == $name && + $cookie->matchesPath($path) && + $cookie->matchesDomain($domain)); + } + ); + } + } + + public function clearSessionCookies() + { + $this->cookies = array_filter( + $this->cookies, + function (SetCookie $cookie) { + return !$cookie->getDiscard() && $cookie->getExpires(); + } + ); + } + + public function setCookie(SetCookie $cookie) + { + // If the name string is empty (but not 0), ignore the set-cookie + // string entirely. + $name = $cookie->getName(); + if (!$name && $name !== '0') { + return false; + } + + // Only allow cookies with set and valid domain, name, value + $result = $cookie->validate(); + if ($result !== true) { + if ($this->strictMode) { + throw new \RuntimeException('Invalid cookie: ' . $result); + } else { + $this->removeCookieIfEmpty($cookie); + return false; + } + } + + // Resolve conflicts with previously set cookies + foreach ($this->cookies as $i => $c) { + + // Two cookies are identical, when their path, and domain are + // identical. + if ($c->getPath() != $cookie->getPath() || + $c->getDomain() != $cookie->getDomain() || + $c->getName() != $cookie->getName() + ) { + continue; + } + + // The previously set cookie is a discard cookie and this one is + // not so allow the new cookie to be set + if (!$cookie->getDiscard() && $c->getDiscard()) { + unset($this->cookies[$i]); + continue; + } + + // If the new cookie's expiration is further into the future, then + // replace the old cookie + if ($cookie->getExpires() > $c->getExpires()) { + unset($this->cookies[$i]); + continue; + } + + // If the value has changed, we better change it + if ($cookie->getValue() !== $c->getValue()) { + unset($this->cookies[$i]); + continue; + } + + // The cookie exists, so no need to continue + return false; + } + + $this->cookies[] = $cookie; + + return true; + } + + public function count() + { + return count($this->cookies); + } + + public function getIterator() + { + return new \ArrayIterator(array_values($this->cookies)); + } + + public function extractCookies( + RequestInterface $request, + ResponseInterface $response + ) { + if ($cookieHeader = $response->getHeader('Set-Cookie')) { + foreach ($cookieHeader as $cookie) { + $sc = SetCookie::fromString($cookie); + if (!$sc->getDomain()) { + $sc->setDomain($request->getUri()->getHost()); + } + if (0 !== strpos($sc->getPath(), '/')) { + $sc->setPath($this->getCookiePathFromRequest($request)); + } + $this->setCookie($sc); + } + } + } + + /** + * Computes cookie path following RFC 6265 section 5.1.4 + * + * @link https://tools.ietf.org/html/rfc6265#section-5.1.4 + * + * @param RequestInterface $request + * @return string + */ + private function getCookiePathFromRequest(RequestInterface $request) + { + $uriPath = $request->getUri()->getPath(); + if ('' === $uriPath) { + return '/'; + } + if (0 !== strpos($uriPath, '/')) { + return '/'; + } + if ('/' === $uriPath) { + return '/'; + } + if (0 === $lastSlashPos = strrpos($uriPath, '/')) { + return '/'; + } + + return substr($uriPath, 0, $lastSlashPos); + } + + public function withCookieHeader(RequestInterface $request) + { + $values = []; + $uri = $request->getUri(); + $scheme = $uri->getScheme(); + $host = $uri->getHost(); + $path = $uri->getPath() ?: '/'; + + foreach ($this->cookies as $cookie) { + if ($cookie->matchesPath($path) && + $cookie->matchesDomain($host) && + !$cookie->isExpired() && + (!$cookie->getSecure() || $scheme === 'https') + ) { + $values[] = $cookie->getName() . '=' + . $cookie->getValue(); + } + } + + return $values + ? $request->withHeader('Cookie', implode('; ', $values)) + : $request; + } + + /** + * If a cookie already exists and the server asks to set it again with a + * null value, the cookie must be deleted. + * + * @param SetCookie $cookie + */ + private function removeCookieIfEmpty(SetCookie $cookie) + { + $cookieValue = $cookie->getValue(); + if ($cookieValue === null || $cookieValue === '') { + $this->clear( + $cookie->getDomain(), + $cookie->getPath(), + $cookie->getName() + ); + } + } +} diff --git a/instafeed/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php b/instafeed/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php new file mode 100755 index 0000000..2cf298a --- /dev/null +++ b/instafeed/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php @@ -0,0 +1,84 @@ +filename = $cookieFile; + $this->storeSessionCookies = $storeSessionCookies; + + if (file_exists($cookieFile)) { + $this->load($cookieFile); + } + } + + /** + * Saves the file when shutting down + */ + public function __destruct() + { + $this->save($this->filename); + } + + /** + * Saves the cookies to a file. + * + * @param string $filename File to save + * @throws \RuntimeException if the file cannot be found or created + */ + public function save($filename) + { + $json = []; + foreach ($this as $cookie) { + /** @var SetCookie $cookie */ + if (CookieJar::shouldPersist($cookie, $this->storeSessionCookies)) { + $json[] = $cookie->toArray(); + } + } + + $jsonStr = \GuzzleHttp\json_encode($json); + if (false === file_put_contents($filename, $jsonStr, LOCK_EX)) { + throw new \RuntimeException("Unable to save file {$filename}"); + } + } + + /** + * Load cookies from a JSON formatted file. + * + * Old cookies are kept unless overwritten by newly loaded ones. + * + * @param string $filename Cookie file to load. + * @throws \RuntimeException if the file cannot be loaded. + */ + public function load($filename) + { + $json = file_get_contents($filename); + if (false === $json) { + throw new \RuntimeException("Unable to load file {$filename}"); + } elseif ($json === '') { + return; + } + + $data = \GuzzleHttp\json_decode($json, true); + if (is_array($data)) { + foreach (json_decode($json, true) as $cookie) { + $this->setCookie(new SetCookie($cookie)); + } + } elseif (strlen($data)) { + throw new \RuntimeException("Invalid cookie file: {$filename}"); + } + } +} diff --git a/instafeed/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php b/instafeed/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php new file mode 100755 index 0000000..0224a24 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php @@ -0,0 +1,72 @@ +sessionKey = $sessionKey; + $this->storeSessionCookies = $storeSessionCookies; + $this->load(); + } + + /** + * Saves cookies to session when shutting down + */ + public function __destruct() + { + $this->save(); + } + + /** + * Save cookies to the client session + */ + public function save() + { + $json = []; + foreach ($this as $cookie) { + /** @var SetCookie $cookie */ + if (CookieJar::shouldPersist($cookie, $this->storeSessionCookies)) { + $json[] = $cookie->toArray(); + } + } + + $_SESSION[$this->sessionKey] = json_encode($json); + } + + /** + * Load the contents of the client session into the data array + */ + protected function load() + { + if (!isset($_SESSION[$this->sessionKey])) { + return; + } + $data = json_decode($_SESSION[$this->sessionKey], true); + if (is_array($data)) { + foreach ($data as $cookie) { + $this->setCookie(new SetCookie($cookie)); + } + } elseif (strlen($data)) { + throw new \RuntimeException("Invalid cookie data"); + } + } +} diff --git a/instafeed/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php b/instafeed/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php new file mode 100755 index 0000000..3d776a7 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php @@ -0,0 +1,403 @@ + null, + 'Value' => null, + 'Domain' => null, + 'Path' => '/', + 'Max-Age' => null, + 'Expires' => null, + 'Secure' => false, + 'Discard' => false, + 'HttpOnly' => false + ]; + + /** @var array Cookie data */ + private $data; + + /** + * Create a new SetCookie object from a string + * + * @param string $cookie Set-Cookie header string + * + * @return self + */ + public static function fromString($cookie) + { + // Create the default return array + $data = self::$defaults; + // Explode the cookie string using a series of semicolons + $pieces = array_filter(array_map('trim', explode(';', $cookie))); + // The name of the cookie (first kvp) must exist and include an equal sign. + if (empty($pieces[0]) || !strpos($pieces[0], '=')) { + return new self($data); + } + + // Add the cookie pieces into the parsed data array + foreach ($pieces as $part) { + $cookieParts = explode('=', $part, 2); + $key = trim($cookieParts[0]); + $value = isset($cookieParts[1]) + ? trim($cookieParts[1], " \n\r\t\0\x0B") + : true; + + // Only check for non-cookies when cookies have been found + if (empty($data['Name'])) { + $data['Name'] = $key; + $data['Value'] = $value; + } else { + foreach (array_keys(self::$defaults) as $search) { + if (!strcasecmp($search, $key)) { + $data[$search] = $value; + continue 2; + } + } + $data[$key] = $value; + } + } + + return new self($data); + } + + /** + * @param array $data Array of cookie data provided by a Cookie parser + */ + public function __construct(array $data = []) + { + $this->data = array_replace(self::$defaults, $data); + // Extract the Expires value and turn it into a UNIX timestamp if needed + if (!$this->getExpires() && $this->getMaxAge()) { + // Calculate the Expires date + $this->setExpires(time() + $this->getMaxAge()); + } elseif ($this->getExpires() && !is_numeric($this->getExpires())) { + $this->setExpires($this->getExpires()); + } + } + + public function __toString() + { + $str = $this->data['Name'] . '=' . $this->data['Value'] . '; '; + foreach ($this->data as $k => $v) { + if ($k !== 'Name' && $k !== 'Value' && $v !== null && $v !== false) { + if ($k === 'Expires') { + $str .= 'Expires=' . gmdate('D, d M Y H:i:s \G\M\T', $v) . '; '; + } else { + $str .= ($v === true ? $k : "{$k}={$v}") . '; '; + } + } + } + + return rtrim($str, '; '); + } + + public function toArray() + { + return $this->data; + } + + /** + * Get the cookie name + * + * @return string + */ + public function getName() + { + return $this->data['Name']; + } + + /** + * Set the cookie name + * + * @param string $name Cookie name + */ + public function setName($name) + { + $this->data['Name'] = $name; + } + + /** + * Get the cookie value + * + * @return string + */ + public function getValue() + { + return $this->data['Value']; + } + + /** + * Set the cookie value + * + * @param string $value Cookie value + */ + public function setValue($value) + { + $this->data['Value'] = $value; + } + + /** + * Get the domain + * + * @return string|null + */ + public function getDomain() + { + return $this->data['Domain']; + } + + /** + * Set the domain of the cookie + * + * @param string $domain + */ + public function setDomain($domain) + { + $this->data['Domain'] = $domain; + } + + /** + * Get the path + * + * @return string + */ + public function getPath() + { + return $this->data['Path']; + } + + /** + * Set the path of the cookie + * + * @param string $path Path of the cookie + */ + public function setPath($path) + { + $this->data['Path'] = $path; + } + + /** + * Maximum lifetime of the cookie in seconds + * + * @return int|null + */ + public function getMaxAge() + { + return $this->data['Max-Age']; + } + + /** + * Set the max-age of the cookie + * + * @param int $maxAge Max age of the cookie in seconds + */ + public function setMaxAge($maxAge) + { + $this->data['Max-Age'] = $maxAge; + } + + /** + * The UNIX timestamp when the cookie Expires + * + * @return mixed + */ + public function getExpires() + { + return $this->data['Expires']; + } + + /** + * Set the unix timestamp for which the cookie will expire + * + * @param int $timestamp Unix timestamp + */ + public function setExpires($timestamp) + { + $this->data['Expires'] = is_numeric($timestamp) + ? (int) $timestamp + : strtotime($timestamp); + } + + /** + * Get whether or not this is a secure cookie + * + * @return bool|null + */ + public function getSecure() + { + return $this->data['Secure']; + } + + /** + * Set whether or not the cookie is secure + * + * @param bool $secure Set to true or false if secure + */ + public function setSecure($secure) + { + $this->data['Secure'] = $secure; + } + + /** + * Get whether or not this is a session cookie + * + * @return bool|null + */ + public function getDiscard() + { + return $this->data['Discard']; + } + + /** + * Set whether or not this is a session cookie + * + * @param bool $discard Set to true or false if this is a session cookie + */ + public function setDiscard($discard) + { + $this->data['Discard'] = $discard; + } + + /** + * Get whether or not this is an HTTP only cookie + * + * @return bool + */ + public function getHttpOnly() + { + return $this->data['HttpOnly']; + } + + /** + * Set whether or not this is an HTTP only cookie + * + * @param bool $httpOnly Set to true or false if this is HTTP only + */ + public function setHttpOnly($httpOnly) + { + $this->data['HttpOnly'] = $httpOnly; + } + + /** + * Check if the cookie matches a path value. + * + * A request-path path-matches a given cookie-path if at least one of + * the following conditions holds: + * + * - The cookie-path and the request-path are identical. + * - The cookie-path is a prefix of the request-path, and the last + * character of the cookie-path is %x2F ("/"). + * - The cookie-path is a prefix of the request-path, and the first + * character of the request-path that is not included in the cookie- + * path is a %x2F ("/") character. + * + * @param string $requestPath Path to check against + * + * @return bool + */ + public function matchesPath($requestPath) + { + $cookiePath = $this->getPath(); + + // Match on exact matches or when path is the default empty "/" + if ($cookiePath === '/' || $cookiePath == $requestPath) { + return true; + } + + // Ensure that the cookie-path is a prefix of the request path. + if (0 !== strpos($requestPath, $cookiePath)) { + return false; + } + + // Match if the last character of the cookie-path is "/" + if (substr($cookiePath, -1, 1) === '/') { + return true; + } + + // Match if the first character not included in cookie path is "/" + return substr($requestPath, strlen($cookiePath), 1) === '/'; + } + + /** + * Check if the cookie matches a domain value + * + * @param string $domain Domain to check against + * + * @return bool + */ + public function matchesDomain($domain) + { + // Remove the leading '.' as per spec in RFC 6265. + // http://tools.ietf.org/html/rfc6265#section-5.2.3 + $cookieDomain = ltrim($this->getDomain(), '.'); + + // Domain not set or exact match. + if (!$cookieDomain || !strcasecmp($domain, $cookieDomain)) { + return true; + } + + // Matching the subdomain according to RFC 6265. + // http://tools.ietf.org/html/rfc6265#section-5.1.3 + if (filter_var($domain, FILTER_VALIDATE_IP)) { + return false; + } + + return (bool) preg_match('/\.' . preg_quote($cookieDomain, '/') . '$/', $domain); + } + + /** + * Check if the cookie is expired + * + * @return bool + */ + public function isExpired() + { + return $this->getExpires() !== null && time() > $this->getExpires(); + } + + /** + * Check if the cookie is valid according to RFC 6265 + * + * @return bool|string Returns true if valid or an error message if invalid + */ + public function validate() + { + // Names must not be empty, but can be 0 + $name = $this->getName(); + if (empty($name) && !is_numeric($name)) { + return 'The cookie name must not be empty'; + } + + // Check if any of the invalid characters are present in the cookie name + if (preg_match( + '/[\x00-\x20\x22\x28-\x29\x2c\x2f\x3a-\x40\x5c\x7b\x7d\x7f]/', + $name + )) { + return 'Cookie name must not contain invalid characters: ASCII ' + . 'Control characters (0-31;127), space, tab and the ' + . 'following characters: ()<>@,;:\"/?={}'; + } + + // Value must not be empty, but can be 0 + $value = $this->getValue(); + if (empty($value) && !is_numeric($value)) { + return 'The cookie value must not be empty'; + } + + // Domains must not be empty, but can be 0 + // A "0" is not a valid internet domain, but may be used as server name + // in a private network. + $domain = $this->getDomain(); + if (empty($domain) && !is_numeric($domain)) { + return 'The cookie domain must not be empty'; + } + + return true; + } +} diff --git a/instafeed/vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php b/instafeed/vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php new file mode 100755 index 0000000..427d896 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php @@ -0,0 +1,27 @@ +getStatusCode() + : 0; + parent::__construct($message, $code, $previous); + $this->request = $request; + $this->response = $response; + $this->handlerContext = $handlerContext; + } + + /** + * Wrap non-RequestExceptions with a RequestException + * + * @param RequestInterface $request + * @param \Exception $e + * + * @return RequestException + */ + public static function wrapException(RequestInterface $request, \Exception $e) + { + return $e instanceof RequestException + ? $e + : new RequestException($e->getMessage(), $request, null, $e); + } + + /** + * Factory method to create a new exception with a normalized error message + * + * @param RequestInterface $request Request + * @param ResponseInterface $response Response received + * @param \Exception $previous Previous exception + * @param array $ctx Optional handler context. + * + * @return self + */ + public static function create( + RequestInterface $request, + ResponseInterface $response = null, + \Exception $previous = null, + array $ctx = [] + ) { + if (!$response) { + return new self( + 'Error completing request', + $request, + null, + $previous, + $ctx + ); + } + + $level = (int) floor($response->getStatusCode() / 100); + if ($level === 4) { + $label = 'Client error'; + $className = ClientException::class; + } elseif ($level === 5) { + $label = 'Server error'; + $className = ServerException::class; + } else { + $label = 'Unsuccessful request'; + $className = __CLASS__; + } + + $uri = $request->getUri(); + $uri = static::obfuscateUri($uri); + + // Client Error: `GET /` resulted in a `404 Not Found` response: + // ... (truncated) + $message = sprintf( + '%s: `%s %s` resulted in a `%s %s` response', + $label, + $request->getMethod(), + $uri, + $response->getStatusCode(), + $response->getReasonPhrase() + ); + + $summary = static::getResponseBodySummary($response); + + if ($summary !== null) { + $message .= ":\n{$summary}\n"; + } + + return new $className($message, $request, $response, $previous, $ctx); + } + + /** + * Get a short summary of the response + * + * Will return `null` if the response is not printable. + * + * @param ResponseInterface $response + * + * @return string|null + */ + public static function getResponseBodySummary(ResponseInterface $response) + { + $body = $response->getBody(); + + if (!$body->isSeekable() || !$body->isReadable()) { + return null; + } + + $size = $body->getSize(); + + if ($size === 0) { + return null; + } + + $summary = $body->read(120); + $body->rewind(); + + if ($size > 120) { + $summary .= ' (truncated...)'; + } + + // Matches any printable character, including unicode characters: + // letters, marks, numbers, punctuation, spacing, and separators. + if (preg_match('/[^\pL\pM\pN\pP\pS\pZ\n\r\t]/', $summary)) { + return null; + } + + return $summary; + } + + /** + * Obfuscates URI if there is an username and a password present + * + * @param UriInterface $uri + * + * @return UriInterface + */ + private static function obfuscateUri($uri) + { + $userInfo = $uri->getUserInfo(); + + if (false !== ($pos = strpos($userInfo, ':'))) { + return $uri->withUserInfo(substr($userInfo, 0, $pos), '***'); + } + + return $uri; + } + + /** + * Get the request that caused the exception + * + * @return RequestInterface + */ + public function getRequest() + { + return $this->request; + } + + /** + * Get the associated response + * + * @return ResponseInterface|null + */ + public function getResponse() + { + return $this->response; + } + + /** + * Check if a response was received + * + * @return bool + */ + public function hasResponse() + { + return $this->response !== null; + } + + /** + * Get contextual information about the error from the underlying handler. + * + * The contents of this array will vary depending on which handler you are + * using. It may also be just an empty array. Relying on this data will + * couple you to a specific handler, but can give more debug information + * when needed. + * + * @return array + */ + public function getHandlerContext() + { + return $this->handlerContext; + } +} diff --git a/instafeed/vendor/guzzlehttp/guzzle/src/Exception/SeekException.php b/instafeed/vendor/guzzlehttp/guzzle/src/Exception/SeekException.php new file mode 100755 index 0000000..a77c289 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/guzzle/src/Exception/SeekException.php @@ -0,0 +1,27 @@ +stream = $stream; + $msg = $msg ?: 'Could not seek the stream to position ' . $pos; + parent::__construct($msg); + } + + /** + * @return StreamInterface + */ + public function getStream() + { + return $this->stream; + } +} diff --git a/instafeed/vendor/guzzlehttp/guzzle/src/Exception/ServerException.php b/instafeed/vendor/guzzlehttp/guzzle/src/Exception/ServerException.php new file mode 100755 index 0000000..127094c --- /dev/null +++ b/instafeed/vendor/guzzlehttp/guzzle/src/Exception/ServerException.php @@ -0,0 +1,9 @@ +maxHandles = $maxHandles; + } + + public function create(RequestInterface $request, array $options) + { + if (isset($options['curl']['body_as_string'])) { + $options['_body_as_string'] = $options['curl']['body_as_string']; + unset($options['curl']['body_as_string']); + } + + $easy = new EasyHandle; + $easy->request = $request; + $easy->options = $options; + $conf = $this->getDefaultConf($easy); + $this->applyMethod($easy, $conf); + $this->applyHandlerOptions($easy, $conf); + $this->applyHeaders($easy, $conf); + unset($conf['_headers']); + + // Add handler options from the request configuration options + if (isset($options['curl'])) { + $conf = array_replace($conf, $options['curl']); + } + + $conf[CURLOPT_HEADERFUNCTION] = $this->createHeaderFn($easy); + $easy->handle = $this->handles + ? array_pop($this->handles) + : curl_init(); + curl_setopt_array($easy->handle, $conf); + + return $easy; + } + + public function release(EasyHandle $easy) + { + $resource = $easy->handle; + unset($easy->handle); + + if (count($this->handles) >= $this->maxHandles) { + curl_close($resource); + } else { + // Remove all callback functions as they can hold onto references + // and are not cleaned up by curl_reset. Using curl_setopt_array + // does not work for some reason, so removing each one + // individually. + curl_setopt($resource, CURLOPT_HEADERFUNCTION, null); + curl_setopt($resource, CURLOPT_READFUNCTION, null); + curl_setopt($resource, CURLOPT_WRITEFUNCTION, null); + curl_setopt($resource, CURLOPT_PROGRESSFUNCTION, null); + curl_reset($resource); + $this->handles[] = $resource; + } + } + + /** + * Completes a cURL transaction, either returning a response promise or a + * rejected promise. + * + * @param callable $handler + * @param EasyHandle $easy + * @param CurlFactoryInterface $factory Dictates how the handle is released + * + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public static function finish( + callable $handler, + EasyHandle $easy, + CurlFactoryInterface $factory + ) { + if (isset($easy->options['on_stats'])) { + self::invokeStats($easy); + } + + if (!$easy->response || $easy->errno) { + return self::finishError($handler, $easy, $factory); + } + + // Return the response if it is present and there is no error. + $factory->release($easy); + + // Rewind the body of the response if possible. + $body = $easy->response->getBody(); + if ($body->isSeekable()) { + $body->rewind(); + } + + return new FulfilledPromise($easy->response); + } + + private static function invokeStats(EasyHandle $easy) + { + $curlStats = curl_getinfo($easy->handle); + $curlStats['appconnect_time'] = curl_getinfo($easy->handle, CURLINFO_APPCONNECT_TIME); + $stats = new TransferStats( + $easy->request, + $easy->response, + $curlStats['total_time'], + $easy->errno, + $curlStats + ); + call_user_func($easy->options['on_stats'], $stats); + } + + private static function finishError( + callable $handler, + EasyHandle $easy, + CurlFactoryInterface $factory + ) { + // Get error information and release the handle to the factory. + $ctx = [ + 'errno' => $easy->errno, + 'error' => curl_error($easy->handle), + 'appconnect_time' => curl_getinfo($easy->handle, CURLINFO_APPCONNECT_TIME), + ] + curl_getinfo($easy->handle); + $ctx[self::CURL_VERSION_STR] = curl_version()['version']; + $factory->release($easy); + + // Retry when nothing is present or when curl failed to rewind. + if (empty($easy->options['_err_message']) + && (!$easy->errno || $easy->errno == 65) + ) { + return self::retryFailedRewind($handler, $easy, $ctx); + } + + return self::createRejection($easy, $ctx); + } + + private static function createRejection(EasyHandle $easy, array $ctx) + { + static $connectionErrors = [ + CURLE_OPERATION_TIMEOUTED => true, + CURLE_COULDNT_RESOLVE_HOST => true, + CURLE_COULDNT_CONNECT => true, + CURLE_SSL_CONNECT_ERROR => true, + CURLE_GOT_NOTHING => true, + ]; + + // If an exception was encountered during the onHeaders event, then + // return a rejected promise that wraps that exception. + if ($easy->onHeadersException) { + return \GuzzleHttp\Promise\rejection_for( + new RequestException( + 'An error was encountered during the on_headers event', + $easy->request, + $easy->response, + $easy->onHeadersException, + $ctx + ) + ); + } + if (version_compare($ctx[self::CURL_VERSION_STR], self::LOW_CURL_VERSION_NUMBER)) { + $message = sprintf( + 'cURL error %s: %s (%s)', + $ctx['errno'], + $ctx['error'], + 'see https://curl.haxx.se/libcurl/c/libcurl-errors.html' + ); + } else { + $message = sprintf( + 'cURL error %s: %s (%s) for %s', + $ctx['errno'], + $ctx['error'], + 'see https://curl.haxx.se/libcurl/c/libcurl-errors.html', + $easy->request->getUri() + ); + } + + // Create a connection exception if it was a specific error code. + $error = isset($connectionErrors[$easy->errno]) + ? new ConnectException($message, $easy->request, null, $ctx) + : new RequestException($message, $easy->request, $easy->response, null, $ctx); + + return \GuzzleHttp\Promise\rejection_for($error); + } + + private function getDefaultConf(EasyHandle $easy) + { + $conf = [ + '_headers' => $easy->request->getHeaders(), + CURLOPT_CUSTOMREQUEST => $easy->request->getMethod(), + CURLOPT_URL => (string) $easy->request->getUri()->withFragment(''), + CURLOPT_RETURNTRANSFER => false, + CURLOPT_HEADER => false, + CURLOPT_CONNECTTIMEOUT => 150, + ]; + + if (defined('CURLOPT_PROTOCOLS')) { + $conf[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS; + } + + $version = $easy->request->getProtocolVersion(); + if ($version == 1.1) { + $conf[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_1; + } elseif ($version == 2.0) { + $conf[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_2_0; + } else { + $conf[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_0; + } + + return $conf; + } + + private function applyMethod(EasyHandle $easy, array &$conf) + { + $body = $easy->request->getBody(); + $size = $body->getSize(); + + if ($size === null || $size > 0) { + $this->applyBody($easy->request, $easy->options, $conf); + return; + } + + $method = $easy->request->getMethod(); + if ($method === 'PUT' || $method === 'POST') { + // See http://tools.ietf.org/html/rfc7230#section-3.3.2 + if (!$easy->request->hasHeader('Content-Length')) { + $conf[CURLOPT_HTTPHEADER][] = 'Content-Length: 0'; + } + } elseif ($method === 'HEAD') { + $conf[CURLOPT_NOBODY] = true; + unset( + $conf[CURLOPT_WRITEFUNCTION], + $conf[CURLOPT_READFUNCTION], + $conf[CURLOPT_FILE], + $conf[CURLOPT_INFILE] + ); + } + } + + private function applyBody(RequestInterface $request, array $options, array &$conf) + { + $size = $request->hasHeader('Content-Length') + ? (int) $request->getHeaderLine('Content-Length') + : null; + + // Send the body as a string if the size is less than 1MB OR if the + // [curl][body_as_string] request value is set. + if (($size !== null && $size < 1000000) || + !empty($options['_body_as_string']) + ) { + $conf[CURLOPT_POSTFIELDS] = (string) $request->getBody(); + // Don't duplicate the Content-Length header + $this->removeHeader('Content-Length', $conf); + $this->removeHeader('Transfer-Encoding', $conf); + } else { + $conf[CURLOPT_UPLOAD] = true; + if ($size !== null) { + $conf[CURLOPT_INFILESIZE] = $size; + $this->removeHeader('Content-Length', $conf); + } + $body = $request->getBody(); + if ($body->isSeekable()) { + $body->rewind(); + } + $conf[CURLOPT_READFUNCTION] = function ($ch, $fd, $length) use ($body) { + return $body->read($length); + }; + } + + // If the Expect header is not present, prevent curl from adding it + if (!$request->hasHeader('Expect')) { + $conf[CURLOPT_HTTPHEADER][] = 'Expect:'; + } + + // cURL sometimes adds a content-type by default. Prevent this. + if (!$request->hasHeader('Content-Type')) { + $conf[CURLOPT_HTTPHEADER][] = 'Content-Type:'; + } + } + + private function applyHeaders(EasyHandle $easy, array &$conf) + { + foreach ($conf['_headers'] as $name => $values) { + foreach ($values as $value) { + $value = (string) $value; + if ($value === '') { + // cURL requires a special format for empty headers. + // See https://github.com/guzzle/guzzle/issues/1882 for more details. + $conf[CURLOPT_HTTPHEADER][] = "$name;"; + } else { + $conf[CURLOPT_HTTPHEADER][] = "$name: $value"; + } + } + } + + // Remove the Accept header if one was not set + if (!$easy->request->hasHeader('Accept')) { + $conf[CURLOPT_HTTPHEADER][] = 'Accept:'; + } + } + + /** + * Remove a header from the options array. + * + * @param string $name Case-insensitive header to remove + * @param array $options Array of options to modify + */ + private function removeHeader($name, array &$options) + { + foreach (array_keys($options['_headers']) as $key) { + if (!strcasecmp($key, $name)) { + unset($options['_headers'][$key]); + return; + } + } + } + + private function applyHandlerOptions(EasyHandle $easy, array &$conf) + { + $options = $easy->options; + if (isset($options['verify'])) { + if ($options['verify'] === false) { + unset($conf[CURLOPT_CAINFO]); + $conf[CURLOPT_SSL_VERIFYHOST] = 0; + $conf[CURLOPT_SSL_VERIFYPEER] = false; + } else { + $conf[CURLOPT_SSL_VERIFYHOST] = 2; + $conf[CURLOPT_SSL_VERIFYPEER] = true; + if (is_string($options['verify'])) { + // Throw an error if the file/folder/link path is not valid or doesn't exist. + if (!file_exists($options['verify'])) { + throw new \InvalidArgumentException( + "SSL CA bundle not found: {$options['verify']}" + ); + } + // If it's a directory or a link to a directory use CURLOPT_CAPATH. + // If not, it's probably a file, or a link to a file, so use CURLOPT_CAINFO. + if (is_dir($options['verify']) || + (is_link($options['verify']) && is_dir(readlink($options['verify'])))) { + $conf[CURLOPT_CAPATH] = $options['verify']; + } else { + $conf[CURLOPT_CAINFO] = $options['verify']; + } + } + } + } + + if (!empty($options['decode_content'])) { + $accept = $easy->request->getHeaderLine('Accept-Encoding'); + if ($accept) { + $conf[CURLOPT_ENCODING] = $accept; + } else { + $conf[CURLOPT_ENCODING] = ''; + // Don't let curl send the header over the wire + $conf[CURLOPT_HTTPHEADER][] = 'Accept-Encoding:'; + } + } + + if (isset($options['sink'])) { + $sink = $options['sink']; + if (!is_string($sink)) { + $sink = \GuzzleHttp\Psr7\stream_for($sink); + } elseif (!is_dir(dirname($sink))) { + // Ensure that the directory exists before failing in curl. + throw new \RuntimeException(sprintf( + 'Directory %s does not exist for sink value of %s', + dirname($sink), + $sink + )); + } else { + $sink = new LazyOpenStream($sink, 'w+'); + } + $easy->sink = $sink; + $conf[CURLOPT_WRITEFUNCTION] = function ($ch, $write) use ($sink) { + return $sink->write($write); + }; + } else { + // Use a default temp stream if no sink was set. + $conf[CURLOPT_FILE] = fopen('php://temp', 'w+'); + $easy->sink = Psr7\stream_for($conf[CURLOPT_FILE]); + } + $timeoutRequiresNoSignal = false; + if (isset($options['timeout'])) { + $timeoutRequiresNoSignal |= $options['timeout'] < 1; + $conf[CURLOPT_TIMEOUT_MS] = $options['timeout'] * 1000; + } + + // CURL default value is CURL_IPRESOLVE_WHATEVER + if (isset($options['force_ip_resolve'])) { + if ('v4' === $options['force_ip_resolve']) { + $conf[CURLOPT_IPRESOLVE] = CURL_IPRESOLVE_V4; + } elseif ('v6' === $options['force_ip_resolve']) { + $conf[CURLOPT_IPRESOLVE] = CURL_IPRESOLVE_V6; + } + } + + if (isset($options['connect_timeout'])) { + $timeoutRequiresNoSignal |= $options['connect_timeout'] < 1; + $conf[CURLOPT_CONNECTTIMEOUT_MS] = $options['connect_timeout'] * 1000; + } + + if ($timeoutRequiresNoSignal && strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') { + $conf[CURLOPT_NOSIGNAL] = true; + } + + if (isset($options['proxy'])) { + if (!is_array($options['proxy'])) { + $conf[CURLOPT_PROXY] = $options['proxy']; + } else { + $scheme = $easy->request->getUri()->getScheme(); + if (isset($options['proxy'][$scheme])) { + $host = $easy->request->getUri()->getHost(); + if (!isset($options['proxy']['no']) || + !\GuzzleHttp\is_host_in_noproxy($host, $options['proxy']['no']) + ) { + $conf[CURLOPT_PROXY] = $options['proxy'][$scheme]; + } + } + } + } + + if (isset($options['cert'])) { + $cert = $options['cert']; + if (is_array($cert)) { + $conf[CURLOPT_SSLCERTPASSWD] = $cert[1]; + $cert = $cert[0]; + } + if (!file_exists($cert)) { + throw new \InvalidArgumentException( + "SSL certificate not found: {$cert}" + ); + } + $conf[CURLOPT_SSLCERT] = $cert; + } + + if (isset($options['ssl_key'])) { + $sslKey = $options['ssl_key']; + if (is_array($sslKey)) { + $conf[CURLOPT_SSLKEYPASSWD] = $sslKey[1]; + $sslKey = $sslKey[0]; + } + if (!file_exists($sslKey)) { + throw new \InvalidArgumentException( + "SSL private key not found: {$sslKey}" + ); + } + $conf[CURLOPT_SSLKEY] = $sslKey; + } + + if (isset($options['progress'])) { + $progress = $options['progress']; + if (!is_callable($progress)) { + throw new \InvalidArgumentException( + 'progress client option must be callable' + ); + } + $conf[CURLOPT_NOPROGRESS] = false; + $conf[CURLOPT_PROGRESSFUNCTION] = function () use ($progress) { + $args = func_get_args(); + // PHP 5.5 pushed the handle onto the start of the args + if (is_resource($args[0])) { + array_shift($args); + } + call_user_func_array($progress, $args); + }; + } + + if (!empty($options['debug'])) { + $conf[CURLOPT_STDERR] = \GuzzleHttp\debug_resource($options['debug']); + $conf[CURLOPT_VERBOSE] = true; + } + } + + /** + * This function ensures that a response was set on a transaction. If one + * was not set, then the request is retried if possible. This error + * typically means you are sending a payload, curl encountered a + * "Connection died, retrying a fresh connect" error, tried to rewind the + * stream, and then encountered a "necessary data rewind wasn't possible" + * error, causing the request to be sent through curl_multi_info_read() + * without an error status. + */ + private static function retryFailedRewind( + callable $handler, + EasyHandle $easy, + array $ctx + ) { + try { + // Only rewind if the body has been read from. + $body = $easy->request->getBody(); + if ($body->tell() > 0) { + $body->rewind(); + } + } catch (\RuntimeException $e) { + $ctx['error'] = 'The connection unexpectedly failed without ' + . 'providing an error. The request would have been retried, ' + . 'but attempting to rewind the request body failed. ' + . 'Exception: ' . $e; + return self::createRejection($easy, $ctx); + } + + // Retry no more than 3 times before giving up. + if (!isset($easy->options['_curl_retries'])) { + $easy->options['_curl_retries'] = 1; + } elseif ($easy->options['_curl_retries'] == 2) { + $ctx['error'] = 'The cURL request was retried 3 times ' + . 'and did not succeed. The most likely reason for the failure ' + . 'is that cURL was unable to rewind the body of the request ' + . 'and subsequent retries resulted in the same error. Turn on ' + . 'the debug option to see what went wrong. See ' + . 'https://bugs.php.net/bug.php?id=47204 for more information.'; + return self::createRejection($easy, $ctx); + } else { + $easy->options['_curl_retries']++; + } + + return $handler($easy->request, $easy->options); + } + + private function createHeaderFn(EasyHandle $easy) + { + if (isset($easy->options['on_headers'])) { + $onHeaders = $easy->options['on_headers']; + + if (!is_callable($onHeaders)) { + throw new \InvalidArgumentException('on_headers must be callable'); + } + } else { + $onHeaders = null; + } + + return function ($ch, $h) use ( + $onHeaders, + $easy, + &$startingResponse + ) { + $value = trim($h); + if ($value === '') { + $startingResponse = true; + $easy->createResponse(); + if ($onHeaders !== null) { + try { + $onHeaders($easy->response); + } catch (\Exception $e) { + // Associate the exception with the handle and trigger + // a curl header write error by returning 0. + $easy->onHeadersException = $e; + return -1; + } + } + } elseif ($startingResponse) { + $startingResponse = false; + $easy->headers = [$value]; + } else { + $easy->headers[] = $value; + } + return strlen($h); + }; + } +} diff --git a/instafeed/vendor/guzzlehttp/guzzle/src/Handler/CurlFactoryInterface.php b/instafeed/vendor/guzzlehttp/guzzle/src/Handler/CurlFactoryInterface.php new file mode 100755 index 0000000..b0fc236 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/guzzle/src/Handler/CurlFactoryInterface.php @@ -0,0 +1,27 @@ +factory = isset($options['handle_factory']) + ? $options['handle_factory'] + : new CurlFactory(3); + } + + public function __invoke(RequestInterface $request, array $options) + { + if (isset($options['delay'])) { + usleep($options['delay'] * 1000); + } + + $easy = $this->factory->create($request, $options); + curl_exec($easy->handle); + $easy->errno = curl_errno($easy->handle); + + return CurlFactory::finish($this, $easy, $this->factory); + } +} diff --git a/instafeed/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php b/instafeed/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php new file mode 100755 index 0000000..d829762 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php @@ -0,0 +1,205 @@ +factory = isset($options['handle_factory']) + ? $options['handle_factory'] : new CurlFactory(50); + + if (isset($options['select_timeout'])) { + $this->selectTimeout = $options['select_timeout']; + } elseif ($selectTimeout = getenv('GUZZLE_CURL_SELECT_TIMEOUT')) { + $this->selectTimeout = $selectTimeout; + } else { + $this->selectTimeout = 1; + } + } + + public function __get($name) + { + if ($name === '_mh') { + return $this->_mh = curl_multi_init(); + } + + throw new \BadMethodCallException(); + } + + public function __destruct() + { + if (isset($this->_mh)) { + curl_multi_close($this->_mh); + unset($this->_mh); + } + } + + public function __invoke(RequestInterface $request, array $options) + { + $easy = $this->factory->create($request, $options); + $id = (int) $easy->handle; + + $promise = new Promise( + [$this, 'execute'], + function () use ($id) { + return $this->cancel($id); + } + ); + + $this->addRequest(['easy' => $easy, 'deferred' => $promise]); + + return $promise; + } + + /** + * Ticks the curl event loop. + */ + public function tick() + { + // Add any delayed handles if needed. + if ($this->delays) { + $currentTime = \GuzzleHttp\_current_time(); + foreach ($this->delays as $id => $delay) { + if ($currentTime >= $delay) { + unset($this->delays[$id]); + curl_multi_add_handle( + $this->_mh, + $this->handles[$id]['easy']->handle + ); + } + } + } + + // Step through the task queue which may add additional requests. + P\queue()->run(); + + if ($this->active && + curl_multi_select($this->_mh, $this->selectTimeout) === -1 + ) { + // Perform a usleep if a select returns -1. + // See: https://bugs.php.net/bug.php?id=61141 + usleep(250); + } + + while (curl_multi_exec($this->_mh, $this->active) === CURLM_CALL_MULTI_PERFORM); + + $this->processMessages(); + } + + /** + * Runs until all outstanding connections have completed. + */ + public function execute() + { + $queue = P\queue(); + + while ($this->handles || !$queue->isEmpty()) { + // If there are no transfers, then sleep for the next delay + if (!$this->active && $this->delays) { + usleep($this->timeToNext()); + } + $this->tick(); + } + } + + private function addRequest(array $entry) + { + $easy = $entry['easy']; + $id = (int) $easy->handle; + $this->handles[$id] = $entry; + if (empty($easy->options['delay'])) { + curl_multi_add_handle($this->_mh, $easy->handle); + } else { + $this->delays[$id] = \GuzzleHttp\_current_time() + ($easy->options['delay'] / 1000); + } + } + + /** + * Cancels a handle from sending and removes references to it. + * + * @param int $id Handle ID to cancel and remove. + * + * @return bool True on success, false on failure. + */ + private function cancel($id) + { + // Cannot cancel if it has been processed. + if (!isset($this->handles[$id])) { + return false; + } + + $handle = $this->handles[$id]['easy']->handle; + unset($this->delays[$id], $this->handles[$id]); + curl_multi_remove_handle($this->_mh, $handle); + curl_close($handle); + + return true; + } + + private function processMessages() + { + while ($done = curl_multi_info_read($this->_mh)) { + $id = (int) $done['handle']; + curl_multi_remove_handle($this->_mh, $done['handle']); + + if (!isset($this->handles[$id])) { + // Probably was cancelled. + continue; + } + + $entry = $this->handles[$id]; + unset($this->handles[$id], $this->delays[$id]); + $entry['easy']->errno = $done['result']; + $entry['deferred']->resolve( + CurlFactory::finish( + $this, + $entry['easy'], + $this->factory + ) + ); + } + } + + private function timeToNext() + { + $currentTime = \GuzzleHttp\_current_time(); + $nextTime = PHP_INT_MAX; + foreach ($this->delays as $time) { + if ($time < $nextTime) { + $nextTime = $time; + } + } + + return max(0, $nextTime - $currentTime) * 1000000; + } +} diff --git a/instafeed/vendor/guzzlehttp/guzzle/src/Handler/EasyHandle.php b/instafeed/vendor/guzzlehttp/guzzle/src/Handler/EasyHandle.php new file mode 100755 index 0000000..7754e91 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/guzzle/src/Handler/EasyHandle.php @@ -0,0 +1,92 @@ +headers)) { + throw new \RuntimeException('No headers have been received'); + } + + // HTTP-version SP status-code SP reason-phrase + $startLine = explode(' ', array_shift($this->headers), 3); + $headers = \GuzzleHttp\headers_from_lines($this->headers); + $normalizedKeys = \GuzzleHttp\normalize_header_keys($headers); + + if (!empty($this->options['decode_content']) + && isset($normalizedKeys['content-encoding']) + ) { + $headers['x-encoded-content-encoding'] + = $headers[$normalizedKeys['content-encoding']]; + unset($headers[$normalizedKeys['content-encoding']]); + if (isset($normalizedKeys['content-length'])) { + $headers['x-encoded-content-length'] + = $headers[$normalizedKeys['content-length']]; + + $bodyLength = (int) $this->sink->getSize(); + if ($bodyLength) { + $headers[$normalizedKeys['content-length']] = $bodyLength; + } else { + unset($headers[$normalizedKeys['content-length']]); + } + } + } + + // Attach a response to the easy handle with the parsed headers. + $this->response = new Response( + $startLine[1], + $headers, + $this->sink, + substr($startLine[0], 5), + isset($startLine[2]) ? (string) $startLine[2] : null + ); + } + + public function __get($name) + { + $msg = $name === 'handle' + ? 'The EasyHandle has been released' + : 'Invalid property: ' . $name; + throw new \BadMethodCallException($msg); + } +} diff --git a/instafeed/vendor/guzzlehttp/guzzle/src/Handler/MockHandler.php b/instafeed/vendor/guzzlehttp/guzzle/src/Handler/MockHandler.php new file mode 100755 index 0000000..d5c449c --- /dev/null +++ b/instafeed/vendor/guzzlehttp/guzzle/src/Handler/MockHandler.php @@ -0,0 +1,190 @@ +onFulfilled = $onFulfilled; + $this->onRejected = $onRejected; + + if ($queue) { + call_user_func_array([$this, 'append'], $queue); + } + } + + public function __invoke(RequestInterface $request, array $options) + { + if (!$this->queue) { + throw new \OutOfBoundsException('Mock queue is empty'); + } + + if (isset($options['delay'])) { + usleep($options['delay'] * 1000); + } + + $this->lastRequest = $request; + $this->lastOptions = $options; + $response = array_shift($this->queue); + + if (isset($options['on_headers'])) { + if (!is_callable($options['on_headers'])) { + throw new \InvalidArgumentException('on_headers must be callable'); + } + try { + $options['on_headers']($response); + } catch (\Exception $e) { + $msg = 'An error was encountered during the on_headers event'; + $response = new RequestException($msg, $request, $response, $e); + } + } + + if (is_callable($response)) { + $response = call_user_func($response, $request, $options); + } + + $response = $response instanceof \Exception + ? \GuzzleHttp\Promise\rejection_for($response) + : \GuzzleHttp\Promise\promise_for($response); + + return $response->then( + function ($value) use ($request, $options) { + $this->invokeStats($request, $options, $value); + if ($this->onFulfilled) { + call_user_func($this->onFulfilled, $value); + } + if (isset($options['sink'])) { + $contents = (string) $value->getBody(); + $sink = $options['sink']; + + if (is_resource($sink)) { + fwrite($sink, $contents); + } elseif (is_string($sink)) { + file_put_contents($sink, $contents); + } elseif ($sink instanceof \Psr\Http\Message\StreamInterface) { + $sink->write($contents); + } + } + + return $value; + }, + function ($reason) use ($request, $options) { + $this->invokeStats($request, $options, null, $reason); + if ($this->onRejected) { + call_user_func($this->onRejected, $reason); + } + return \GuzzleHttp\Promise\rejection_for($reason); + } + ); + } + + /** + * Adds one or more variadic requests, exceptions, callables, or promises + * to the queue. + */ + public function append() + { + foreach (func_get_args() as $value) { + if ($value instanceof ResponseInterface + || $value instanceof \Exception + || $value instanceof PromiseInterface + || is_callable($value) + ) { + $this->queue[] = $value; + } else { + throw new \InvalidArgumentException('Expected a response or ' + . 'exception. Found ' . \GuzzleHttp\describe_type($value)); + } + } + } + + /** + * Get the last received request. + * + * @return RequestInterface + */ + public function getLastRequest() + { + return $this->lastRequest; + } + + /** + * Get the last received request options. + * + * @return array + */ + public function getLastOptions() + { + return $this->lastOptions; + } + + /** + * Returns the number of remaining items in the queue. + * + * @return int + */ + public function count() + { + return count($this->queue); + } + + private function invokeStats( + RequestInterface $request, + array $options, + ResponseInterface $response = null, + $reason = null + ) { + if (isset($options['on_stats'])) { + $transferTime = isset($options['transfer_time']) ? $options['transfer_time'] : 0; + $stats = new TransferStats($request, $response, $transferTime, $reason); + call_user_func($options['on_stats'], $stats); + } + } +} diff --git a/instafeed/vendor/guzzlehttp/guzzle/src/Handler/Proxy.php b/instafeed/vendor/guzzlehttp/guzzle/src/Handler/Proxy.php new file mode 100755 index 0000000..f8b00be --- /dev/null +++ b/instafeed/vendor/guzzlehttp/guzzle/src/Handler/Proxy.php @@ -0,0 +1,55 @@ +withoutHeader('Expect'); + + // Append a content-length header if body size is zero to match + // cURL's behavior. + if (0 === $request->getBody()->getSize()) { + $request = $request->withHeader('Content-Length', '0'); + } + + return $this->createResponse( + $request, + $options, + $this->createStream($request, $options), + $startTime + ); + } catch (\InvalidArgumentException $e) { + throw $e; + } catch (\Exception $e) { + // Determine if the error was a networking error. + $message = $e->getMessage(); + // This list can probably get more comprehensive. + if (strpos($message, 'getaddrinfo') // DNS lookup failed + || strpos($message, 'Connection refused') + || strpos($message, "couldn't connect to host") // error on HHVM + || strpos($message, "connection attempt failed") + ) { + $e = new ConnectException($e->getMessage(), $request, $e); + } + $e = RequestException::wrapException($request, $e); + $this->invokeStats($options, $request, $startTime, null, $e); + + return \GuzzleHttp\Promise\rejection_for($e); + } + } + + private function invokeStats( + array $options, + RequestInterface $request, + $startTime, + ResponseInterface $response = null, + $error = null + ) { + if (isset($options['on_stats'])) { + $stats = new TransferStats( + $request, + $response, + \GuzzleHttp\_current_time() - $startTime, + $error, + [] + ); + call_user_func($options['on_stats'], $stats); + } + } + + private function createResponse( + RequestInterface $request, + array $options, + $stream, + $startTime + ) { + $hdrs = $this->lastHeaders; + $this->lastHeaders = []; + $parts = explode(' ', array_shift($hdrs), 3); + $ver = explode('/', $parts[0])[1]; + $status = $parts[1]; + $reason = isset($parts[2]) ? $parts[2] : null; + $headers = \GuzzleHttp\headers_from_lines($hdrs); + list($stream, $headers) = $this->checkDecode($options, $headers, $stream); + $stream = Psr7\stream_for($stream); + $sink = $stream; + + if (strcasecmp('HEAD', $request->getMethod())) { + $sink = $this->createSink($stream, $options); + } + + $response = new Psr7\Response($status, $headers, $sink, $ver, $reason); + + if (isset($options['on_headers'])) { + try { + $options['on_headers']($response); + } catch (\Exception $e) { + $msg = 'An error was encountered during the on_headers event'; + $ex = new RequestException($msg, $request, $response, $e); + return \GuzzleHttp\Promise\rejection_for($ex); + } + } + + // Do not drain when the request is a HEAD request because they have + // no body. + if ($sink !== $stream) { + $this->drain( + $stream, + $sink, + $response->getHeaderLine('Content-Length') + ); + } + + $this->invokeStats($options, $request, $startTime, $response, null); + + return new FulfilledPromise($response); + } + + private function createSink(StreamInterface $stream, array $options) + { + if (!empty($options['stream'])) { + return $stream; + } + + $sink = isset($options['sink']) + ? $options['sink'] + : fopen('php://temp', 'r+'); + + return is_string($sink) + ? new Psr7\LazyOpenStream($sink, 'w+') + : Psr7\stream_for($sink); + } + + private function checkDecode(array $options, array $headers, $stream) + { + // Automatically decode responses when instructed. + if (!empty($options['decode_content'])) { + $normalizedKeys = \GuzzleHttp\normalize_header_keys($headers); + if (isset($normalizedKeys['content-encoding'])) { + $encoding = $headers[$normalizedKeys['content-encoding']]; + if ($encoding[0] === 'gzip' || $encoding[0] === 'deflate') { + $stream = new Psr7\InflateStream( + Psr7\stream_for($stream) + ); + $headers['x-encoded-content-encoding'] + = $headers[$normalizedKeys['content-encoding']]; + // Remove content-encoding header + unset($headers[$normalizedKeys['content-encoding']]); + // Fix content-length header + if (isset($normalizedKeys['content-length'])) { + $headers['x-encoded-content-length'] + = $headers[$normalizedKeys['content-length']]; + + $length = (int) $stream->getSize(); + if ($length === 0) { + unset($headers[$normalizedKeys['content-length']]); + } else { + $headers[$normalizedKeys['content-length']] = [$length]; + } + } + } + } + } + + return [$stream, $headers]; + } + + /** + * Drains the source stream into the "sink" client option. + * + * @param StreamInterface $source + * @param StreamInterface $sink + * @param string $contentLength Header specifying the amount of + * data to read. + * + * @return StreamInterface + * @throws \RuntimeException when the sink option is invalid. + */ + private function drain( + StreamInterface $source, + StreamInterface $sink, + $contentLength + ) { + // If a content-length header is provided, then stop reading once + // that number of bytes has been read. This can prevent infinitely + // reading from a stream when dealing with servers that do not honor + // Connection: Close headers. + Psr7\copy_to_stream( + $source, + $sink, + (strlen($contentLength) > 0 && (int) $contentLength > 0) ? (int) $contentLength : -1 + ); + + $sink->seek(0); + $source->close(); + + return $sink; + } + + /** + * Create a resource and check to ensure it was created successfully + * + * @param callable $callback Callable that returns stream resource + * + * @return resource + * @throws \RuntimeException on error + */ + private function createResource(callable $callback) + { + $errors = null; + set_error_handler(function ($_, $msg, $file, $line) use (&$errors) { + $errors[] = [ + 'message' => $msg, + 'file' => $file, + 'line' => $line + ]; + return true; + }); + + $resource = $callback(); + restore_error_handler(); + + if (!$resource) { + $message = 'Error creating resource: '; + foreach ($errors as $err) { + foreach ($err as $key => $value) { + $message .= "[$key] $value" . PHP_EOL; + } + } + throw new \RuntimeException(trim($message)); + } + + return $resource; + } + + private function createStream(RequestInterface $request, array $options) + { + static $methods; + if (!$methods) { + $methods = array_flip(get_class_methods(__CLASS__)); + } + + // HTTP/1.1 streams using the PHP stream wrapper require a + // Connection: close header + if ($request->getProtocolVersion() == '1.1' + && !$request->hasHeader('Connection') + ) { + $request = $request->withHeader('Connection', 'close'); + } + + // Ensure SSL is verified by default + if (!isset($options['verify'])) { + $options['verify'] = true; + } + + $params = []; + $context = $this->getDefaultContext($request); + + if (isset($options['on_headers']) && !is_callable($options['on_headers'])) { + throw new \InvalidArgumentException('on_headers must be callable'); + } + + if (!empty($options)) { + foreach ($options as $key => $value) { + $method = "add_{$key}"; + if (isset($methods[$method])) { + $this->{$method}($request, $context, $value, $params); + } + } + } + + if (isset($options['stream_context'])) { + if (!is_array($options['stream_context'])) { + throw new \InvalidArgumentException('stream_context must be an array'); + } + $context = array_replace_recursive( + $context, + $options['stream_context'] + ); + } + + // Microsoft NTLM authentication only supported with curl handler + if (isset($options['auth']) + && is_array($options['auth']) + && isset($options['auth'][2]) + && 'ntlm' == $options['auth'][2] + ) { + throw new \InvalidArgumentException('Microsoft NTLM authentication only supported with curl handler'); + } + + $uri = $this->resolveHost($request, $options); + + $context = $this->createResource( + function () use ($context, $params) { + return stream_context_create($context, $params); + } + ); + + return $this->createResource( + function () use ($uri, &$http_response_header, $context, $options) { + $resource = fopen((string) $uri, 'r', null, $context); + $this->lastHeaders = $http_response_header; + + if (isset($options['read_timeout'])) { + $readTimeout = $options['read_timeout']; + $sec = (int) $readTimeout; + $usec = ($readTimeout - $sec) * 100000; + stream_set_timeout($resource, $sec, $usec); + } + + return $resource; + } + ); + } + + private function resolveHost(RequestInterface $request, array $options) + { + $uri = $request->getUri(); + + if (isset($options['force_ip_resolve']) && !filter_var($uri->getHost(), FILTER_VALIDATE_IP)) { + if ('v4' === $options['force_ip_resolve']) { + $records = dns_get_record($uri->getHost(), DNS_A); + if (!isset($records[0]['ip'])) { + throw new ConnectException( + sprintf( + "Could not resolve IPv4 address for host '%s'", + $uri->getHost() + ), + $request + ); + } + $uri = $uri->withHost($records[0]['ip']); + } elseif ('v6' === $options['force_ip_resolve']) { + $records = dns_get_record($uri->getHost(), DNS_AAAA); + if (!isset($records[0]['ipv6'])) { + throw new ConnectException( + sprintf( + "Could not resolve IPv6 address for host '%s'", + $uri->getHost() + ), + $request + ); + } + $uri = $uri->withHost('[' . $records[0]['ipv6'] . ']'); + } + } + + return $uri; + } + + private function getDefaultContext(RequestInterface $request) + { + $headers = ''; + foreach ($request->getHeaders() as $name => $value) { + foreach ($value as $val) { + $headers .= "$name: $val\r\n"; + } + } + + $context = [ + 'http' => [ + 'method' => $request->getMethod(), + 'header' => $headers, + 'protocol_version' => $request->getProtocolVersion(), + 'ignore_errors' => true, + 'follow_location' => 0, + ], + ]; + + $body = (string) $request->getBody(); + + if (!empty($body)) { + $context['http']['content'] = $body; + // Prevent the HTTP handler from adding a Content-Type header. + if (!$request->hasHeader('Content-Type')) { + $context['http']['header'] .= "Content-Type:\r\n"; + } + } + + $context['http']['header'] = rtrim($context['http']['header']); + + return $context; + } + + private function add_proxy(RequestInterface $request, &$options, $value, &$params) + { + if (!is_array($value)) { + $options['http']['proxy'] = $value; + } else { + $scheme = $request->getUri()->getScheme(); + if (isset($value[$scheme])) { + if (!isset($value['no']) + || !\GuzzleHttp\is_host_in_noproxy( + $request->getUri()->getHost(), + $value['no'] + ) + ) { + $options['http']['proxy'] = $value[$scheme]; + } + } + } + } + + private function add_timeout(RequestInterface $request, &$options, $value, &$params) + { + if ($value > 0) { + $options['http']['timeout'] = $value; + } + } + + private function add_verify(RequestInterface $request, &$options, $value, &$params) + { + if ($value === true) { + // PHP 5.6 or greater will find the system cert by default. When + // < 5.6, use the Guzzle bundled cacert. + if (PHP_VERSION_ID < 50600) { + $options['ssl']['cafile'] = \GuzzleHttp\default_ca_bundle(); + } + } elseif (is_string($value)) { + $options['ssl']['cafile'] = $value; + if (!file_exists($value)) { + throw new \RuntimeException("SSL CA bundle not found: $value"); + } + } elseif ($value === false) { + $options['ssl']['verify_peer'] = false; + $options['ssl']['verify_peer_name'] = false; + return; + } else { + throw new \InvalidArgumentException('Invalid verify request option'); + } + + $options['ssl']['verify_peer'] = true; + $options['ssl']['verify_peer_name'] = true; + $options['ssl']['allow_self_signed'] = false; + } + + private function add_cert(RequestInterface $request, &$options, $value, &$params) + { + if (is_array($value)) { + $options['ssl']['passphrase'] = $value[1]; + $value = $value[0]; + } + + if (!file_exists($value)) { + throw new \RuntimeException("SSL certificate not found: {$value}"); + } + + $options['ssl']['local_cert'] = $value; + } + + private function add_progress(RequestInterface $request, &$options, $value, &$params) + { + $this->addNotification( + $params, + function ($code, $a, $b, $c, $transferred, $total) use ($value) { + if ($code == STREAM_NOTIFY_PROGRESS) { + $value($total, $transferred, null, null); + } + } + ); + } + + private function add_debug(RequestInterface $request, &$options, $value, &$params) + { + if ($value === false) { + return; + } + + static $map = [ + STREAM_NOTIFY_CONNECT => 'CONNECT', + STREAM_NOTIFY_AUTH_REQUIRED => 'AUTH_REQUIRED', + STREAM_NOTIFY_AUTH_RESULT => 'AUTH_RESULT', + STREAM_NOTIFY_MIME_TYPE_IS => 'MIME_TYPE_IS', + STREAM_NOTIFY_FILE_SIZE_IS => 'FILE_SIZE_IS', + STREAM_NOTIFY_REDIRECTED => 'REDIRECTED', + STREAM_NOTIFY_PROGRESS => 'PROGRESS', + STREAM_NOTIFY_FAILURE => 'FAILURE', + STREAM_NOTIFY_COMPLETED => 'COMPLETED', + STREAM_NOTIFY_RESOLVE => 'RESOLVE', + ]; + static $args = ['severity', 'message', 'message_code', + 'bytes_transferred', 'bytes_max']; + + $value = \GuzzleHttp\debug_resource($value); + $ident = $request->getMethod() . ' ' . $request->getUri()->withFragment(''); + $this->addNotification( + $params, + function () use ($ident, $value, $map, $args) { + $passed = func_get_args(); + $code = array_shift($passed); + fprintf($value, '<%s> [%s] ', $ident, $map[$code]); + foreach (array_filter($passed) as $i => $v) { + fwrite($value, $args[$i] . ': "' . $v . '" '); + } + fwrite($value, "\n"); + } + ); + } + + private function addNotification(array &$params, callable $notify) + { + // Wrap the existing function if needed. + if (!isset($params['notification'])) { + $params['notification'] = $notify; + } else { + $params['notification'] = $this->callArray([ + $params['notification'], + $notify + ]); + } + } + + private function callArray(array $functions) + { + return function () use ($functions) { + $args = func_get_args(); + foreach ($functions as $fn) { + call_user_func_array($fn, $args); + } + }; + } +} diff --git a/instafeed/vendor/guzzlehttp/guzzle/src/HandlerStack.php b/instafeed/vendor/guzzlehttp/guzzle/src/HandlerStack.php new file mode 100755 index 0000000..f001686 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/guzzle/src/HandlerStack.php @@ -0,0 +1,273 @@ +push(Middleware::httpErrors(), 'http_errors'); + $stack->push(Middleware::redirect(), 'allow_redirects'); + $stack->push(Middleware::cookies(), 'cookies'); + $stack->push(Middleware::prepareBody(), 'prepare_body'); + + return $stack; + } + + /** + * @param callable $handler Underlying HTTP handler. + */ + public function __construct(callable $handler = null) + { + $this->handler = $handler; + } + + /** + * Invokes the handler stack as a composed handler + * + * @param RequestInterface $request + * @param array $options + */ + public function __invoke(RequestInterface $request, array $options) + { + $handler = $this->resolve(); + + return $handler($request, $options); + } + + /** + * Dumps a string representation of the stack. + * + * @return string + */ + public function __toString() + { + $depth = 0; + $stack = []; + if ($this->handler) { + $stack[] = "0) Handler: " . $this->debugCallable($this->handler); + } + + $result = ''; + foreach (array_reverse($this->stack) as $tuple) { + $depth++; + $str = "{$depth}) Name: '{$tuple[1]}', "; + $str .= "Function: " . $this->debugCallable($tuple[0]); + $result = "> {$str}\n{$result}"; + $stack[] = $str; + } + + foreach (array_keys($stack) as $k) { + $result .= "< {$stack[$k]}\n"; + } + + return $result; + } + + /** + * Set the HTTP handler that actually returns a promise. + * + * @param callable $handler Accepts a request and array of options and + * returns a Promise. + */ + public function setHandler(callable $handler) + { + $this->handler = $handler; + $this->cached = null; + } + + /** + * Returns true if the builder has a handler. + * + * @return bool + */ + public function hasHandler() + { + return (bool) $this->handler; + } + + /** + * Unshift a middleware to the bottom of the stack. + * + * @param callable $middleware Middleware function + * @param string $name Name to register for this middleware. + */ + public function unshift(callable $middleware, $name = null) + { + array_unshift($this->stack, [$middleware, $name]); + $this->cached = null; + } + + /** + * Push a middleware to the top of the stack. + * + * @param callable $middleware Middleware function + * @param string $name Name to register for this middleware. + */ + public function push(callable $middleware, $name = '') + { + $this->stack[] = [$middleware, $name]; + $this->cached = null; + } + + /** + * Add a middleware before another middleware by name. + * + * @param string $findName Middleware to find + * @param callable $middleware Middleware function + * @param string $withName Name to register for this middleware. + */ + public function before($findName, callable $middleware, $withName = '') + { + $this->splice($findName, $withName, $middleware, true); + } + + /** + * Add a middleware after another middleware by name. + * + * @param string $findName Middleware to find + * @param callable $middleware Middleware function + * @param string $withName Name to register for this middleware. + */ + public function after($findName, callable $middleware, $withName = '') + { + $this->splice($findName, $withName, $middleware, false); + } + + /** + * Remove a middleware by instance or name from the stack. + * + * @param callable|string $remove Middleware to remove by instance or name. + */ + public function remove($remove) + { + $this->cached = null; + $idx = is_callable($remove) ? 0 : 1; + $this->stack = array_values(array_filter( + $this->stack, + function ($tuple) use ($idx, $remove) { + return $tuple[$idx] !== $remove; + } + )); + } + + /** + * Compose the middleware and handler into a single callable function. + * + * @return callable + */ + public function resolve() + { + if (!$this->cached) { + if (!($prev = $this->handler)) { + throw new \LogicException('No handler has been specified'); + } + + foreach (array_reverse($this->stack) as $fn) { + $prev = $fn[0]($prev); + } + + $this->cached = $prev; + } + + return $this->cached; + } + + /** + * @param string $name + * @return int + */ + private function findByName($name) + { + foreach ($this->stack as $k => $v) { + if ($v[1] === $name) { + return $k; + } + } + + throw new \InvalidArgumentException("Middleware not found: $name"); + } + + /** + * Splices a function into the middleware list at a specific position. + * + * @param string $findName + * @param string $withName + * @param callable $middleware + * @param bool $before + */ + private function splice($findName, $withName, callable $middleware, $before) + { + $this->cached = null; + $idx = $this->findByName($findName); + $tuple = [$middleware, $withName]; + + if ($before) { + if ($idx === 0) { + array_unshift($this->stack, $tuple); + } else { + $replacement = [$tuple, $this->stack[$idx]]; + array_splice($this->stack, $idx, 1, $replacement); + } + } elseif ($idx === count($this->stack) - 1) { + $this->stack[] = $tuple; + } else { + $replacement = [$this->stack[$idx], $tuple]; + array_splice($this->stack, $idx, 1, $replacement); + } + } + + /** + * Provides a debug string for a given callable. + * + * @param array|callable $fn Function to write as a string. + * + * @return string + */ + private function debugCallable($fn) + { + if (is_string($fn)) { + return "callable({$fn})"; + } + + if (is_array($fn)) { + return is_string($fn[0]) + ? "callable({$fn[0]}::{$fn[1]})" + : "callable(['" . get_class($fn[0]) . "', '{$fn[1]}'])"; + } + + return 'callable(' . spl_object_hash($fn) . ')'; + } +} diff --git a/instafeed/vendor/guzzlehttp/guzzle/src/MessageFormatter.php b/instafeed/vendor/guzzlehttp/guzzle/src/MessageFormatter.php new file mode 100755 index 0000000..663ac73 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/guzzle/src/MessageFormatter.php @@ -0,0 +1,180 @@ +>>>>>>>\n{request}\n<<<<<<<<\n{response}\n--------\n{error}"; + const SHORT = '[{ts}] "{method} {target} HTTP/{version}" {code}'; + + /** @var string Template used to format log messages */ + private $template; + + /** + * @param string $template Log message template + */ + public function __construct($template = self::CLF) + { + $this->template = $template ?: self::CLF; + } + + /** + * Returns a formatted message string. + * + * @param RequestInterface $request Request that was sent + * @param ResponseInterface $response Response that was received + * @param \Exception $error Exception that was received + * + * @return string + */ + public function format( + RequestInterface $request, + ResponseInterface $response = null, + \Exception $error = null + ) { + $cache = []; + + return preg_replace_callback( + '/{\s*([A-Za-z_\-\.0-9]+)\s*}/', + function (array $matches) use ($request, $response, $error, &$cache) { + if (isset($cache[$matches[1]])) { + return $cache[$matches[1]]; + } + + $result = ''; + switch ($matches[1]) { + case 'request': + $result = Psr7\str($request); + break; + case 'response': + $result = $response ? Psr7\str($response) : ''; + break; + case 'req_headers': + $result = trim($request->getMethod() + . ' ' . $request->getRequestTarget()) + . ' HTTP/' . $request->getProtocolVersion() . "\r\n" + . $this->headers($request); + break; + case 'res_headers': + $result = $response ? + sprintf( + 'HTTP/%s %d %s', + $response->getProtocolVersion(), + $response->getStatusCode(), + $response->getReasonPhrase() + ) . "\r\n" . $this->headers($response) + : 'NULL'; + break; + case 'req_body': + $result = $request->getBody(); + break; + case 'res_body': + $result = $response ? $response->getBody() : 'NULL'; + break; + case 'ts': + case 'date_iso_8601': + $result = gmdate('c'); + break; + case 'date_common_log': + $result = date('d/M/Y:H:i:s O'); + break; + case 'method': + $result = $request->getMethod(); + break; + case 'version': + $result = $request->getProtocolVersion(); + break; + case 'uri': + case 'url': + $result = $request->getUri(); + break; + case 'target': + $result = $request->getRequestTarget(); + break; + case 'req_version': + $result = $request->getProtocolVersion(); + break; + case 'res_version': + $result = $response + ? $response->getProtocolVersion() + : 'NULL'; + break; + case 'host': + $result = $request->getHeaderLine('Host'); + break; + case 'hostname': + $result = gethostname(); + break; + case 'code': + $result = $response ? $response->getStatusCode() : 'NULL'; + break; + case 'phrase': + $result = $response ? $response->getReasonPhrase() : 'NULL'; + break; + case 'error': + $result = $error ? $error->getMessage() : 'NULL'; + break; + default: + // handle prefixed dynamic headers + if (strpos($matches[1], 'req_header_') === 0) { + $result = $request->getHeaderLine(substr($matches[1], 11)); + } elseif (strpos($matches[1], 'res_header_') === 0) { + $result = $response + ? $response->getHeaderLine(substr($matches[1], 11)) + : 'NULL'; + } + } + + $cache[$matches[1]] = $result; + return $result; + }, + $this->template + ); + } + + private function headers(MessageInterface $message) + { + $result = ''; + foreach ($message->getHeaders() as $name => $values) { + $result .= $name . ': ' . implode(', ', $values) . "\r\n"; + } + + return trim($result); + } +} diff --git a/instafeed/vendor/guzzlehttp/guzzle/src/Middleware.php b/instafeed/vendor/guzzlehttp/guzzle/src/Middleware.php new file mode 100755 index 0000000..bffc197 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/guzzle/src/Middleware.php @@ -0,0 +1,254 @@ +withCookieHeader($request); + return $handler($request, $options) + ->then( + function ($response) use ($cookieJar, $request) { + $cookieJar->extractCookies($request, $response); + return $response; + } + ); + }; + }; + } + + /** + * Middleware that throws exceptions for 4xx or 5xx responses when the + * "http_error" request option is set to true. + * + * @return callable Returns a function that accepts the next handler. + */ + public static function httpErrors() + { + return function (callable $handler) { + return function ($request, array $options) use ($handler) { + if (empty($options['http_errors'])) { + return $handler($request, $options); + } + return $handler($request, $options)->then( + function (ResponseInterface $response) use ($request) { + $code = $response->getStatusCode(); + if ($code < 400) { + return $response; + } + throw RequestException::create($request, $response); + } + ); + }; + }; + } + + /** + * Middleware that pushes history data to an ArrayAccess container. + * + * @param array|\ArrayAccess $container Container to hold the history (by reference). + * + * @return callable Returns a function that accepts the next handler. + * @throws \InvalidArgumentException if container is not an array or ArrayAccess. + */ + public static function history(&$container) + { + if (!is_array($container) && !$container instanceof \ArrayAccess) { + throw new \InvalidArgumentException('history container must be an array or object implementing ArrayAccess'); + } + + return function (callable $handler) use (&$container) { + return function ($request, array $options) use ($handler, &$container) { + return $handler($request, $options)->then( + function ($value) use ($request, &$container, $options) { + $container[] = [ + 'request' => $request, + 'response' => $value, + 'error' => null, + 'options' => $options + ]; + return $value; + }, + function ($reason) use ($request, &$container, $options) { + $container[] = [ + 'request' => $request, + 'response' => null, + 'error' => $reason, + 'options' => $options + ]; + return \GuzzleHttp\Promise\rejection_for($reason); + } + ); + }; + }; + } + + /** + * Middleware that invokes a callback before and after sending a request. + * + * The provided listener cannot modify or alter the response. It simply + * "taps" into the chain to be notified before returning the promise. The + * before listener accepts a request and options array, and the after + * listener accepts a request, options array, and response promise. + * + * @param callable $before Function to invoke before forwarding the request. + * @param callable $after Function invoked after forwarding. + * + * @return callable Returns a function that accepts the next handler. + */ + public static function tap(callable $before = null, callable $after = null) + { + return function (callable $handler) use ($before, $after) { + return function ($request, array $options) use ($handler, $before, $after) { + if ($before) { + $before($request, $options); + } + $response = $handler($request, $options); + if ($after) { + $after($request, $options, $response); + } + return $response; + }; + }; + } + + /** + * Middleware that handles request redirects. + * + * @return callable Returns a function that accepts the next handler. + */ + public static function redirect() + { + return function (callable $handler) { + return new RedirectMiddleware($handler); + }; + } + + /** + * Middleware that retries requests based on the boolean result of + * invoking the provided "decider" function. + * + * If no delay function is provided, a simple implementation of exponential + * backoff will be utilized. + * + * @param callable $decider Function that accepts the number of retries, + * a request, [response], and [exception] and + * returns true if the request is to be retried. + * @param callable $delay Function that accepts the number of retries and + * returns the number of milliseconds to delay. + * + * @return callable Returns a function that accepts the next handler. + */ + public static function retry(callable $decider, callable $delay = null) + { + return function (callable $handler) use ($decider, $delay) { + return new RetryMiddleware($decider, $handler, $delay); + }; + } + + /** + * Middleware that logs requests, responses, and errors using a message + * formatter. + * + * @param LoggerInterface $logger Logs messages. + * @param MessageFormatter $formatter Formatter used to create message strings. + * @param string $logLevel Level at which to log requests. + * + * @return callable Returns a function that accepts the next handler. + */ + public static function log(LoggerInterface $logger, MessageFormatter $formatter, $logLevel = 'info' /* \Psr\Log\LogLevel::INFO */) + { + return function (callable $handler) use ($logger, $formatter, $logLevel) { + return function ($request, array $options) use ($handler, $logger, $formatter, $logLevel) { + return $handler($request, $options)->then( + function ($response) use ($logger, $request, $formatter, $logLevel) { + $message = $formatter->format($request, $response); + $logger->log($logLevel, $message); + return $response; + }, + function ($reason) use ($logger, $request, $formatter) { + $response = $reason instanceof RequestException + ? $reason->getResponse() + : null; + $message = $formatter->format($request, $response, $reason); + $logger->notice($message); + return \GuzzleHttp\Promise\rejection_for($reason); + } + ); + }; + }; + } + + /** + * This middleware adds a default content-type if possible, a default + * content-length or transfer-encoding header, and the expect header. + * + * @return callable + */ + public static function prepareBody() + { + return function (callable $handler) { + return new PrepareBodyMiddleware($handler); + }; + } + + /** + * Middleware that applies a map function to the request before passing to + * the next handler. + * + * @param callable $fn Function that accepts a RequestInterface and returns + * a RequestInterface. + * @return callable + */ + public static function mapRequest(callable $fn) + { + return function (callable $handler) use ($fn) { + return function ($request, array $options) use ($handler, $fn) { + return $handler($fn($request), $options); + }; + }; + } + + /** + * Middleware that applies a map function to the resolved promise's + * response. + * + * @param callable $fn Function that accepts a ResponseInterface and + * returns a ResponseInterface. + * @return callable + */ + public static function mapResponse(callable $fn) + { + return function (callable $handler) use ($fn) { + return function ($request, array $options) use ($handler, $fn) { + return $handler($request, $options)->then($fn); + }; + }; + } +} diff --git a/instafeed/vendor/guzzlehttp/guzzle/src/Pool.php b/instafeed/vendor/guzzlehttp/guzzle/src/Pool.php new file mode 100755 index 0000000..05c854a --- /dev/null +++ b/instafeed/vendor/guzzlehttp/guzzle/src/Pool.php @@ -0,0 +1,123 @@ + $rfn) { + if ($rfn instanceof RequestInterface) { + yield $key => $client->sendAsync($rfn, $opts); + } elseif (is_callable($rfn)) { + yield $key => $rfn($opts); + } else { + throw new \InvalidArgumentException('Each value yielded by ' + . 'the iterator must be a Psr7\Http\Message\RequestInterface ' + . 'or a callable that returns a promise that fulfills ' + . 'with a Psr7\Message\Http\ResponseInterface object.'); + } + } + }; + + $this->each = new EachPromise($requests(), $config); + } + + public function promise() + { + return $this->each->promise(); + } + + /** + * Sends multiple requests concurrently and returns an array of responses + * and exceptions that uses the same ordering as the provided requests. + * + * IMPORTANT: This method keeps every request and response in memory, and + * as such, is NOT recommended when sending a large number or an + * indeterminate number of requests concurrently. + * + * @param ClientInterface $client Client used to send the requests + * @param array|\Iterator $requests Requests to send concurrently. + * @param array $options Passes through the options available in + * {@see GuzzleHttp\Pool::__construct} + * + * @return array Returns an array containing the response or an exception + * in the same order that the requests were sent. + * @throws \InvalidArgumentException if the event format is incorrect. + */ + public static function batch( + ClientInterface $client, + $requests, + array $options = [] + ) { + $res = []; + self::cmpCallback($options, 'fulfilled', $res); + self::cmpCallback($options, 'rejected', $res); + $pool = new static($client, $requests, $options); + $pool->promise()->wait(); + ksort($res); + + return $res; + } + + private static function cmpCallback(array &$options, $name, array &$results) + { + if (!isset($options[$name])) { + $options[$name] = function ($v, $k) use (&$results) { + $results[$k] = $v; + }; + } else { + $currentFn = $options[$name]; + $options[$name] = function ($v, $k) use (&$results, $currentFn) { + $currentFn($v, $k); + $results[$k] = $v; + }; + } + } +} diff --git a/instafeed/vendor/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php b/instafeed/vendor/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php new file mode 100755 index 0000000..2eb95f9 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php @@ -0,0 +1,106 @@ +nextHandler = $nextHandler; + } + + /** + * @param RequestInterface $request + * @param array $options + * + * @return PromiseInterface + */ + public function __invoke(RequestInterface $request, array $options) + { + $fn = $this->nextHandler; + + // Don't do anything if the request has no body. + if ($request->getBody()->getSize() === 0) { + return $fn($request, $options); + } + + $modify = []; + + // Add a default content-type if possible. + if (!$request->hasHeader('Content-Type')) { + if ($uri = $request->getBody()->getMetadata('uri')) { + if ($type = Psr7\mimetype_from_filename($uri)) { + $modify['set_headers']['Content-Type'] = $type; + } + } + } + + // Add a default content-length or transfer-encoding header. + if (!$request->hasHeader('Content-Length') + && !$request->hasHeader('Transfer-Encoding') + ) { + $size = $request->getBody()->getSize(); + if ($size !== null) { + $modify['set_headers']['Content-Length'] = $size; + } else { + $modify['set_headers']['Transfer-Encoding'] = 'chunked'; + } + } + + // Add the expect header if needed. + $this->addExpectHeader($request, $options, $modify); + + return $fn(Psr7\modify_request($request, $modify), $options); + } + + private function addExpectHeader( + RequestInterface $request, + array $options, + array &$modify + ) { + // Determine if the Expect header should be used + if ($request->hasHeader('Expect')) { + return; + } + + $expect = isset($options['expect']) ? $options['expect'] : null; + + // Return if disabled or if you're not using HTTP/1.1 or HTTP/2.0 + if ($expect === false || $request->getProtocolVersion() < 1.1) { + return; + } + + // The expect header is unconditionally enabled + if ($expect === true) { + $modify['set_headers']['Expect'] = '100-Continue'; + return; + } + + // By default, send the expect header when the payload is > 1mb + if ($expect === null) { + $expect = 1048576; + } + + // Always add if the body cannot be rewound, the size cannot be + // determined, or the size is greater than the cutoff threshold + $body = $request->getBody(); + $size = $body->getSize(); + + if ($size === null || $size >= (int) $expect || !$body->isSeekable()) { + $modify['set_headers']['Expect'] = '100-Continue'; + } + } +} diff --git a/instafeed/vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php b/instafeed/vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php new file mode 100755 index 0000000..bff4e4e --- /dev/null +++ b/instafeed/vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php @@ -0,0 +1,237 @@ + 5, + 'protocols' => ['http', 'https'], + 'strict' => false, + 'referer' => false, + 'track_redirects' => false, + ]; + + /** @var callable */ + private $nextHandler; + + /** + * @param callable $nextHandler Next handler to invoke. + */ + public function __construct(callable $nextHandler) + { + $this->nextHandler = $nextHandler; + } + + /** + * @param RequestInterface $request + * @param array $options + * + * @return PromiseInterface + */ + public function __invoke(RequestInterface $request, array $options) + { + $fn = $this->nextHandler; + + if (empty($options['allow_redirects'])) { + return $fn($request, $options); + } + + if ($options['allow_redirects'] === true) { + $options['allow_redirects'] = self::$defaultSettings; + } elseif (!is_array($options['allow_redirects'])) { + throw new \InvalidArgumentException('allow_redirects must be true, false, or array'); + } else { + // Merge the default settings with the provided settings + $options['allow_redirects'] += self::$defaultSettings; + } + + if (empty($options['allow_redirects']['max'])) { + return $fn($request, $options); + } + + return $fn($request, $options) + ->then(function (ResponseInterface $response) use ($request, $options) { + return $this->checkRedirect($request, $options, $response); + }); + } + + /** + * @param RequestInterface $request + * @param array $options + * @param ResponseInterface|PromiseInterface $response + * + * @return ResponseInterface|PromiseInterface + */ + public function checkRedirect( + RequestInterface $request, + array $options, + ResponseInterface $response + ) { + if (substr($response->getStatusCode(), 0, 1) != '3' + || !$response->hasHeader('Location') + ) { + return $response; + } + + $this->guardMax($request, $options); + $nextRequest = $this->modifyRequest($request, $options, $response); + + if (isset($options['allow_redirects']['on_redirect'])) { + call_user_func( + $options['allow_redirects']['on_redirect'], + $request, + $response, + $nextRequest->getUri() + ); + } + + /** @var PromiseInterface|ResponseInterface $promise */ + $promise = $this($nextRequest, $options); + + // Add headers to be able to track history of redirects. + if (!empty($options['allow_redirects']['track_redirects'])) { + return $this->withTracking( + $promise, + (string) $nextRequest->getUri(), + $response->getStatusCode() + ); + } + + return $promise; + } + + private function withTracking(PromiseInterface $promise, $uri, $statusCode) + { + return $promise->then( + function (ResponseInterface $response) use ($uri, $statusCode) { + // Note that we are pushing to the front of the list as this + // would be an earlier response than what is currently present + // in the history header. + $historyHeader = $response->getHeader(self::HISTORY_HEADER); + $statusHeader = $response->getHeader(self::STATUS_HISTORY_HEADER); + array_unshift($historyHeader, $uri); + array_unshift($statusHeader, $statusCode); + return $response->withHeader(self::HISTORY_HEADER, $historyHeader) + ->withHeader(self::STATUS_HISTORY_HEADER, $statusHeader); + } + ); + } + + private function guardMax(RequestInterface $request, array &$options) + { + $current = isset($options['__redirect_count']) + ? $options['__redirect_count'] + : 0; + $options['__redirect_count'] = $current + 1; + $max = $options['allow_redirects']['max']; + + if ($options['__redirect_count'] > $max) { + throw new TooManyRedirectsException( + "Will not follow more than {$max} redirects", + $request + ); + } + } + + /** + * @param RequestInterface $request + * @param array $options + * @param ResponseInterface $response + * + * @return RequestInterface + */ + public function modifyRequest( + RequestInterface $request, + array $options, + ResponseInterface $response + ) { + // Request modifications to apply. + $modify = []; + $protocols = $options['allow_redirects']['protocols']; + + // Use a GET request if this is an entity enclosing request and we are + // not forcing RFC compliance, but rather emulating what all browsers + // would do. + $statusCode = $response->getStatusCode(); + if ($statusCode == 303 || + ($statusCode <= 302 && $request->getBody() && !$options['allow_redirects']['strict']) + ) { + $modify['method'] = 'GET'; + $modify['body'] = ''; + } + + $modify['uri'] = $this->redirectUri($request, $response, $protocols); + Psr7\rewind_body($request); + + // Add the Referer header if it is told to do so and only + // add the header if we are not redirecting from https to http. + if ($options['allow_redirects']['referer'] + && $modify['uri']->getScheme() === $request->getUri()->getScheme() + ) { + $uri = $request->getUri()->withUserInfo(''); + $modify['set_headers']['Referer'] = (string) $uri; + } else { + $modify['remove_headers'][] = 'Referer'; + } + + // Remove Authorization header if host is different. + if ($request->getUri()->getHost() !== $modify['uri']->getHost()) { + $modify['remove_headers'][] = 'Authorization'; + } + + return Psr7\modify_request($request, $modify); + } + + /** + * Set the appropriate URL on the request based on the location header + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @param array $protocols + * + * @return UriInterface + */ + private function redirectUri( + RequestInterface $request, + ResponseInterface $response, + array $protocols + ) { + $location = Psr7\UriResolver::resolve( + $request->getUri(), + new Psr7\Uri($response->getHeaderLine('Location')) + ); + + // Ensure that the redirect URI is allowed based on the protocols. + if (!in_array($location->getScheme(), $protocols)) { + throw new BadResponseException( + sprintf( + 'Redirect URI, %s, does not use one of the allowed redirect protocols: %s', + $location, + implode(', ', $protocols) + ), + $request, + $response + ); + } + + return $location; + } +} diff --git a/instafeed/vendor/guzzlehttp/guzzle/src/RequestOptions.php b/instafeed/vendor/guzzlehttp/guzzle/src/RequestOptions.php new file mode 100755 index 0000000..5c0fd19 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/guzzle/src/RequestOptions.php @@ -0,0 +1,255 @@ +decider = $decider; + $this->nextHandler = $nextHandler; + $this->delay = $delay ?: __CLASS__ . '::exponentialDelay'; + } + + /** + * Default exponential backoff delay function. + * + * @param int $retries + * + * @return int + */ + public static function exponentialDelay($retries) + { + return (int) pow(2, $retries - 1); + } + + /** + * @param RequestInterface $request + * @param array $options + * + * @return PromiseInterface + */ + public function __invoke(RequestInterface $request, array $options) + { + if (!isset($options['retries'])) { + $options['retries'] = 0; + } + + $fn = $this->nextHandler; + return $fn($request, $options) + ->then( + $this->onFulfilled($request, $options), + $this->onRejected($request, $options) + ); + } + + private function onFulfilled(RequestInterface $req, array $options) + { + return function ($value) use ($req, $options) { + if (!call_user_func( + $this->decider, + $options['retries'], + $req, + $value, + null + )) { + return $value; + } + return $this->doRetry($req, $options, $value); + }; + } + + private function onRejected(RequestInterface $req, array $options) + { + return function ($reason) use ($req, $options) { + if (!call_user_func( + $this->decider, + $options['retries'], + $req, + null, + $reason + )) { + return \GuzzleHttp\Promise\rejection_for($reason); + } + return $this->doRetry($req, $options); + }; + } + + private function doRetry(RequestInterface $request, array $options, ResponseInterface $response = null) + { + $options['delay'] = call_user_func($this->delay, ++$options['retries'], $response); + + return $this($request, $options); + } +} diff --git a/instafeed/vendor/guzzlehttp/guzzle/src/TransferStats.php b/instafeed/vendor/guzzlehttp/guzzle/src/TransferStats.php new file mode 100755 index 0000000..23a22a3 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/guzzle/src/TransferStats.php @@ -0,0 +1,126 @@ +request = $request; + $this->response = $response; + $this->transferTime = $transferTime; + $this->handlerErrorData = $handlerErrorData; + $this->handlerStats = $handlerStats; + } + + /** + * @return RequestInterface + */ + public function getRequest() + { + return $this->request; + } + + /** + * Returns the response that was received (if any). + * + * @return ResponseInterface|null + */ + public function getResponse() + { + return $this->response; + } + + /** + * Returns true if a response was received. + * + * @return bool + */ + public function hasResponse() + { + return $this->response !== null; + } + + /** + * Gets handler specific error data. + * + * This might be an exception, a integer representing an error code, or + * anything else. Relying on this value assumes that you know what handler + * you are using. + * + * @return mixed + */ + public function getHandlerErrorData() + { + return $this->handlerErrorData; + } + + /** + * Get the effective URI the request was sent to. + * + * @return UriInterface + */ + public function getEffectiveUri() + { + return $this->request->getUri(); + } + + /** + * Get the estimated time the request was being transferred by the handler. + * + * @return float Time in seconds. + */ + public function getTransferTime() + { + return $this->transferTime; + } + + /** + * Gets an array of all of the handler specific transfer data. + * + * @return array + */ + public function getHandlerStats() + { + return $this->handlerStats; + } + + /** + * Get a specific handler statistic from the handler by name. + * + * @param string $stat Handler specific transfer stat to retrieve. + * + * @return mixed|null + */ + public function getHandlerStat($stat) + { + return isset($this->handlerStats[$stat]) + ? $this->handlerStats[$stat] + : null; + } +} diff --git a/instafeed/vendor/guzzlehttp/guzzle/src/UriTemplate.php b/instafeed/vendor/guzzlehttp/guzzle/src/UriTemplate.php new file mode 100755 index 0000000..96dcfd0 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/guzzle/src/UriTemplate.php @@ -0,0 +1,237 @@ + ['prefix' => '', 'joiner' => ',', 'query' => false], + '+' => ['prefix' => '', 'joiner' => ',', 'query' => false], + '#' => ['prefix' => '#', 'joiner' => ',', 'query' => false], + '.' => ['prefix' => '.', 'joiner' => '.', 'query' => false], + '/' => ['prefix' => '/', 'joiner' => '/', 'query' => false], + ';' => ['prefix' => ';', 'joiner' => ';', 'query' => true], + '?' => ['prefix' => '?', 'joiner' => '&', 'query' => true], + '&' => ['prefix' => '&', 'joiner' => '&', 'query' => true] + ]; + + /** @var array Delimiters */ + private static $delims = [':', '/', '?', '#', '[', ']', '@', '!', '$', + '&', '\'', '(', ')', '*', '+', ',', ';', '=']; + + /** @var array Percent encoded delimiters */ + private static $delimsPct = ['%3A', '%2F', '%3F', '%23', '%5B', '%5D', + '%40', '%21', '%24', '%26', '%27', '%28', '%29', '%2A', '%2B', '%2C', + '%3B', '%3D']; + + public function expand($template, array $variables) + { + if (false === strpos($template, '{')) { + return $template; + } + + $this->template = $template; + $this->variables = $variables; + + return preg_replace_callback( + '/\{([^\}]+)\}/', + [$this, 'expandMatch'], + $this->template + ); + } + + /** + * Parse an expression into parts + * + * @param string $expression Expression to parse + * + * @return array Returns an associative array of parts + */ + private function parseExpression($expression) + { + $result = []; + + if (isset(self::$operatorHash[$expression[0]])) { + $result['operator'] = $expression[0]; + $expression = substr($expression, 1); + } else { + $result['operator'] = ''; + } + + foreach (explode(',', $expression) as $value) { + $value = trim($value); + $varspec = []; + if ($colonPos = strpos($value, ':')) { + $varspec['value'] = substr($value, 0, $colonPos); + $varspec['modifier'] = ':'; + $varspec['position'] = (int) substr($value, $colonPos + 1); + } elseif (substr($value, -1) === '*') { + $varspec['modifier'] = '*'; + $varspec['value'] = substr($value, 0, -1); + } else { + $varspec['value'] = (string) $value; + $varspec['modifier'] = ''; + } + $result['values'][] = $varspec; + } + + return $result; + } + + /** + * Process an expansion + * + * @param array $matches Matches met in the preg_replace_callback + * + * @return string Returns the replacement string + */ + private function expandMatch(array $matches) + { + static $rfc1738to3986 = ['+' => '%20', '%7e' => '~']; + + $replacements = []; + $parsed = self::parseExpression($matches[1]); + $prefix = self::$operatorHash[$parsed['operator']]['prefix']; + $joiner = self::$operatorHash[$parsed['operator']]['joiner']; + $useQuery = self::$operatorHash[$parsed['operator']]['query']; + + foreach ($parsed['values'] as $value) { + if (!isset($this->variables[$value['value']])) { + continue; + } + + $variable = $this->variables[$value['value']]; + $actuallyUseQuery = $useQuery; + $expanded = ''; + + if (is_array($variable)) { + $isAssoc = $this->isAssoc($variable); + $kvp = []; + foreach ($variable as $key => $var) { + if ($isAssoc) { + $key = rawurlencode($key); + $isNestedArray = is_array($var); + } else { + $isNestedArray = false; + } + + if (!$isNestedArray) { + $var = rawurlencode($var); + if ($parsed['operator'] === '+' || + $parsed['operator'] === '#' + ) { + $var = $this->decodeReserved($var); + } + } + + if ($value['modifier'] === '*') { + if ($isAssoc) { + if ($isNestedArray) { + // Nested arrays must allow for deeply nested + // structures. + $var = strtr( + http_build_query([$key => $var]), + $rfc1738to3986 + ); + } else { + $var = $key . '=' . $var; + } + } elseif ($key > 0 && $actuallyUseQuery) { + $var = $value['value'] . '=' . $var; + } + } + + $kvp[$key] = $var; + } + + if (empty($variable)) { + $actuallyUseQuery = false; + } elseif ($value['modifier'] === '*') { + $expanded = implode($joiner, $kvp); + if ($isAssoc) { + // Don't prepend the value name when using the explode + // modifier with an associative array. + $actuallyUseQuery = false; + } + } else { + if ($isAssoc) { + // When an associative array is encountered and the + // explode modifier is not set, then the result must be + // a comma separated list of keys followed by their + // respective values. + foreach ($kvp as $k => &$v) { + $v = $k . ',' . $v; + } + } + $expanded = implode(',', $kvp); + } + } else { + if ($value['modifier'] === ':') { + $variable = substr($variable, 0, $value['position']); + } + $expanded = rawurlencode($variable); + if ($parsed['operator'] === '+' || $parsed['operator'] === '#') { + $expanded = $this->decodeReserved($expanded); + } + } + + if ($actuallyUseQuery) { + if (!$expanded && $joiner !== '&') { + $expanded = $value['value']; + } else { + $expanded = $value['value'] . '=' . $expanded; + } + } + + $replacements[] = $expanded; + } + + $ret = implode($joiner, $replacements); + if ($ret && $prefix) { + return $prefix . $ret; + } + + return $ret; + } + + /** + * Determines if an array is associative. + * + * This makes the assumption that input arrays are sequences or hashes. + * This assumption is a tradeoff for accuracy in favor of speed, but it + * should work in almost every case where input is supplied for a URI + * template. + * + * @param array $array Array to check + * + * @return bool + */ + private function isAssoc(array $array) + { + return $array && array_keys($array)[0] !== 0; + } + + /** + * Removes percent encoding on reserved characters (used with + and # + * modifiers). + * + * @param string $string String to fix + * + * @return string + */ + private function decodeReserved($string) + { + return str_replace(self::$delimsPct, self::$delims, $string); + } +} diff --git a/instafeed/vendor/guzzlehttp/guzzle/src/functions.php b/instafeed/vendor/guzzlehttp/guzzle/src/functions.php new file mode 100755 index 0000000..51d736d --- /dev/null +++ b/instafeed/vendor/guzzlehttp/guzzle/src/functions.php @@ -0,0 +1,346 @@ +expand($template, $variables); +} + +/** + * Debug function used to describe the provided value type and class. + * + * @param mixed $input + * + * @return string Returns a string containing the type of the variable and + * if a class is provided, the class name. + */ +function describe_type($input) +{ + switch (gettype($input)) { + case 'object': + return 'object(' . get_class($input) . ')'; + case 'array': + return 'array(' . count($input) . ')'; + default: + ob_start(); + var_dump($input); + // normalize float vs double + return str_replace('double(', 'float(', rtrim(ob_get_clean())); + } +} + +/** + * Parses an array of header lines into an associative array of headers. + * + * @param array $lines Header lines array of strings in the following + * format: "Name: Value" + * @return array + */ +function headers_from_lines($lines) +{ + $headers = []; + + foreach ($lines as $line) { + $parts = explode(':', $line, 2); + $headers[trim($parts[0])][] = isset($parts[1]) + ? trim($parts[1]) + : null; + } + + return $headers; +} + +/** + * Returns a debug stream based on the provided variable. + * + * @param mixed $value Optional value + * + * @return resource + */ +function debug_resource($value = null) +{ + if (is_resource($value)) { + return $value; + } elseif (defined('STDOUT')) { + return STDOUT; + } + + return fopen('php://output', 'w'); +} + +/** + * Chooses and creates a default handler to use based on the environment. + * + * The returned handler is not wrapped by any default middlewares. + * + * @throws \RuntimeException if no viable Handler is available. + * @return callable Returns the best handler for the given system. + */ +function choose_handler() +{ + $handler = null; + if (function_exists('curl_multi_exec') && function_exists('curl_exec')) { + $handler = Proxy::wrapSync(new CurlMultiHandler(), new CurlHandler()); + } elseif (function_exists('curl_exec')) { + $handler = new CurlHandler(); + } elseif (function_exists('curl_multi_exec')) { + $handler = new CurlMultiHandler(); + } + + if (ini_get('allow_url_fopen')) { + $handler = $handler + ? Proxy::wrapStreaming($handler, new StreamHandler()) + : new StreamHandler(); + } elseif (!$handler) { + throw new \RuntimeException('GuzzleHttp requires cURL, the ' + . 'allow_url_fopen ini setting, or a custom HTTP handler.'); + } + + return $handler; +} + +/** + * Get the default User-Agent string to use with Guzzle + * + * @return string + */ +function default_user_agent() +{ + static $defaultAgent = ''; + + if (!$defaultAgent) { + $defaultAgent = 'GuzzleHttp/' . Client::VERSION; + if (extension_loaded('curl') && function_exists('curl_version')) { + $defaultAgent .= ' curl/' . \curl_version()['version']; + } + $defaultAgent .= ' PHP/' . PHP_VERSION; + } + + return $defaultAgent; +} + +/** + * Returns the default cacert bundle for the current system. + * + * First, the openssl.cafile and curl.cainfo php.ini settings are checked. + * If those settings are not configured, then the common locations for + * bundles found on Red Hat, CentOS, Fedora, Ubuntu, Debian, FreeBSD, OS X + * and Windows are checked. If any of these file locations are found on + * disk, they will be utilized. + * + * Note: the result of this function is cached for subsequent calls. + * + * @return string + * @throws \RuntimeException if no bundle can be found. + */ +function default_ca_bundle() +{ + static $cached = null; + static $cafiles = [ + // Red Hat, CentOS, Fedora (provided by the ca-certificates package) + '/etc/pki/tls/certs/ca-bundle.crt', + // Ubuntu, Debian (provided by the ca-certificates package) + '/etc/ssl/certs/ca-certificates.crt', + // FreeBSD (provided by the ca_root_nss package) + '/usr/local/share/certs/ca-root-nss.crt', + // SLES 12 (provided by the ca-certificates package) + '/var/lib/ca-certificates/ca-bundle.pem', + // OS X provided by homebrew (using the default path) + '/usr/local/etc/openssl/cert.pem', + // Google app engine + '/etc/ca-certificates.crt', + // Windows? + 'C:\\windows\\system32\\curl-ca-bundle.crt', + 'C:\\windows\\curl-ca-bundle.crt', + ]; + + if ($cached) { + return $cached; + } + + if ($ca = ini_get('openssl.cafile')) { + return $cached = $ca; + } + + if ($ca = ini_get('curl.cainfo')) { + return $cached = $ca; + } + + foreach ($cafiles as $filename) { + if (file_exists($filename)) { + return $cached = $filename; + } + } + + throw new \RuntimeException( + <<< EOT +No system CA bundle could be found in any of the the common system locations. +PHP versions earlier than 5.6 are not properly configured to use the system's +CA bundle by default. In order to verify peer certificates, you will need to +supply the path on disk to a certificate bundle to the 'verify' request +option: http://docs.guzzlephp.org/en/latest/clients.html#verify. If you do not +need a specific certificate bundle, then Mozilla provides a commonly used CA +bundle which can be downloaded here (provided by the maintainer of cURL): +https://raw.githubusercontent.com/bagder/ca-bundle/master/ca-bundle.crt. Once +you have a CA bundle available on disk, you can set the 'openssl.cafile' PHP +ini setting to point to the path to the file, allowing you to omit the 'verify' +request option. See http://curl.haxx.se/docs/sslcerts.html for more +information. +EOT + ); +} + +/** + * Creates an associative array of lowercase header names to the actual + * header casing. + * + * @param array $headers + * + * @return array + */ +function normalize_header_keys(array $headers) +{ + $result = []; + foreach (array_keys($headers) as $key) { + $result[strtolower($key)] = $key; + } + + return $result; +} + +/** + * Returns true if the provided host matches any of the no proxy areas. + * + * This method will strip a port from the host if it is present. Each pattern + * can be matched with an exact match (e.g., "foo.com" == "foo.com") or a + * partial match: (e.g., "foo.com" == "baz.foo.com" and ".foo.com" == + * "baz.foo.com", but ".foo.com" != "foo.com"). + * + * Areas are matched in the following cases: + * 1. "*" (without quotes) always matches any hosts. + * 2. An exact match. + * 3. The area starts with "." and the area is the last part of the host. e.g. + * '.mit.edu' will match any host that ends with '.mit.edu'. + * + * @param string $host Host to check against the patterns. + * @param array $noProxyArray An array of host patterns. + * + * @return bool + */ +function is_host_in_noproxy($host, array $noProxyArray) +{ + if (strlen($host) === 0) { + throw new \InvalidArgumentException('Empty host provided'); + } + + // Strip port if present. + if (strpos($host, ':')) { + $host = explode($host, ':', 2)[0]; + } + + foreach ($noProxyArray as $area) { + // Always match on wildcards. + if ($area === '*') { + return true; + } elseif (empty($area)) { + // Don't match on empty values. + continue; + } elseif ($area === $host) { + // Exact matches. + return true; + } else { + // Special match if the area when prefixed with ".". Remove any + // existing leading "." and add a new leading ".". + $area = '.' . ltrim($area, '.'); + if (substr($host, -(strlen($area))) === $area) { + return true; + } + } + } + + return false; +} + +/** + * Wrapper for json_decode that throws when an error occurs. + * + * @param string $json JSON data to parse + * @param bool $assoc When true, returned objects will be converted + * into associative arrays. + * @param int $depth User specified recursion depth. + * @param int $options Bitmask of JSON decode options. + * + * @return mixed + * @throws Exception\InvalidArgumentException if the JSON cannot be decoded. + * @link http://www.php.net/manual/en/function.json-decode.php + */ +function json_decode($json, $assoc = false, $depth = 512, $options = 0) +{ + $data = \json_decode($json, $assoc, $depth, $options); + if (JSON_ERROR_NONE !== json_last_error()) { + throw new Exception\InvalidArgumentException( + 'json_decode error: ' . json_last_error_msg() + ); + } + + return $data; +} + +/** + * Wrapper for JSON encoding that throws when an error occurs. + * + * @param mixed $value The value being encoded + * @param int $options JSON encode option bitmask + * @param int $depth Set the maximum depth. Must be greater than zero. + * + * @return string + * @throws Exception\InvalidArgumentException if the JSON cannot be encoded. + * @link http://www.php.net/manual/en/function.json-encode.php + */ +function json_encode($value, $options = 0, $depth = 512) +{ + $json = \json_encode($value, $options, $depth); + if (JSON_ERROR_NONE !== json_last_error()) { + throw new Exception\InvalidArgumentException( + 'json_encode error: ' . json_last_error_msg() + ); + } + + return $json; +} + +/** + * Wrapper for the hrtime() or microtime() functions + * (depending on the PHP version, one of the two is used) + * + * @return float|mixed UNIX timestamp + * @internal + */ +function _current_time() +{ + return function_exists('hrtime') ? hrtime(true) / 1e9 : microtime(true); +} diff --git a/instafeed/vendor/guzzlehttp/guzzle/src/functions_include.php b/instafeed/vendor/guzzlehttp/guzzle/src/functions_include.php new file mode 100755 index 0000000..a93393a --- /dev/null +++ b/instafeed/vendor/guzzlehttp/guzzle/src/functions_include.php @@ -0,0 +1,6 @@ + + +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/instafeed/vendor/guzzlehttp/promises/Makefile b/instafeed/vendor/guzzlehttp/promises/Makefile new file mode 100755 index 0000000..8d5b3ef --- /dev/null +++ b/instafeed/vendor/guzzlehttp/promises/Makefile @@ -0,0 +1,13 @@ +all: clean test + +test: + vendor/bin/phpunit + +coverage: + vendor/bin/phpunit --coverage-html=artifacts/coverage + +view-coverage: + open artifacts/coverage/index.html + +clean: + rm -rf artifacts/* diff --git a/instafeed/vendor/guzzlehttp/promises/README.md b/instafeed/vendor/guzzlehttp/promises/README.md new file mode 100755 index 0000000..7b607e2 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/promises/README.md @@ -0,0 +1,504 @@ +# Guzzle Promises + +[Promises/A+](https://promisesaplus.com/) implementation that handles promise +chaining and resolution iteratively, allowing for "infinite" promise chaining +while keeping the stack size constant. Read [this blog post](https://blog.domenic.me/youre-missing-the-point-of-promises/) +for a general introduction to promises. + +- [Features](#features) +- [Quick start](#quick-start) +- [Synchronous wait](#synchronous-wait) +- [Cancellation](#cancellation) +- [API](#api) + - [Promise](#promise) + - [FulfilledPromise](#fulfilledpromise) + - [RejectedPromise](#rejectedpromise) +- [Promise interop](#promise-interop) +- [Implementation notes](#implementation-notes) + + +# Features + +- [Promises/A+](https://promisesaplus.com/) implementation. +- Promise resolution and chaining is handled iteratively, allowing for + "infinite" promise chaining. +- Promises have a synchronous `wait` method. +- Promises can be cancelled. +- Works with any object that has a `then` function. +- C# style async/await coroutine promises using + `GuzzleHttp\Promise\coroutine()`. + + +# Quick start + +A *promise* represents the eventual result of an asynchronous operation. The +primary way of interacting with a promise is through its `then` method, which +registers callbacks to receive either a promise's eventual value or the reason +why the promise cannot be fulfilled. + + +## Callbacks + +Callbacks are registered with the `then` method by providing an optional +`$onFulfilled` followed by an optional `$onRejected` function. + + +```php +use GuzzleHttp\Promise\Promise; + +$promise = new Promise(); +$promise->then( + // $onFulfilled + function ($value) { + echo 'The promise was fulfilled.'; + }, + // $onRejected + function ($reason) { + echo 'The promise was rejected.'; + } +); +``` + +*Resolving* a promise means that you either fulfill a promise with a *value* or +reject a promise with a *reason*. Resolving a promises triggers callbacks +registered with the promises's `then` method. These callbacks are triggered +only once and in the order in which they were added. + + +## Resolving a promise + +Promises are fulfilled using the `resolve($value)` method. Resolving a promise +with any value other than a `GuzzleHttp\Promise\RejectedPromise` will trigger +all of the onFulfilled callbacks (resolving a promise with a rejected promise +will reject the promise and trigger the `$onRejected` callbacks). + +```php +use GuzzleHttp\Promise\Promise; + +$promise = new Promise(); +$promise + ->then(function ($value) { + // Return a value and don't break the chain + return "Hello, " . $value; + }) + // This then is executed after the first then and receives the value + // returned from the first then. + ->then(function ($value) { + echo $value; + }); + +// Resolving the promise triggers the $onFulfilled callbacks and outputs +// "Hello, reader". +$promise->resolve('reader.'); +``` + + +## Promise forwarding + +Promises can be chained one after the other. Each then in the chain is a new +promise. The return value of a promise is what's forwarded to the next +promise in the chain. Returning a promise in a `then` callback will cause the +subsequent promises in the chain to only be fulfilled when the returned promise +has been fulfilled. The next promise in the chain will be invoked with the +resolved value of the promise. + +```php +use GuzzleHttp\Promise\Promise; + +$promise = new Promise(); +$nextPromise = new Promise(); + +$promise + ->then(function ($value) use ($nextPromise) { + echo $value; + return $nextPromise; + }) + ->then(function ($value) { + echo $value; + }); + +// Triggers the first callback and outputs "A" +$promise->resolve('A'); +// Triggers the second callback and outputs "B" +$nextPromise->resolve('B'); +``` + +## Promise rejection + +When a promise is rejected, the `$onRejected` callbacks are invoked with the +rejection reason. + +```php +use GuzzleHttp\Promise\Promise; + +$promise = new Promise(); +$promise->then(null, function ($reason) { + echo $reason; +}); + +$promise->reject('Error!'); +// Outputs "Error!" +``` + +## Rejection forwarding + +If an exception is thrown in an `$onRejected` callback, subsequent +`$onRejected` callbacks are invoked with the thrown exception as the reason. + +```php +use GuzzleHttp\Promise\Promise; + +$promise = new Promise(); +$promise->then(null, function ($reason) { + throw new \Exception($reason); +})->then(null, function ($reason) { + assert($reason->getMessage() === 'Error!'); +}); + +$promise->reject('Error!'); +``` + +You can also forward a rejection down the promise chain by returning a +`GuzzleHttp\Promise\RejectedPromise` in either an `$onFulfilled` or +`$onRejected` callback. + +```php +use GuzzleHttp\Promise\Promise; +use GuzzleHttp\Promise\RejectedPromise; + +$promise = new Promise(); +$promise->then(null, function ($reason) { + return new RejectedPromise($reason); +})->then(null, function ($reason) { + assert($reason === 'Error!'); +}); + +$promise->reject('Error!'); +``` + +If an exception is not thrown in a `$onRejected` callback and the callback +does not return a rejected promise, downstream `$onFulfilled` callbacks are +invoked using the value returned from the `$onRejected` callback. + +```php +use GuzzleHttp\Promise\Promise; +use GuzzleHttp\Promise\RejectedPromise; + +$promise = new Promise(); +$promise + ->then(null, function ($reason) { + return "It's ok"; + }) + ->then(function ($value) { + assert($value === "It's ok"); + }); + +$promise->reject('Error!'); +``` + +# Synchronous wait + +You can synchronously force promises to complete using a promise's `wait` +method. When creating a promise, you can provide a wait function that is used +to synchronously force a promise to complete. When a wait function is invoked +it is expected to deliver a value to the promise or reject the promise. If the +wait function does not deliver a value, then an exception is thrown. The wait +function provided to a promise constructor is invoked when the `wait` function +of the promise is called. + +```php +$promise = new Promise(function () use (&$promise) { + $promise->resolve('foo'); +}); + +// Calling wait will return the value of the promise. +echo $promise->wait(); // outputs "foo" +``` + +If an exception is encountered while invoking the wait function of a promise, +the promise is rejected with the exception and the exception is thrown. + +```php +$promise = new Promise(function () use (&$promise) { + throw new \Exception('foo'); +}); + +$promise->wait(); // throws the exception. +``` + +Calling `wait` on a promise that has been fulfilled will not trigger the wait +function. It will simply return the previously resolved value. + +```php +$promise = new Promise(function () { die('this is not called!'); }); +$promise->resolve('foo'); +echo $promise->wait(); // outputs "foo" +``` + +Calling `wait` on a promise that has been rejected will throw an exception. If +the rejection reason is an instance of `\Exception` the reason is thrown. +Otherwise, a `GuzzleHttp\Promise\RejectionException` is thrown and the reason +can be obtained by calling the `getReason` method of the exception. + +```php +$promise = new Promise(); +$promise->reject('foo'); +$promise->wait(); +``` + +> PHP Fatal error: Uncaught exception 'GuzzleHttp\Promise\RejectionException' with message 'The promise was rejected with value: foo' + + +## Unwrapping a promise + +When synchronously waiting on a promise, you are joining the state of the +promise into the current state of execution (i.e., return the value of the +promise if it was fulfilled or throw an exception if it was rejected). This is +called "unwrapping" the promise. Waiting on a promise will by default unwrap +the promise state. + +You can force a promise to resolve and *not* unwrap the state of the promise +by passing `false` to the first argument of the `wait` function: + +```php +$promise = new Promise(); +$promise->reject('foo'); +// This will not throw an exception. It simply ensures the promise has +// been resolved. +$promise->wait(false); +``` + +When unwrapping a promise, the resolved value of the promise will be waited +upon until the unwrapped value is not a promise. This means that if you resolve +promise A with a promise B and unwrap promise A, the value returned by the +wait function will be the value delivered to promise B. + +**Note**: when you do not unwrap the promise, no value is returned. + + +# Cancellation + +You can cancel a promise that has not yet been fulfilled using the `cancel()` +method of a promise. When creating a promise you can provide an optional +cancel function that when invoked cancels the action of computing a resolution +of the promise. + + +# API + + +## Promise + +When creating a promise object, you can provide an optional `$waitFn` and +`$cancelFn`. `$waitFn` is a function that is invoked with no arguments and is +expected to resolve the promise. `$cancelFn` is a function with no arguments +that is expected to cancel the computation of a promise. It is invoked when the +`cancel()` method of a promise is called. + +```php +use GuzzleHttp\Promise\Promise; + +$promise = new Promise( + function () use (&$promise) { + $promise->resolve('waited'); + }, + function () { + // do something that will cancel the promise computation (e.g., close + // a socket, cancel a database query, etc...) + } +); + +assert('waited' === $promise->wait()); +``` + +A promise has the following methods: + +- `then(callable $onFulfilled, callable $onRejected) : PromiseInterface` + + Appends fulfillment and rejection handlers to the promise, and returns a new promise resolving to the return value of the called handler. + +- `otherwise(callable $onRejected) : PromiseInterface` + + Appends a rejection handler callback to the promise, and returns a new promise resolving to the return value of the callback if it is called, or to its original fulfillment value if the promise is instead fulfilled. + +- `wait($unwrap = true) : mixed` + + Synchronously waits on the promise to complete. + + `$unwrap` controls whether or not the value of the promise is returned for a + fulfilled promise or if an exception is thrown if the promise is rejected. + This is set to `true` by default. + +- `cancel()` + + Attempts to cancel the promise if possible. The promise being cancelled and + the parent most ancestor that has not yet been resolved will also be + cancelled. Any promises waiting on the cancelled promise to resolve will also + be cancelled. + +- `getState() : string` + + Returns the state of the promise. One of `pending`, `fulfilled`, or + `rejected`. + +- `resolve($value)` + + Fulfills the promise with the given `$value`. + +- `reject($reason)` + + Rejects the promise with the given `$reason`. + + +## FulfilledPromise + +A fulfilled promise can be created to represent a promise that has been +fulfilled. + +```php +use GuzzleHttp\Promise\FulfilledPromise; + +$promise = new FulfilledPromise('value'); + +// Fulfilled callbacks are immediately invoked. +$promise->then(function ($value) { + echo $value; +}); +``` + + +## RejectedPromise + +A rejected promise can be created to represent a promise that has been +rejected. + +```php +use GuzzleHttp\Promise\RejectedPromise; + +$promise = new RejectedPromise('Error'); + +// Rejected callbacks are immediately invoked. +$promise->then(null, function ($reason) { + echo $reason; +}); +``` + + +# Promise interop + +This library works with foreign promises that have a `then` method. This means +you can use Guzzle promises with [React promises](https://github.com/reactphp/promise) +for example. When a foreign promise is returned inside of a then method +callback, promise resolution will occur recursively. + +```php +// Create a React promise +$deferred = new React\Promise\Deferred(); +$reactPromise = $deferred->promise(); + +// Create a Guzzle promise that is fulfilled with a React promise. +$guzzlePromise = new \GuzzleHttp\Promise\Promise(); +$guzzlePromise->then(function ($value) use ($reactPromise) { + // Do something something with the value... + // Return the React promise + return $reactPromise; +}); +``` + +Please note that wait and cancel chaining is no longer possible when forwarding +a foreign promise. You will need to wrap a third-party promise with a Guzzle +promise in order to utilize wait and cancel functions with foreign promises. + + +## Event Loop Integration + +In order to keep the stack size constant, Guzzle promises are resolved +asynchronously using a task queue. When waiting on promises synchronously, the +task queue will be automatically run to ensure that the blocking promise and +any forwarded promises are resolved. When using promises asynchronously in an +event loop, you will need to run the task queue on each tick of the loop. If +you do not run the task queue, then promises will not be resolved. + +You can run the task queue using the `run()` method of the global task queue +instance. + +```php +// Get the global task queue +$queue = \GuzzleHttp\Promise\queue(); +$queue->run(); +``` + +For example, you could use Guzzle promises with React using a periodic timer: + +```php +$loop = React\EventLoop\Factory::create(); +$loop->addPeriodicTimer(0, [$queue, 'run']); +``` + +*TODO*: Perhaps adding a `futureTick()` on each tick would be faster? + + +# Implementation notes + + +## Promise resolution and chaining is handled iteratively + +By shuffling pending handlers from one owner to another, promises are +resolved iteratively, allowing for "infinite" then chaining. + +```php +then(function ($v) { + // The stack size remains constant (a good thing) + echo xdebug_get_stack_depth() . ', '; + return $v + 1; + }); +} + +$parent->resolve(0); +var_dump($p->wait()); // int(1000) + +``` + +When a promise is fulfilled or rejected with a non-promise value, the promise +then takes ownership of the handlers of each child promise and delivers values +down the chain without using recursion. + +When a promise is resolved with another promise, the original promise transfers +all of its pending handlers to the new promise. When the new promise is +eventually resolved, all of the pending handlers are delivered the forwarded +value. + + +## A promise is the deferred. + +Some promise libraries implement promises using a deferred object to represent +a computation and a promise object to represent the delivery of the result of +the computation. This is a nice separation of computation and delivery because +consumers of the promise cannot modify the value that will be eventually +delivered. + +One side effect of being able to implement promise resolution and chaining +iteratively is that you need to be able for one promise to reach into the state +of another promise to shuffle around ownership of handlers. In order to achieve +this without making the handlers of a promise publicly mutable, a promise is +also the deferred value, allowing promises of the same parent class to reach +into and modify the private properties of promises of the same type. While this +does allow consumers of the value to modify the resolution or rejection of the +deferred, it is a small price to pay for keeping the stack size constant. + +```php +$promise = new Promise(); +$promise->then(function ($value) { echo $value; }); +// The promise is the deferred value, so you can deliver a value to it. +$promise->resolve('foo'); +// prints "foo" +``` diff --git a/instafeed/vendor/guzzlehttp/promises/composer.json b/instafeed/vendor/guzzlehttp/promises/composer.json new file mode 100755 index 0000000..ec41a61 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/promises/composer.json @@ -0,0 +1,34 @@ +{ + "name": "guzzlehttp/promises", + "description": "Guzzle promises library", + "keywords": ["promise"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.5.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0" + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + }, + "files": ["src/functions_include.php"] + }, + "scripts": { + "test": "vendor/bin/phpunit", + "test-ci": "vendor/bin/phpunit --coverage-text" + }, + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + } +} diff --git a/instafeed/vendor/guzzlehttp/promises/src/AggregateException.php b/instafeed/vendor/guzzlehttp/promises/src/AggregateException.php new file mode 100755 index 0000000..6a5690c --- /dev/null +++ b/instafeed/vendor/guzzlehttp/promises/src/AggregateException.php @@ -0,0 +1,16 @@ +then(function ($v) { echo $v; }); + * + * @param callable $generatorFn Generator function to wrap into a promise. + * + * @return Promise + * @link https://github.com/petkaantonov/bluebird/blob/master/API.md#generators inspiration + */ +final class Coroutine implements PromiseInterface +{ + /** + * @var PromiseInterface|null + */ + private $currentPromise; + + /** + * @var Generator + */ + private $generator; + + /** + * @var Promise + */ + private $result; + + public function __construct(callable $generatorFn) + { + $this->generator = $generatorFn(); + $this->result = new Promise(function () { + while (isset($this->currentPromise)) { + $this->currentPromise->wait(); + } + }); + $this->nextCoroutine($this->generator->current()); + } + + public function then( + callable $onFulfilled = null, + callable $onRejected = null + ) { + return $this->result->then($onFulfilled, $onRejected); + } + + public function otherwise(callable $onRejected) + { + return $this->result->otherwise($onRejected); + } + + public function wait($unwrap = true) + { + return $this->result->wait($unwrap); + } + + public function getState() + { + return $this->result->getState(); + } + + public function resolve($value) + { + $this->result->resolve($value); + } + + public function reject($reason) + { + $this->result->reject($reason); + } + + public function cancel() + { + $this->currentPromise->cancel(); + $this->result->cancel(); + } + + private function nextCoroutine($yielded) + { + $this->currentPromise = promise_for($yielded) + ->then([$this, '_handleSuccess'], [$this, '_handleFailure']); + } + + /** + * @internal + */ + public function _handleSuccess($value) + { + unset($this->currentPromise); + try { + $next = $this->generator->send($value); + if ($this->generator->valid()) { + $this->nextCoroutine($next); + } else { + $this->result->resolve($value); + } + } catch (Exception $exception) { + $this->result->reject($exception); + } catch (Throwable $throwable) { + $this->result->reject($throwable); + } + } + + /** + * @internal + */ + public function _handleFailure($reason) + { + unset($this->currentPromise); + try { + $nextYield = $this->generator->throw(exception_for($reason)); + // The throw was caught, so keep iterating on the coroutine + $this->nextCoroutine($nextYield); + } catch (Exception $exception) { + $this->result->reject($exception); + } catch (Throwable $throwable) { + $this->result->reject($throwable); + } + } +} diff --git a/instafeed/vendor/guzzlehttp/promises/src/EachPromise.php b/instafeed/vendor/guzzlehttp/promises/src/EachPromise.php new file mode 100755 index 0000000..d0ddf60 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/promises/src/EachPromise.php @@ -0,0 +1,229 @@ +iterable = iter_for($iterable); + + if (isset($config['concurrency'])) { + $this->concurrency = $config['concurrency']; + } + + if (isset($config['fulfilled'])) { + $this->onFulfilled = $config['fulfilled']; + } + + if (isset($config['rejected'])) { + $this->onRejected = $config['rejected']; + } + } + + public function promise() + { + if ($this->aggregate) { + return $this->aggregate; + } + + try { + $this->createPromise(); + $this->iterable->rewind(); + $this->refillPending(); + } catch (\Throwable $e) { + $this->aggregate->reject($e); + } catch (\Exception $e) { + $this->aggregate->reject($e); + } + + return $this->aggregate; + } + + private function createPromise() + { + $this->mutex = false; + $this->aggregate = new Promise(function () { + reset($this->pending); + if (empty($this->pending) && !$this->iterable->valid()) { + $this->aggregate->resolve(null); + return; + } + + // Consume a potentially fluctuating list of promises while + // ensuring that indexes are maintained (precluding array_shift). + while ($promise = current($this->pending)) { + next($this->pending); + $promise->wait(); + if ($this->aggregate->getState() !== PromiseInterface::PENDING) { + return; + } + } + }); + + // Clear the references when the promise is resolved. + $clearFn = function () { + $this->iterable = $this->concurrency = $this->pending = null; + $this->onFulfilled = $this->onRejected = null; + }; + + $this->aggregate->then($clearFn, $clearFn); + } + + private function refillPending() + { + if (!$this->concurrency) { + // Add all pending promises. + while ($this->addPending() && $this->advanceIterator()); + return; + } + + // Add only up to N pending promises. + $concurrency = is_callable($this->concurrency) + ? call_user_func($this->concurrency, count($this->pending)) + : $this->concurrency; + $concurrency = max($concurrency - count($this->pending), 0); + // Concurrency may be set to 0 to disallow new promises. + if (!$concurrency) { + return; + } + // Add the first pending promise. + $this->addPending(); + // Note this is special handling for concurrency=1 so that we do + // not advance the iterator after adding the first promise. This + // helps work around issues with generators that might not have the + // next value to yield until promise callbacks are called. + while (--$concurrency + && $this->advanceIterator() + && $this->addPending()); + } + + private function addPending() + { + if (!$this->iterable || !$this->iterable->valid()) { + return false; + } + + $promise = promise_for($this->iterable->current()); + $idx = $this->iterable->key(); + + $this->pending[$idx] = $promise->then( + function ($value) use ($idx) { + if ($this->onFulfilled) { + call_user_func( + $this->onFulfilled, $value, $idx, $this->aggregate + ); + } + $this->step($idx); + }, + function ($reason) use ($idx) { + if ($this->onRejected) { + call_user_func( + $this->onRejected, $reason, $idx, $this->aggregate + ); + } + $this->step($idx); + } + ); + + return true; + } + + private function advanceIterator() + { + // Place a lock on the iterator so that we ensure to not recurse, + // preventing fatal generator errors. + if ($this->mutex) { + return false; + } + + $this->mutex = true; + + try { + $this->iterable->next(); + $this->mutex = false; + return true; + } catch (\Throwable $e) { + $this->aggregate->reject($e); + $this->mutex = false; + return false; + } catch (\Exception $e) { + $this->aggregate->reject($e); + $this->mutex = false; + return false; + } + } + + private function step($idx) + { + // If the promise was already resolved, then ignore this step. + if ($this->aggregate->getState() !== PromiseInterface::PENDING) { + return; + } + + unset($this->pending[$idx]); + + // Only refill pending promises if we are not locked, preventing the + // EachPromise to recursively invoke the provided iterator, which + // cause a fatal error: "Cannot resume an already running generator" + if ($this->advanceIterator() && !$this->checkIfFinished()) { + // Add more pending promises if possible. + $this->refillPending(); + } + } + + private function checkIfFinished() + { + if (!$this->pending && !$this->iterable->valid()) { + // Resolve the promise if there's nothing left to do. + $this->aggregate->resolve(null); + return true; + } + + return false; + } +} diff --git a/instafeed/vendor/guzzlehttp/promises/src/FulfilledPromise.php b/instafeed/vendor/guzzlehttp/promises/src/FulfilledPromise.php new file mode 100755 index 0000000..dbbeeb9 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/promises/src/FulfilledPromise.php @@ -0,0 +1,82 @@ +value = $value; + } + + public function then( + callable $onFulfilled = null, + callable $onRejected = null + ) { + // Return itself if there is no onFulfilled function. + if (!$onFulfilled) { + return $this; + } + + $queue = queue(); + $p = new Promise([$queue, 'run']); + $value = $this->value; + $queue->add(static function () use ($p, $value, $onFulfilled) { + if ($p->getState() === self::PENDING) { + try { + $p->resolve($onFulfilled($value)); + } catch (\Throwable $e) { + $p->reject($e); + } catch (\Exception $e) { + $p->reject($e); + } + } + }); + + return $p; + } + + public function otherwise(callable $onRejected) + { + return $this->then(null, $onRejected); + } + + public function wait($unwrap = true, $defaultDelivery = null) + { + return $unwrap ? $this->value : null; + } + + public function getState() + { + return self::FULFILLED; + } + + public function resolve($value) + { + if ($value !== $this->value) { + throw new \LogicException("Cannot resolve a fulfilled promise"); + } + } + + public function reject($reason) + { + throw new \LogicException("Cannot reject a fulfilled promise"); + } + + public function cancel() + { + // pass + } +} diff --git a/instafeed/vendor/guzzlehttp/promises/src/Promise.php b/instafeed/vendor/guzzlehttp/promises/src/Promise.php new file mode 100755 index 0000000..844ada0 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/promises/src/Promise.php @@ -0,0 +1,280 @@ +waitFn = $waitFn; + $this->cancelFn = $cancelFn; + } + + public function then( + callable $onFulfilled = null, + callable $onRejected = null + ) { + if ($this->state === self::PENDING) { + $p = new Promise(null, [$this, 'cancel']); + $this->handlers[] = [$p, $onFulfilled, $onRejected]; + $p->waitList = $this->waitList; + $p->waitList[] = $this; + return $p; + } + + // Return a fulfilled promise and immediately invoke any callbacks. + if ($this->state === self::FULFILLED) { + return $onFulfilled + ? promise_for($this->result)->then($onFulfilled) + : promise_for($this->result); + } + + // It's either cancelled or rejected, so return a rejected promise + // and immediately invoke any callbacks. + $rejection = rejection_for($this->result); + return $onRejected ? $rejection->then(null, $onRejected) : $rejection; + } + + public function otherwise(callable $onRejected) + { + return $this->then(null, $onRejected); + } + + public function wait($unwrap = true) + { + $this->waitIfPending(); + + $inner = $this->result instanceof PromiseInterface + ? $this->result->wait($unwrap) + : $this->result; + + if ($unwrap) { + if ($this->result instanceof PromiseInterface + || $this->state === self::FULFILLED + ) { + return $inner; + } else { + // It's rejected so "unwrap" and throw an exception. + throw exception_for($inner); + } + } + } + + public function getState() + { + return $this->state; + } + + public function cancel() + { + if ($this->state !== self::PENDING) { + return; + } + + $this->waitFn = $this->waitList = null; + + if ($this->cancelFn) { + $fn = $this->cancelFn; + $this->cancelFn = null; + try { + $fn(); + } catch (\Throwable $e) { + $this->reject($e); + } catch (\Exception $e) { + $this->reject($e); + } + } + + // Reject the promise only if it wasn't rejected in a then callback. + if ($this->state === self::PENDING) { + $this->reject(new CancellationException('Promise has been cancelled')); + } + } + + public function resolve($value) + { + $this->settle(self::FULFILLED, $value); + } + + public function reject($reason) + { + $this->settle(self::REJECTED, $reason); + } + + private function settle($state, $value) + { + if ($this->state !== self::PENDING) { + // Ignore calls with the same resolution. + if ($state === $this->state && $value === $this->result) { + return; + } + throw $this->state === $state + ? new \LogicException("The promise is already {$state}.") + : new \LogicException("Cannot change a {$this->state} promise to {$state}"); + } + + if ($value === $this) { + throw new \LogicException('Cannot fulfill or reject a promise with itself'); + } + + // Clear out the state of the promise but stash the handlers. + $this->state = $state; + $this->result = $value; + $handlers = $this->handlers; + $this->handlers = null; + $this->waitList = $this->waitFn = null; + $this->cancelFn = null; + + if (!$handlers) { + return; + } + + // If the value was not a settled promise or a thenable, then resolve + // it in the task queue using the correct ID. + if (!method_exists($value, 'then')) { + $id = $state === self::FULFILLED ? 1 : 2; + // It's a success, so resolve the handlers in the queue. + queue()->add(static function () use ($id, $value, $handlers) { + foreach ($handlers as $handler) { + self::callHandler($id, $value, $handler); + } + }); + } elseif ($value instanceof Promise + && $value->getState() === self::PENDING + ) { + // We can just merge our handlers onto the next promise. + $value->handlers = array_merge($value->handlers, $handlers); + } else { + // Resolve the handlers when the forwarded promise is resolved. + $value->then( + static function ($value) use ($handlers) { + foreach ($handlers as $handler) { + self::callHandler(1, $value, $handler); + } + }, + static function ($reason) use ($handlers) { + foreach ($handlers as $handler) { + self::callHandler(2, $reason, $handler); + } + } + ); + } + } + + /** + * Call a stack of handlers using a specific callback index and value. + * + * @param int $index 1 (resolve) or 2 (reject). + * @param mixed $value Value to pass to the callback. + * @param array $handler Array of handler data (promise and callbacks). + * + * @return array Returns the next group to resolve. + */ + private static function callHandler($index, $value, array $handler) + { + /** @var PromiseInterface $promise */ + $promise = $handler[0]; + + // The promise may have been cancelled or resolved before placing + // this thunk in the queue. + if ($promise->getState() !== self::PENDING) { + return; + } + + try { + if (isset($handler[$index])) { + $promise->resolve($handler[$index]($value)); + } elseif ($index === 1) { + // Forward resolution values as-is. + $promise->resolve($value); + } else { + // Forward rejections down the chain. + $promise->reject($value); + } + } catch (\Throwable $reason) { + $promise->reject($reason); + } catch (\Exception $reason) { + $promise->reject($reason); + } + } + + private function waitIfPending() + { + if ($this->state !== self::PENDING) { + return; + } elseif ($this->waitFn) { + $this->invokeWaitFn(); + } elseif ($this->waitList) { + $this->invokeWaitList(); + } else { + // If there's not wait function, then reject the promise. + $this->reject('Cannot wait on a promise that has ' + . 'no internal wait function. You must provide a wait ' + . 'function when constructing the promise to be able to ' + . 'wait on a promise.'); + } + + queue()->run(); + + if ($this->state === self::PENDING) { + $this->reject('Invoking the wait callback did not resolve the promise'); + } + } + + private function invokeWaitFn() + { + try { + $wfn = $this->waitFn; + $this->waitFn = null; + $wfn(true); + } catch (\Exception $reason) { + if ($this->state === self::PENDING) { + // The promise has not been resolved yet, so reject the promise + // with the exception. + $this->reject($reason); + } else { + // The promise was already resolved, so there's a problem in + // the application. + throw $reason; + } + } + } + + private function invokeWaitList() + { + $waitList = $this->waitList; + $this->waitList = null; + + foreach ($waitList as $result) { + while (true) { + $result->waitIfPending(); + + if ($result->result instanceof Promise) { + $result = $result->result; + } else { + if ($result->result instanceof PromiseInterface) { + $result->result->wait(false); + } + break; + } + } + } + } +} diff --git a/instafeed/vendor/guzzlehttp/promises/src/PromiseInterface.php b/instafeed/vendor/guzzlehttp/promises/src/PromiseInterface.php new file mode 100755 index 0000000..8f5f4b9 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/promises/src/PromiseInterface.php @@ -0,0 +1,93 @@ +reason = $reason; + } + + public function then( + callable $onFulfilled = null, + callable $onRejected = null + ) { + // If there's no onRejected callback then just return self. + if (!$onRejected) { + return $this; + } + + $queue = queue(); + $reason = $this->reason; + $p = new Promise([$queue, 'run']); + $queue->add(static function () use ($p, $reason, $onRejected) { + if ($p->getState() === self::PENDING) { + try { + // Return a resolved promise if onRejected does not throw. + $p->resolve($onRejected($reason)); + } catch (\Throwable $e) { + // onRejected threw, so return a rejected promise. + $p->reject($e); + } catch (\Exception $e) { + // onRejected threw, so return a rejected promise. + $p->reject($e); + } + } + }); + + return $p; + } + + public function otherwise(callable $onRejected) + { + return $this->then(null, $onRejected); + } + + public function wait($unwrap = true, $defaultDelivery = null) + { + if ($unwrap) { + throw exception_for($this->reason); + } + } + + public function getState() + { + return self::REJECTED; + } + + public function resolve($value) + { + throw new \LogicException("Cannot resolve a rejected promise"); + } + + public function reject($reason) + { + if ($reason !== $this->reason) { + throw new \LogicException("Cannot reject a rejected promise"); + } + } + + public function cancel() + { + // pass + } +} diff --git a/instafeed/vendor/guzzlehttp/promises/src/RejectionException.php b/instafeed/vendor/guzzlehttp/promises/src/RejectionException.php new file mode 100755 index 0000000..07c1136 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/promises/src/RejectionException.php @@ -0,0 +1,47 @@ +reason = $reason; + + $message = 'The promise was rejected'; + + if ($description) { + $message .= ' with reason: ' . $description; + } elseif (is_string($reason) + || (is_object($reason) && method_exists($reason, '__toString')) + ) { + $message .= ' with reason: ' . $this->reason; + } elseif ($reason instanceof \JsonSerializable) { + $message .= ' with reason: ' + . json_encode($this->reason, JSON_PRETTY_PRINT); + } + + parent::__construct($message); + } + + /** + * Returns the rejection reason. + * + * @return mixed + */ + public function getReason() + { + return $this->reason; + } +} diff --git a/instafeed/vendor/guzzlehttp/promises/src/TaskQueue.php b/instafeed/vendor/guzzlehttp/promises/src/TaskQueue.php new file mode 100755 index 0000000..6e8a2a0 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/promises/src/TaskQueue.php @@ -0,0 +1,66 @@ +run(); + */ +class TaskQueue implements TaskQueueInterface +{ + private $enableShutdown = true; + private $queue = []; + + public function __construct($withShutdown = true) + { + if ($withShutdown) { + register_shutdown_function(function () { + if ($this->enableShutdown) { + // Only run the tasks if an E_ERROR didn't occur. + $err = error_get_last(); + if (!$err || ($err['type'] ^ E_ERROR)) { + $this->run(); + } + } + }); + } + } + + public function isEmpty() + { + return !$this->queue; + } + + public function add(callable $task) + { + $this->queue[] = $task; + } + + public function run() + { + /** @var callable $task */ + while ($task = array_shift($this->queue)) { + $task(); + } + } + + /** + * The task queue will be run and exhausted by default when the process + * exits IFF the exit is not the result of a PHP E_ERROR error. + * + * You can disable running the automatic shutdown of the queue by calling + * this function. If you disable the task queue shutdown process, then you + * MUST either run the task queue (as a result of running your event loop + * or manually using the run() method) or wait on each outstanding promise. + * + * Note: This shutdown will occur before any destructors are triggered. + */ + public function disableShutdown() + { + $this->enableShutdown = false; + } +} diff --git a/instafeed/vendor/guzzlehttp/promises/src/TaskQueueInterface.php b/instafeed/vendor/guzzlehttp/promises/src/TaskQueueInterface.php new file mode 100755 index 0000000..ac8306e --- /dev/null +++ b/instafeed/vendor/guzzlehttp/promises/src/TaskQueueInterface.php @@ -0,0 +1,25 @@ + + * while ($eventLoop->isRunning()) { + * GuzzleHttp\Promise\queue()->run(); + * } + * + * + * @param TaskQueueInterface $assign Optionally specify a new queue instance. + * + * @return TaskQueueInterface + */ +function queue(TaskQueueInterface $assign = null) +{ + static $queue; + + if ($assign) { + $queue = $assign; + } elseif (!$queue) { + $queue = new TaskQueue(); + } + + return $queue; +} + +/** + * Adds a function to run in the task queue when it is next `run()` and returns + * a promise that is fulfilled or rejected with the result. + * + * @param callable $task Task function to run. + * + * @return PromiseInterface + */ +function task(callable $task) +{ + $queue = queue(); + $promise = new Promise([$queue, 'run']); + $queue->add(function () use ($task, $promise) { + try { + $promise->resolve($task()); + } catch (\Throwable $e) { + $promise->reject($e); + } catch (\Exception $e) { + $promise->reject($e); + } + }); + + return $promise; +} + +/** + * Creates a promise for a value if the value is not a promise. + * + * @param mixed $value Promise or value. + * + * @return PromiseInterface + */ +function promise_for($value) +{ + if ($value instanceof PromiseInterface) { + return $value; + } + + // Return a Guzzle promise that shadows the given promise. + if (method_exists($value, 'then')) { + $wfn = method_exists($value, 'wait') ? [$value, 'wait'] : null; + $cfn = method_exists($value, 'cancel') ? [$value, 'cancel'] : null; + $promise = new Promise($wfn, $cfn); + $value->then([$promise, 'resolve'], [$promise, 'reject']); + return $promise; + } + + return new FulfilledPromise($value); +} + +/** + * Creates a rejected promise for a reason if the reason is not a promise. If + * the provided reason is a promise, then it is returned as-is. + * + * @param mixed $reason Promise or reason. + * + * @return PromiseInterface + */ +function rejection_for($reason) +{ + if ($reason instanceof PromiseInterface) { + return $reason; + } + + return new RejectedPromise($reason); +} + +/** + * Create an exception for a rejected promise value. + * + * @param mixed $reason + * + * @return \Exception|\Throwable + */ +function exception_for($reason) +{ + return $reason instanceof \Exception || $reason instanceof \Throwable + ? $reason + : new RejectionException($reason); +} + +/** + * Returns an iterator for the given value. + * + * @param mixed $value + * + * @return \Iterator + */ +function iter_for($value) +{ + if ($value instanceof \Iterator) { + return $value; + } elseif (is_array($value)) { + return new \ArrayIterator($value); + } else { + return new \ArrayIterator([$value]); + } +} + +/** + * Synchronously waits on a promise to resolve and returns an inspection state + * array. + * + * Returns a state associative array containing a "state" key mapping to a + * valid promise state. If the state of the promise is "fulfilled", the array + * will contain a "value" key mapping to the fulfilled value of the promise. If + * the promise is rejected, the array will contain a "reason" key mapping to + * the rejection reason of the promise. + * + * @param PromiseInterface $promise Promise or value. + * + * @return array + */ +function inspect(PromiseInterface $promise) +{ + try { + return [ + 'state' => PromiseInterface::FULFILLED, + 'value' => $promise->wait() + ]; + } catch (RejectionException $e) { + return ['state' => PromiseInterface::REJECTED, 'reason' => $e->getReason()]; + } catch (\Throwable $e) { + return ['state' => PromiseInterface::REJECTED, 'reason' => $e]; + } catch (\Exception $e) { + return ['state' => PromiseInterface::REJECTED, 'reason' => $e]; + } +} + +/** + * Waits on all of the provided promises, but does not unwrap rejected promises + * as thrown exception. + * + * Returns an array of inspection state arrays. + * + * @param PromiseInterface[] $promises Traversable of promises to wait upon. + * + * @return array + * @see GuzzleHttp\Promise\inspect for the inspection state array format. + */ +function inspect_all($promises) +{ + $results = []; + foreach ($promises as $key => $promise) { + $results[$key] = inspect($promise); + } + + return $results; +} + +/** + * Waits on all of the provided promises and returns the fulfilled values. + * + * Returns an array that contains the value of each promise (in the same order + * the promises were provided). An exception is thrown if any of the promises + * are rejected. + * + * @param mixed $promises Iterable of PromiseInterface objects to wait on. + * + * @return array + * @throws \Exception on error + * @throws \Throwable on error in PHP >=7 + */ +function unwrap($promises) +{ + $results = []; + foreach ($promises as $key => $promise) { + $results[$key] = $promise->wait(); + } + + return $results; +} + +/** + * Given an array of promises, return a promise that is fulfilled when all the + * items in the array are fulfilled. + * + * The promise's fulfillment value is an array with fulfillment values at + * respective positions to the original array. If any promise in the array + * rejects, the returned promise is rejected with the rejection reason. + * + * @param mixed $promises Promises or values. + * + * @return PromiseInterface + */ +function all($promises) +{ + $results = []; + return each( + $promises, + function ($value, $idx) use (&$results) { + $results[$idx] = $value; + }, + function ($reason, $idx, Promise $aggregate) { + $aggregate->reject($reason); + } + )->then(function () use (&$results) { + ksort($results); + return $results; + }); +} + +/** + * Initiate a competitive race between multiple promises or values (values will + * become immediately fulfilled promises). + * + * When count amount of promises have been fulfilled, the returned promise is + * fulfilled with an array that contains the fulfillment values of the winners + * in order of resolution. + * + * This prommise is rejected with a {@see GuzzleHttp\Promise\AggregateException} + * if the number of fulfilled promises is less than the desired $count. + * + * @param int $count Total number of promises. + * @param mixed $promises Promises or values. + * + * @return PromiseInterface + */ +function some($count, $promises) +{ + $results = []; + $rejections = []; + + return each( + $promises, + function ($value, $idx, PromiseInterface $p) use (&$results, $count) { + if ($p->getState() !== PromiseInterface::PENDING) { + return; + } + $results[$idx] = $value; + if (count($results) >= $count) { + $p->resolve(null); + } + }, + function ($reason) use (&$rejections) { + $rejections[] = $reason; + } + )->then( + function () use (&$results, &$rejections, $count) { + if (count($results) !== $count) { + throw new AggregateException( + 'Not enough promises to fulfill count', + $rejections + ); + } + ksort($results); + return array_values($results); + } + ); +} + +/** + * Like some(), with 1 as count. However, if the promise fulfills, the + * fulfillment value is not an array of 1 but the value directly. + * + * @param mixed $promises Promises or values. + * + * @return PromiseInterface + */ +function any($promises) +{ + return some(1, $promises)->then(function ($values) { return $values[0]; }); +} + +/** + * Returns a promise that is fulfilled when all of the provided promises have + * been fulfilled or rejected. + * + * The returned promise is fulfilled with an array of inspection state arrays. + * + * @param mixed $promises Promises or values. + * + * @return PromiseInterface + * @see GuzzleHttp\Promise\inspect for the inspection state array format. + */ +function settle($promises) +{ + $results = []; + + return each( + $promises, + function ($value, $idx) use (&$results) { + $results[$idx] = ['state' => PromiseInterface::FULFILLED, 'value' => $value]; + }, + function ($reason, $idx) use (&$results) { + $results[$idx] = ['state' => PromiseInterface::REJECTED, 'reason' => $reason]; + } + )->then(function () use (&$results) { + ksort($results); + return $results; + }); +} + +/** + * Given an iterator that yields promises or values, returns a promise that is + * fulfilled with a null value when the iterator has been consumed or the + * aggregate promise has been fulfilled or rejected. + * + * $onFulfilled is a function that accepts the fulfilled value, iterator + * index, and the aggregate promise. The callback can invoke any necessary side + * effects and choose to resolve or reject the aggregate promise if needed. + * + * $onRejected is a function that accepts the rejection reason, iterator + * index, and the aggregate promise. The callback can invoke any necessary side + * effects and choose to resolve or reject the aggregate promise if needed. + * + * @param mixed $iterable Iterator or array to iterate over. + * @param callable $onFulfilled + * @param callable $onRejected + * + * @return PromiseInterface + */ +function each( + $iterable, + callable $onFulfilled = null, + callable $onRejected = null +) { + return (new EachPromise($iterable, [ + 'fulfilled' => $onFulfilled, + 'rejected' => $onRejected + ]))->promise(); +} + +/** + * Like each, but only allows a certain number of outstanding promises at any + * given time. + * + * $concurrency may be an integer or a function that accepts the number of + * pending promises and returns a numeric concurrency limit value to allow for + * dynamic a concurrency size. + * + * @param mixed $iterable + * @param int|callable $concurrency + * @param callable $onFulfilled + * @param callable $onRejected + * + * @return PromiseInterface + */ +function each_limit( + $iterable, + $concurrency, + callable $onFulfilled = null, + callable $onRejected = null +) { + return (new EachPromise($iterable, [ + 'fulfilled' => $onFulfilled, + 'rejected' => $onRejected, + 'concurrency' => $concurrency + ]))->promise(); +} + +/** + * Like each_limit, but ensures that no promise in the given $iterable argument + * is rejected. If any promise is rejected, then the aggregate promise is + * rejected with the encountered rejection. + * + * @param mixed $iterable + * @param int|callable $concurrency + * @param callable $onFulfilled + * + * @return PromiseInterface + */ +function each_limit_all( + $iterable, + $concurrency, + callable $onFulfilled = null +) { + return each_limit( + $iterable, + $concurrency, + $onFulfilled, + function ($reason, $idx, PromiseInterface $aggregate) { + $aggregate->reject($reason); + } + ); +} + +/** + * Returns true if a promise is fulfilled. + * + * @param PromiseInterface $promise + * + * @return bool + */ +function is_fulfilled(PromiseInterface $promise) +{ + return $promise->getState() === PromiseInterface::FULFILLED; +} + +/** + * Returns true if a promise is rejected. + * + * @param PromiseInterface $promise + * + * @return bool + */ +function is_rejected(PromiseInterface $promise) +{ + return $promise->getState() === PromiseInterface::REJECTED; +} + +/** + * Returns true if a promise is fulfilled or rejected. + * + * @param PromiseInterface $promise + * + * @return bool + */ +function is_settled(PromiseInterface $promise) +{ + return $promise->getState() !== PromiseInterface::PENDING; +} + +/** + * @see Coroutine + * + * @param callable $generatorFn + * + * @return PromiseInterface + */ +function coroutine(callable $generatorFn) +{ + return new Coroutine($generatorFn); +} diff --git a/instafeed/vendor/guzzlehttp/promises/src/functions_include.php b/instafeed/vendor/guzzlehttp/promises/src/functions_include.php new file mode 100755 index 0000000..34cd171 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/promises/src/functions_include.php @@ -0,0 +1,6 @@ +withPath('foo')->withHost('example.com')` will throw an exception + because the path of a URI with an authority must start with a slash "/" or be empty + - `(new Uri())->withScheme('http')` will return `'http://localhost'` + +### Deprecated + +- `Uri::resolve` in favor of `UriResolver::resolve` +- `Uri::removeDotSegments` in favor of `UriResolver::removeDotSegments` + +### Fixed + +- `Stream::read` when length parameter <= 0. +- `copy_to_stream` reads bytes in chunks instead of `maxLen` into memory. +- `ServerRequest::getUriFromGlobals` when `Host` header contains port. +- Compatibility of URIs with `file` scheme and empty host. + + +## [1.3.1] - 2016-06-25 + +### Fixed + +- `Uri::__toString` for network path references, e.g. `//example.org`. +- Missing lowercase normalization for host. +- Handling of URI components in case they are `'0'` in a lot of places, + e.g. as a user info password. +- `Uri::withAddedHeader` to correctly merge headers with different case. +- Trimming of header values in `Uri::withAddedHeader`. Header values may + be surrounded by whitespace which should be ignored according to RFC 7230 + Section 3.2.4. This does not apply to header names. +- `Uri::withAddedHeader` with an array of header values. +- `Uri::resolve` when base path has no slash and handling of fragment. +- Handling of encoding in `Uri::with(out)QueryValue` so one can pass the + key/value both in encoded as well as decoded form to those methods. This is + consistent with withPath, withQuery etc. +- `ServerRequest::withoutAttribute` when attribute value is null. + + +## [1.3.0] - 2016-04-13 + +### Added + +- Remaining interfaces needed for full PSR7 compatibility + (ServerRequestInterface, UploadedFileInterface, etc.). +- Support for stream_for from scalars. + +### Changed + +- Can now extend Uri. + +### Fixed +- A bug in validating request methods by making it more permissive. + + +## [1.2.3] - 2016-02-18 + +### Fixed + +- Support in `GuzzleHttp\Psr7\CachingStream` for seeking forward on remote + streams, which can sometimes return fewer bytes than requested with `fread`. +- Handling of gzipped responses with FNAME headers. + + +## [1.2.2] - 2016-01-22 + +### Added + +- Support for URIs without any authority. +- Support for HTTP 451 'Unavailable For Legal Reasons.' +- Support for using '0' as a filename. +- Support for including non-standard ports in Host headers. + + +## [1.2.1] - 2015-11-02 + +### Changes + +- Now supporting negative offsets when seeking to SEEK_END. + + +## [1.2.0] - 2015-08-15 + +### Changed + +- Body as `"0"` is now properly added to a response. +- Now allowing forward seeking in CachingStream. +- Now properly parsing HTTP requests that contain proxy targets in + `parse_request`. +- functions.php is now conditionally required. +- user-info is no longer dropped when resolving URIs. + + +## [1.1.0] - 2015-06-24 + +### Changed + +- URIs can now be relative. +- `multipart/form-data` headers are now overridden case-insensitively. +- URI paths no longer encode the following characters because they are allowed + in URIs: "(", ")", "*", "!", "'" +- A port is no longer added to a URI when the scheme is missing and no port is + present. + + +## 1.0.0 - 2015-05-19 + +Initial release. + +Currently unsupported: + +- `Psr\Http\Message\ServerRequestInterface` +- `Psr\Http\Message\UploadedFileInterface` + + + +[Unreleased]: https://github.com/guzzle/psr7/compare/1.6.0...HEAD +[1.6.0]: https://github.com/guzzle/psr7/compare/1.5.2...1.6.0 +[1.5.2]: https://github.com/guzzle/psr7/compare/1.5.1...1.5.2 +[1.5.1]: https://github.com/guzzle/psr7/compare/1.5.0...1.5.1 +[1.5.0]: https://github.com/guzzle/psr7/compare/1.4.2...1.5.0 +[1.4.2]: https://github.com/guzzle/psr7/compare/1.4.1...1.4.2 +[1.4.1]: https://github.com/guzzle/psr7/compare/1.4.0...1.4.1 +[1.4.0]: https://github.com/guzzle/psr7/compare/1.3.1...1.4.0 +[1.3.1]: https://github.com/guzzle/psr7/compare/1.3.0...1.3.1 +[1.3.0]: https://github.com/guzzle/psr7/compare/1.2.3...1.3.0 +[1.2.3]: https://github.com/guzzle/psr7/compare/1.2.2...1.2.3 +[1.2.2]: https://github.com/guzzle/psr7/compare/1.2.1...1.2.2 +[1.2.1]: https://github.com/guzzle/psr7/compare/1.2.0...1.2.1 +[1.2.0]: https://github.com/guzzle/psr7/compare/1.1.0...1.2.0 +[1.1.0]: https://github.com/guzzle/psr7/compare/1.0.0...1.1.0 diff --git a/instafeed/vendor/guzzlehttp/psr7/LICENSE b/instafeed/vendor/guzzlehttp/psr7/LICENSE new file mode 100755 index 0000000..581d95f --- /dev/null +++ b/instafeed/vendor/guzzlehttp/psr7/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2015 Michael Dowling, https://github.com/mtdowling + +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/instafeed/vendor/guzzlehttp/psr7/README.md b/instafeed/vendor/guzzlehttp/psr7/README.md new file mode 100755 index 0000000..c60a6a3 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/psr7/README.md @@ -0,0 +1,745 @@ +# PSR-7 Message Implementation + +This repository contains a full [PSR-7](http://www.php-fig.org/psr/psr-7/) +message implementation, several stream decorators, and some helpful +functionality like query string parsing. + + +[![Build Status](https://travis-ci.org/guzzle/psr7.svg?branch=master)](https://travis-ci.org/guzzle/psr7) + + +# Stream implementation + +This package comes with a number of stream implementations and stream +decorators. + + +## AppendStream + +`GuzzleHttp\Psr7\AppendStream` + +Reads from multiple streams, one after the other. + +```php +use GuzzleHttp\Psr7; + +$a = Psr7\stream_for('abc, '); +$b = Psr7\stream_for('123.'); +$composed = new Psr7\AppendStream([$a, $b]); + +$composed->addStream(Psr7\stream_for(' Above all listen to me')); + +echo $composed; // abc, 123. Above all listen to me. +``` + + +## BufferStream + +`GuzzleHttp\Psr7\BufferStream` + +Provides a buffer stream that can be written to fill a buffer, and read +from to remove bytes from the buffer. + +This stream returns a "hwm" metadata value that tells upstream consumers +what the configured high water mark of the stream is, or the maximum +preferred size of the buffer. + +```php +use GuzzleHttp\Psr7; + +// When more than 1024 bytes are in the buffer, it will begin returning +// false to writes. This is an indication that writers should slow down. +$buffer = new Psr7\BufferStream(1024); +``` + + +## CachingStream + +The CachingStream is used to allow seeking over previously read bytes on +non-seekable streams. This can be useful when transferring a non-seekable +entity body fails due to needing to rewind the stream (for example, resulting +from a redirect). Data that is read from the remote stream will be buffered in +a PHP temp stream so that previously read bytes are cached first in memory, +then on disk. + +```php +use GuzzleHttp\Psr7; + +$original = Psr7\stream_for(fopen('http://www.google.com', 'r')); +$stream = new Psr7\CachingStream($original); + +$stream->read(1024); +echo $stream->tell(); +// 1024 + +$stream->seek(0); +echo $stream->tell(); +// 0 +``` + + +## DroppingStream + +`GuzzleHttp\Psr7\DroppingStream` + +Stream decorator that begins dropping data once the size of the underlying +stream becomes too full. + +```php +use GuzzleHttp\Psr7; + +// Create an empty stream +$stream = Psr7\stream_for(); + +// Start dropping data when the stream has more than 10 bytes +$dropping = new Psr7\DroppingStream($stream, 10); + +$dropping->write('01234567890123456789'); +echo $stream; // 0123456789 +``` + + +## FnStream + +`GuzzleHttp\Psr7\FnStream` + +Compose stream implementations based on a hash of functions. + +Allows for easy testing and extension of a provided stream without needing +to create a concrete class for a simple extension point. + +```php + +use GuzzleHttp\Psr7; + +$stream = Psr7\stream_for('hi'); +$fnStream = Psr7\FnStream::decorate($stream, [ + 'rewind' => function () use ($stream) { + echo 'About to rewind - '; + $stream->rewind(); + echo 'rewound!'; + } +]); + +$fnStream->rewind(); +// Outputs: About to rewind - rewound! +``` + + +## InflateStream + +`GuzzleHttp\Psr7\InflateStream` + +Uses PHP's zlib.inflate filter to inflate deflate or gzipped content. + +This stream decorator skips the first 10 bytes of the given stream to remove +the gzip header, converts the provided stream to a PHP stream resource, +then appends the zlib.inflate filter. The stream is then converted back +to a Guzzle stream resource to be used as a Guzzle stream. + + +## LazyOpenStream + +`GuzzleHttp\Psr7\LazyOpenStream` + +Lazily reads or writes to a file that is opened only after an IO operation +take place on the stream. + +```php +use GuzzleHttp\Psr7; + +$stream = new Psr7\LazyOpenStream('/path/to/file', 'r'); +// The file has not yet been opened... + +echo $stream->read(10); +// The file is opened and read from only when needed. +``` + + +## LimitStream + +`GuzzleHttp\Psr7\LimitStream` + +LimitStream can be used to read a subset or slice of an existing stream object. +This can be useful for breaking a large file into smaller pieces to be sent in +chunks (e.g. Amazon S3's multipart upload API). + +```php +use GuzzleHttp\Psr7; + +$original = Psr7\stream_for(fopen('/tmp/test.txt', 'r+')); +echo $original->getSize(); +// >>> 1048576 + +// Limit the size of the body to 1024 bytes and start reading from byte 2048 +$stream = new Psr7\LimitStream($original, 1024, 2048); +echo $stream->getSize(); +// >>> 1024 +echo $stream->tell(); +// >>> 0 +``` + + +## MultipartStream + +`GuzzleHttp\Psr7\MultipartStream` + +Stream that when read returns bytes for a streaming multipart or +multipart/form-data stream. + + +## NoSeekStream + +`GuzzleHttp\Psr7\NoSeekStream` + +NoSeekStream wraps a stream and does not allow seeking. + +```php +use GuzzleHttp\Psr7; + +$original = Psr7\stream_for('foo'); +$noSeek = new Psr7\NoSeekStream($original); + +echo $noSeek->read(3); +// foo +var_export($noSeek->isSeekable()); +// false +$noSeek->seek(0); +var_export($noSeek->read(3)); +// NULL +``` + + +## PumpStream + +`GuzzleHttp\Psr7\PumpStream` + +Provides a read only stream that pumps data from a PHP callable. + +When invoking the provided callable, the PumpStream will pass the amount of +data requested to read to the callable. The callable can choose to ignore +this value and return fewer or more bytes than requested. Any extra data +returned by the provided callable is buffered internally until drained using +the read() function of the PumpStream. The provided callable MUST return +false when there is no more data to read. + + +## Implementing stream decorators + +Creating a stream decorator is very easy thanks to the +`GuzzleHttp\Psr7\StreamDecoratorTrait`. This trait provides methods that +implement `Psr\Http\Message\StreamInterface` by proxying to an underlying +stream. Just `use` the `StreamDecoratorTrait` and implement your custom +methods. + +For example, let's say we wanted to call a specific function each time the last +byte is read from a stream. This could be implemented by overriding the +`read()` method. + +```php +use Psr\Http\Message\StreamInterface; +use GuzzleHttp\Psr7\StreamDecoratorTrait; + +class EofCallbackStream implements StreamInterface +{ + use StreamDecoratorTrait; + + private $callback; + + public function __construct(StreamInterface $stream, callable $cb) + { + $this->stream = $stream; + $this->callback = $cb; + } + + public function read($length) + { + $result = $this->stream->read($length); + + // Invoke the callback when EOF is hit. + if ($this->eof()) { + call_user_func($this->callback); + } + + return $result; + } +} +``` + +This decorator could be added to any existing stream and used like so: + +```php +use GuzzleHttp\Psr7; + +$original = Psr7\stream_for('foo'); + +$eofStream = new EofCallbackStream($original, function () { + echo 'EOF!'; +}); + +$eofStream->read(2); +$eofStream->read(1); +// echoes "EOF!" +$eofStream->seek(0); +$eofStream->read(3); +// echoes "EOF!" +``` + + +## PHP StreamWrapper + +You can use the `GuzzleHttp\Psr7\StreamWrapper` class if you need to use a +PSR-7 stream as a PHP stream resource. + +Use the `GuzzleHttp\Psr7\StreamWrapper::getResource()` method to create a PHP +stream from a PSR-7 stream. + +```php +use GuzzleHttp\Psr7\StreamWrapper; + +$stream = GuzzleHttp\Psr7\stream_for('hello!'); +$resource = StreamWrapper::getResource($stream); +echo fread($resource, 6); // outputs hello! +``` + + +# Function API + +There are various functions available under the `GuzzleHttp\Psr7` namespace. + + +## `function str` + +`function str(MessageInterface $message)` + +Returns the string representation of an HTTP message. + +```php +$request = new GuzzleHttp\Psr7\Request('GET', 'http://example.com'); +echo GuzzleHttp\Psr7\str($request); +``` + + +## `function uri_for` + +`function uri_for($uri)` + +This function accepts a string or `Psr\Http\Message\UriInterface` and returns a +UriInterface for the given value. If the value is already a `UriInterface`, it +is returned as-is. + +```php +$uri = GuzzleHttp\Psr7\uri_for('http://example.com'); +assert($uri === GuzzleHttp\Psr7\uri_for($uri)); +``` + + +## `function stream_for` + +`function stream_for($resource = '', array $options = [])` + +Create a new stream based on the input type. + +Options is an associative array that can contain the following keys: + +* - metadata: Array of custom metadata. +* - size: Size of the stream. + +This method accepts the following `$resource` types: + +- `Psr\Http\Message\StreamInterface`: Returns the value as-is. +- `string`: Creates a stream object that uses the given string as the contents. +- `resource`: Creates a stream object that wraps the given PHP stream resource. +- `Iterator`: If the provided value implements `Iterator`, then a read-only + stream object will be created that wraps the given iterable. Each time the + stream is read from, data from the iterator will fill a buffer and will be + continuously called until the buffer is equal to the requested read size. + Subsequent read calls will first read from the buffer and then call `next` + on the underlying iterator until it is exhausted. +- `object` with `__toString()`: If the object has the `__toString()` method, + the object will be cast to a string and then a stream will be returned that + uses the string value. +- `NULL`: When `null` is passed, an empty stream object is returned. +- `callable` When a callable is passed, a read-only stream object will be + created that invokes the given callable. The callable is invoked with the + number of suggested bytes to read. The callable can return any number of + bytes, but MUST return `false` when there is no more data to return. The + stream object that wraps the callable will invoke the callable until the + number of requested bytes are available. Any additional bytes will be + buffered and used in subsequent reads. + +```php +$stream = GuzzleHttp\Psr7\stream_for('foo'); +$stream = GuzzleHttp\Psr7\stream_for(fopen('/path/to/file', 'r')); + +$generator = function ($bytes) { + for ($i = 0; $i < $bytes; $i++) { + yield ' '; + } +} + +$stream = GuzzleHttp\Psr7\stream_for($generator(100)); +``` + + +## `function parse_header` + +`function parse_header($header)` + +Parse an array of header values containing ";" separated data into an array of +associative arrays representing the header key value pair data of the header. +When a parameter does not contain a value, but just contains a key, this +function will inject a key with a '' string value. + + +## `function normalize_header` + +`function normalize_header($header)` + +Converts an array of header values that may contain comma separated headers +into an array of headers with no comma separated values. + + +## `function modify_request` + +`function modify_request(RequestInterface $request, array $changes)` + +Clone and modify a request with the given changes. This method is useful for +reducing the number of clones needed to mutate a message. + +The changes can be one of: + +- method: (string) Changes the HTTP method. +- set_headers: (array) Sets the given headers. +- remove_headers: (array) Remove the given headers. +- body: (mixed) Sets the given body. +- uri: (UriInterface) Set the URI. +- query: (string) Set the query string value of the URI. +- version: (string) Set the protocol version. + + +## `function rewind_body` + +`function rewind_body(MessageInterface $message)` + +Attempts to rewind a message body and throws an exception on failure. The body +of the message will only be rewound if a call to `tell()` returns a value other +than `0`. + + +## `function try_fopen` + +`function try_fopen($filename, $mode)` + +Safely opens a PHP stream resource using a filename. + +When fopen fails, PHP normally raises a warning. This function adds an error +handler that checks for errors and throws an exception instead. + + +## `function copy_to_string` + +`function copy_to_string(StreamInterface $stream, $maxLen = -1)` + +Copy the contents of a stream into a string until the given number of bytes +have been read. + + +## `function copy_to_stream` + +`function copy_to_stream(StreamInterface $source, StreamInterface $dest, $maxLen = -1)` + +Copy the contents of a stream into another stream until the given number of +bytes have been read. + + +## `function hash` + +`function hash(StreamInterface $stream, $algo, $rawOutput = false)` + +Calculate a hash of a Stream. This method reads the entire stream to calculate +a rolling hash (based on PHP's hash_init functions). + + +## `function readline` + +`function readline(StreamInterface $stream, $maxLength = null)` + +Read a line from the stream up to the maximum allowed buffer length. + + +## `function parse_request` + +`function parse_request($message)` + +Parses a request message string into a request object. + + +## `function parse_response` + +`function parse_response($message)` + +Parses a response message string into a response object. + + +## `function parse_query` + +`function parse_query($str, $urlEncoding = true)` + +Parse a query string into an associative array. + +If multiple values are found for the same key, the value of that key value pair +will become an array. This function does not parse nested PHP style arrays into +an associative array (e.g., `foo[a]=1&foo[b]=2` will be parsed into +`['foo[a]' => '1', 'foo[b]' => '2']`). + + +## `function build_query` + +`function build_query(array $params, $encoding = PHP_QUERY_RFC3986)` + +Build a query string from an array of key value pairs. + +This function can use the return value of parse_query() to build a query string. +This function does not modify the provided keys when an array is encountered +(like http_build_query would). + + +## `function mimetype_from_filename` + +`function mimetype_from_filename($filename)` + +Determines the mimetype of a file by looking at its extension. + + +## `function mimetype_from_extension` + +`function mimetype_from_extension($extension)` + +Maps a file extensions to a mimetype. + + +# Additional URI Methods + +Aside from the standard `Psr\Http\Message\UriInterface` implementation in form of the `GuzzleHttp\Psr7\Uri` class, +this library also provides additional functionality when working with URIs as static methods. + +## URI Types + +An instance of `Psr\Http\Message\UriInterface` can either be an absolute URI or a relative reference. +An absolute URI has a scheme. A relative reference is used to express a URI relative to another URI, +the base URI. Relative references can be divided into several forms according to +[RFC 3986 Section 4.2](https://tools.ietf.org/html/rfc3986#section-4.2): + +- network-path references, e.g. `//example.com/path` +- absolute-path references, e.g. `/path` +- relative-path references, e.g. `subpath` + +The following methods can be used to identify the type of the URI. + +### `GuzzleHttp\Psr7\Uri::isAbsolute` + +`public static function isAbsolute(UriInterface $uri): bool` + +Whether the URI is absolute, i.e. it has a scheme. + +### `GuzzleHttp\Psr7\Uri::isNetworkPathReference` + +`public static function isNetworkPathReference(UriInterface $uri): bool` + +Whether the URI is a network-path reference. A relative reference that begins with two slash characters is +termed an network-path reference. + +### `GuzzleHttp\Psr7\Uri::isAbsolutePathReference` + +`public static function isAbsolutePathReference(UriInterface $uri): bool` + +Whether the URI is a absolute-path reference. A relative reference that begins with a single slash character is +termed an absolute-path reference. + +### `GuzzleHttp\Psr7\Uri::isRelativePathReference` + +`public static function isRelativePathReference(UriInterface $uri): bool` + +Whether the URI is a relative-path reference. A relative reference that does not begin with a slash character is +termed a relative-path reference. + +### `GuzzleHttp\Psr7\Uri::isSameDocumentReference` + +`public static function isSameDocumentReference(UriInterface $uri, UriInterface $base = null): bool` + +Whether the URI is a same-document reference. A same-document reference refers to a URI that is, aside from its +fragment component, identical to the base URI. When no base URI is given, only an empty URI reference +(apart from its fragment) is considered a same-document reference. + +## URI Components + +Additional methods to work with URI components. + +### `GuzzleHttp\Psr7\Uri::isDefaultPort` + +`public static function isDefaultPort(UriInterface $uri): bool` + +Whether the URI has the default port of the current scheme. `Psr\Http\Message\UriInterface::getPort` may return null +or the standard port. This method can be used independently of the implementation. + +### `GuzzleHttp\Psr7\Uri::composeComponents` + +`public static function composeComponents($scheme, $authority, $path, $query, $fragment): string` + +Composes a URI reference string from its various components according to +[RFC 3986 Section 5.3](https://tools.ietf.org/html/rfc3986#section-5.3). Usually this method does not need to be called +manually but instead is used indirectly via `Psr\Http\Message\UriInterface::__toString`. + +### `GuzzleHttp\Psr7\Uri::fromParts` + +`public static function fromParts(array $parts): UriInterface` + +Creates a URI from a hash of [`parse_url`](http://php.net/manual/en/function.parse-url.php) components. + + +### `GuzzleHttp\Psr7\Uri::withQueryValue` + +`public static function withQueryValue(UriInterface $uri, $key, $value): UriInterface` + +Creates a new URI with a specific query string value. Any existing query string values that exactly match the +provided key are removed and replaced with the given key value pair. A value of null will set the query string +key without a value, e.g. "key" instead of "key=value". + +### `GuzzleHttp\Psr7\Uri::withQueryValues` + +`public static function withQueryValues(UriInterface $uri, array $keyValueArray): UriInterface` + +Creates a new URI with multiple query string values. It has the same behavior as `withQueryValue()` but for an +associative array of key => value. + +### `GuzzleHttp\Psr7\Uri::withoutQueryValue` + +`public static function withoutQueryValue(UriInterface $uri, $key): UriInterface` + +Creates a new URI with a specific query string value removed. Any existing query string values that exactly match the +provided key are removed. + +## Reference Resolution + +`GuzzleHttp\Psr7\UriResolver` provides methods to resolve a URI reference in the context of a base URI according +to [RFC 3986 Section 5](https://tools.ietf.org/html/rfc3986#section-5). This is for example also what web browsers +do when resolving a link in a website based on the current request URI. + +### `GuzzleHttp\Psr7\UriResolver::resolve` + +`public static function resolve(UriInterface $base, UriInterface $rel): UriInterface` + +Converts the relative URI into a new URI that is resolved against the base URI. + +### `GuzzleHttp\Psr7\UriResolver::removeDotSegments` + +`public static function removeDotSegments(string $path): string` + +Removes dot segments from a path and returns the new path according to +[RFC 3986 Section 5.2.4](https://tools.ietf.org/html/rfc3986#section-5.2.4). + +### `GuzzleHttp\Psr7\UriResolver::relativize` + +`public static function relativize(UriInterface $base, UriInterface $target): UriInterface` + +Returns the target URI as a relative reference from the base URI. This method is the counterpart to resolve(): + +```php +(string) $target === (string) UriResolver::resolve($base, UriResolver::relativize($base, $target)) +``` + +One use-case is to use the current request URI as base URI and then generate relative links in your documents +to reduce the document size or offer self-contained downloadable document archives. + +```php +$base = new Uri('http://example.com/a/b/'); +echo UriResolver::relativize($base, new Uri('http://example.com/a/b/c')); // prints 'c'. +echo UriResolver::relativize($base, new Uri('http://example.com/a/x/y')); // prints '../x/y'. +echo UriResolver::relativize($base, new Uri('http://example.com/a/b/?q')); // prints '?q'. +echo UriResolver::relativize($base, new Uri('http://example.org/a/b/')); // prints '//example.org/a/b/'. +``` + +## Normalization and Comparison + +`GuzzleHttp\Psr7\UriNormalizer` provides methods to normalize and compare URIs according to +[RFC 3986 Section 6](https://tools.ietf.org/html/rfc3986#section-6). + +### `GuzzleHttp\Psr7\UriNormalizer::normalize` + +`public static function normalize(UriInterface $uri, $flags = self::PRESERVING_NORMALIZATIONS): UriInterface` + +Returns a normalized URI. The scheme and host component are already normalized to lowercase per PSR-7 UriInterface. +This methods adds additional normalizations that can be configured with the `$flags` parameter which is a bitmask +of normalizations to apply. The following normalizations are available: + +- `UriNormalizer::PRESERVING_NORMALIZATIONS` + + Default normalizations which only include the ones that preserve semantics. + +- `UriNormalizer::CAPITALIZE_PERCENT_ENCODING` + + All letters within a percent-encoding triplet (e.g., "%3A") are case-insensitive, and should be capitalized. + + Example: `http://example.org/a%c2%b1b` → `http://example.org/a%C2%B1b` + +- `UriNormalizer::DECODE_UNRESERVED_CHARACTERS` + + Decodes percent-encoded octets of unreserved characters. For consistency, percent-encoded octets in the ranges of + ALPHA (%41–%5A and %61–%7A), DIGIT (%30–%39), hyphen (%2D), period (%2E), underscore (%5F), or tilde (%7E) should + not be created by URI producers and, when found in a URI, should be decoded to their corresponding unreserved + characters by URI normalizers. + + Example: `http://example.org/%7Eusern%61me/` → `http://example.org/~username/` + +- `UriNormalizer::CONVERT_EMPTY_PATH` + + Converts the empty path to "/" for http and https URIs. + + Example: `http://example.org` → `http://example.org/` + +- `UriNormalizer::REMOVE_DEFAULT_HOST` + + Removes the default host of the given URI scheme from the URI. Only the "file" scheme defines the default host + "localhost". All of `file:/myfile`, `file:///myfile`, and `file://localhost/myfile` are equivalent according to + RFC 3986. + + Example: `file://localhost/myfile` → `file:///myfile` + +- `UriNormalizer::REMOVE_DEFAULT_PORT` + + Removes the default port of the given URI scheme from the URI. + + Example: `http://example.org:80/` → `http://example.org/` + +- `UriNormalizer::REMOVE_DOT_SEGMENTS` + + Removes unnecessary dot-segments. Dot-segments in relative-path references are not removed as it would + change the semantics of the URI reference. + + Example: `http://example.org/../a/b/../c/./d.html` → `http://example.org/a/c/d.html` + +- `UriNormalizer::REMOVE_DUPLICATE_SLASHES` + + Paths which include two or more adjacent slashes are converted to one. Webservers usually ignore duplicate slashes + and treat those URIs equivalent. But in theory those URIs do not need to be equivalent. So this normalization + may change the semantics. Encoded slashes (%2F) are not removed. + + Example: `http://example.org//foo///bar.html` → `http://example.org/foo/bar.html` + +- `UriNormalizer::SORT_QUERY_PARAMETERS` + + Sort query parameters with their values in alphabetical order. However, the order of parameters in a URI may be + significant (this is not defined by the standard). So this normalization is not safe and may change the semantics + of the URI. + + Example: `?lang=en&article=fred` → `?article=fred&lang=en` + +### `GuzzleHttp\Psr7\UriNormalizer::isEquivalent` + +`public static function isEquivalent(UriInterface $uri1, UriInterface $uri2, $normalizations = self::PRESERVING_NORMALIZATIONS): bool` + +Whether two URIs can be considered equivalent. Both URIs are normalized automatically before comparison with the given +`$normalizations` bitmask. The method also accepts relative URI references and returns true when they are equivalent. +This of course assumes they will be resolved against the same base URI. If this is not the case, determination of +equivalence or difference of relative references does not mean anything. diff --git a/instafeed/vendor/guzzlehttp/psr7/composer.json b/instafeed/vendor/guzzlehttp/psr7/composer.json new file mode 100755 index 0000000..168a055 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/psr7/composer.json @@ -0,0 +1,49 @@ +{ + "name": "guzzlehttp/psr7", + "type": "library", + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": ["request", "response", "message", "stream", "http", "uri", "url", "psr-7"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Schultze", + "homepage": "https://github.com/Tobion" + } + ], + "require": { + "php": ">=5.4.0", + "psr/http-message": "~1.0", + "ralouphie/getallheaders": "^2.0.5 || ^3.0.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8", + "ext-zlib": "*" + }, + "provide": { + "psr/http-message-implementation": "1.0" + }, + "suggest": { + "zendframework/zend-httphandlerrunner": "Emit PSR-7 responses" + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + }, + "files": ["src/functions_include.php"] + }, + "autoload-dev": { + "psr-4": { + "GuzzleHttp\\Tests\\Psr7\\": "tests/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.6-dev" + } + } +} diff --git a/instafeed/vendor/guzzlehttp/psr7/src/AppendStream.php b/instafeed/vendor/guzzlehttp/psr7/src/AppendStream.php new file mode 100755 index 0000000..472a0d6 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/psr7/src/AppendStream.php @@ -0,0 +1,241 @@ +addStream($stream); + } + } + + public function __toString() + { + try { + $this->rewind(); + return $this->getContents(); + } catch (\Exception $e) { + return ''; + } + } + + /** + * Add a stream to the AppendStream + * + * @param StreamInterface $stream Stream to append. Must be readable. + * + * @throws \InvalidArgumentException if the stream is not readable + */ + public function addStream(StreamInterface $stream) + { + if (!$stream->isReadable()) { + throw new \InvalidArgumentException('Each stream must be readable'); + } + + // The stream is only seekable if all streams are seekable + if (!$stream->isSeekable()) { + $this->seekable = false; + } + + $this->streams[] = $stream; + } + + public function getContents() + { + return copy_to_string($this); + } + + /** + * Closes each attached stream. + * + * {@inheritdoc} + */ + public function close() + { + $this->pos = $this->current = 0; + $this->seekable = true; + + foreach ($this->streams as $stream) { + $stream->close(); + } + + $this->streams = []; + } + + /** + * Detaches each attached stream. + * + * Returns null as it's not clear which underlying stream resource to return. + * + * {@inheritdoc} + */ + public function detach() + { + $this->pos = $this->current = 0; + $this->seekable = true; + + foreach ($this->streams as $stream) { + $stream->detach(); + } + + $this->streams = []; + } + + public function tell() + { + return $this->pos; + } + + /** + * Tries to calculate the size by adding the size of each stream. + * + * If any of the streams do not return a valid number, then the size of the + * append stream cannot be determined and null is returned. + * + * {@inheritdoc} + */ + public function getSize() + { + $size = 0; + + foreach ($this->streams as $stream) { + $s = $stream->getSize(); + if ($s === null) { + return null; + } + $size += $s; + } + + return $size; + } + + public function eof() + { + return !$this->streams || + ($this->current >= count($this->streams) - 1 && + $this->streams[$this->current]->eof()); + } + + public function rewind() + { + $this->seek(0); + } + + /** + * Attempts to seek to the given position. Only supports SEEK_SET. + * + * {@inheritdoc} + */ + public function seek($offset, $whence = SEEK_SET) + { + if (!$this->seekable) { + throw new \RuntimeException('This AppendStream is not seekable'); + } elseif ($whence !== SEEK_SET) { + throw new \RuntimeException('The AppendStream can only seek with SEEK_SET'); + } + + $this->pos = $this->current = 0; + + // Rewind each stream + foreach ($this->streams as $i => $stream) { + try { + $stream->rewind(); + } catch (\Exception $e) { + throw new \RuntimeException('Unable to seek stream ' + . $i . ' of the AppendStream', 0, $e); + } + } + + // Seek to the actual position by reading from each stream + while ($this->pos < $offset && !$this->eof()) { + $result = $this->read(min(8096, $offset - $this->pos)); + if ($result === '') { + break; + } + } + } + + /** + * Reads from all of the appended streams until the length is met or EOF. + * + * {@inheritdoc} + */ + public function read($length) + { + $buffer = ''; + $total = count($this->streams) - 1; + $remaining = $length; + $progressToNext = false; + + while ($remaining > 0) { + + // Progress to the next stream if needed. + if ($progressToNext || $this->streams[$this->current]->eof()) { + $progressToNext = false; + if ($this->current === $total) { + break; + } + $this->current++; + } + + $result = $this->streams[$this->current]->read($remaining); + + // Using a loose comparison here to match on '', false, and null + if ($result == null) { + $progressToNext = true; + continue; + } + + $buffer .= $result; + $remaining = $length - strlen($buffer); + } + + $this->pos += strlen($buffer); + + return $buffer; + } + + public function isReadable() + { + return true; + } + + public function isWritable() + { + return false; + } + + public function isSeekable() + { + return $this->seekable; + } + + public function write($string) + { + throw new \RuntimeException('Cannot write to an AppendStream'); + } + + public function getMetadata($key = null) + { + return $key ? null : []; + } +} diff --git a/instafeed/vendor/guzzlehttp/psr7/src/BufferStream.php b/instafeed/vendor/guzzlehttp/psr7/src/BufferStream.php new file mode 100755 index 0000000..af4d4c2 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/psr7/src/BufferStream.php @@ -0,0 +1,137 @@ +hwm = $hwm; + } + + public function __toString() + { + return $this->getContents(); + } + + public function getContents() + { + $buffer = $this->buffer; + $this->buffer = ''; + + return $buffer; + } + + public function close() + { + $this->buffer = ''; + } + + public function detach() + { + $this->close(); + } + + public function getSize() + { + return strlen($this->buffer); + } + + public function isReadable() + { + return true; + } + + public function isWritable() + { + return true; + } + + public function isSeekable() + { + return false; + } + + public function rewind() + { + $this->seek(0); + } + + public function seek($offset, $whence = SEEK_SET) + { + throw new \RuntimeException('Cannot seek a BufferStream'); + } + + public function eof() + { + return strlen($this->buffer) === 0; + } + + public function tell() + { + throw new \RuntimeException('Cannot determine the position of a BufferStream'); + } + + /** + * Reads data from the buffer. + */ + public function read($length) + { + $currentLength = strlen($this->buffer); + + if ($length >= $currentLength) { + // No need to slice the buffer because we don't have enough data. + $result = $this->buffer; + $this->buffer = ''; + } else { + // Slice up the result to provide a subset of the buffer. + $result = substr($this->buffer, 0, $length); + $this->buffer = substr($this->buffer, $length); + } + + return $result; + } + + /** + * Writes data to the buffer. + */ + public function write($string) + { + $this->buffer .= $string; + + // TODO: What should happen here? + if (strlen($this->buffer) >= $this->hwm) { + return false; + } + + return strlen($string); + } + + public function getMetadata($key = null) + { + if ($key == 'hwm') { + return $this->hwm; + } + + return $key ? null : []; + } +} diff --git a/instafeed/vendor/guzzlehttp/psr7/src/CachingStream.php b/instafeed/vendor/guzzlehttp/psr7/src/CachingStream.php new file mode 100755 index 0000000..ed68f08 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/psr7/src/CachingStream.php @@ -0,0 +1,138 @@ +remoteStream = $stream; + $this->stream = $target ?: new Stream(fopen('php://temp', 'r+')); + } + + public function getSize() + { + return max($this->stream->getSize(), $this->remoteStream->getSize()); + } + + public function rewind() + { + $this->seek(0); + } + + public function seek($offset, $whence = SEEK_SET) + { + if ($whence == SEEK_SET) { + $byte = $offset; + } elseif ($whence == SEEK_CUR) { + $byte = $offset + $this->tell(); + } elseif ($whence == SEEK_END) { + $size = $this->remoteStream->getSize(); + if ($size === null) { + $size = $this->cacheEntireStream(); + } + $byte = $size + $offset; + } else { + throw new \InvalidArgumentException('Invalid whence'); + } + + $diff = $byte - $this->stream->getSize(); + + if ($diff > 0) { + // Read the remoteStream until we have read in at least the amount + // of bytes requested, or we reach the end of the file. + while ($diff > 0 && !$this->remoteStream->eof()) { + $this->read($diff); + $diff = $byte - $this->stream->getSize(); + } + } else { + // We can just do a normal seek since we've already seen this byte. + $this->stream->seek($byte); + } + } + + public function read($length) + { + // Perform a regular read on any previously read data from the buffer + $data = $this->stream->read($length); + $remaining = $length - strlen($data); + + // More data was requested so read from the remote stream + if ($remaining) { + // If data was written to the buffer in a position that would have + // been filled from the remote stream, then we must skip bytes on + // the remote stream to emulate overwriting bytes from that + // position. This mimics the behavior of other PHP stream wrappers. + $remoteData = $this->remoteStream->read( + $remaining + $this->skipReadBytes + ); + + if ($this->skipReadBytes) { + $len = strlen($remoteData); + $remoteData = substr($remoteData, $this->skipReadBytes); + $this->skipReadBytes = max(0, $this->skipReadBytes - $len); + } + + $data .= $remoteData; + $this->stream->write($remoteData); + } + + return $data; + } + + public function write($string) + { + // When appending to the end of the currently read stream, you'll want + // to skip bytes from being read from the remote stream to emulate + // other stream wrappers. Basically replacing bytes of data of a fixed + // length. + $overflow = (strlen($string) + $this->tell()) - $this->remoteStream->tell(); + if ($overflow > 0) { + $this->skipReadBytes += $overflow; + } + + return $this->stream->write($string); + } + + public function eof() + { + return $this->stream->eof() && $this->remoteStream->eof(); + } + + /** + * Close both the remote stream and buffer stream + */ + public function close() + { + $this->remoteStream->close() && $this->stream->close(); + } + + private function cacheEntireStream() + { + $target = new FnStream(['write' => 'strlen']); + copy_to_stream($this, $target); + + return $this->tell(); + } +} diff --git a/instafeed/vendor/guzzlehttp/psr7/src/DroppingStream.php b/instafeed/vendor/guzzlehttp/psr7/src/DroppingStream.php new file mode 100755 index 0000000..8935c80 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/psr7/src/DroppingStream.php @@ -0,0 +1,42 @@ +stream = $stream; + $this->maxLength = $maxLength; + } + + public function write($string) + { + $diff = $this->maxLength - $this->stream->getSize(); + + // Begin returning 0 when the underlying stream is too large. + if ($diff <= 0) { + return 0; + } + + // Write the stream or a subset of the stream if needed. + if (strlen($string) < $diff) { + return $this->stream->write($string); + } + + return $this->stream->write(substr($string, 0, $diff)); + } +} diff --git a/instafeed/vendor/guzzlehttp/psr7/src/FnStream.php b/instafeed/vendor/guzzlehttp/psr7/src/FnStream.php new file mode 100755 index 0000000..73daea6 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/psr7/src/FnStream.php @@ -0,0 +1,158 @@ +methods = $methods; + + // Create the functions on the class + foreach ($methods as $name => $fn) { + $this->{'_fn_' . $name} = $fn; + } + } + + /** + * Lazily determine which methods are not implemented. + * @throws \BadMethodCallException + */ + public function __get($name) + { + throw new \BadMethodCallException(str_replace('_fn_', '', $name) + . '() is not implemented in the FnStream'); + } + + /** + * The close method is called on the underlying stream only if possible. + */ + public function __destruct() + { + if (isset($this->_fn_close)) { + call_user_func($this->_fn_close); + } + } + + /** + * An unserialize would allow the __destruct to run when the unserialized value goes out of scope. + * @throws \LogicException + */ + public function __wakeup() + { + throw new \LogicException('FnStream should never be unserialized'); + } + + /** + * Adds custom functionality to an underlying stream by intercepting + * specific method calls. + * + * @param StreamInterface $stream Stream to decorate + * @param array $methods Hash of method name to a closure + * + * @return FnStream + */ + public static function decorate(StreamInterface $stream, array $methods) + { + // If any of the required methods were not provided, then simply + // proxy to the decorated stream. + foreach (array_diff(self::$slots, array_keys($methods)) as $diff) { + $methods[$diff] = [$stream, $diff]; + } + + return new self($methods); + } + + public function __toString() + { + return call_user_func($this->_fn___toString); + } + + public function close() + { + return call_user_func($this->_fn_close); + } + + public function detach() + { + return call_user_func($this->_fn_detach); + } + + public function getSize() + { + return call_user_func($this->_fn_getSize); + } + + public function tell() + { + return call_user_func($this->_fn_tell); + } + + public function eof() + { + return call_user_func($this->_fn_eof); + } + + public function isSeekable() + { + return call_user_func($this->_fn_isSeekable); + } + + public function rewind() + { + call_user_func($this->_fn_rewind); + } + + public function seek($offset, $whence = SEEK_SET) + { + call_user_func($this->_fn_seek, $offset, $whence); + } + + public function isWritable() + { + return call_user_func($this->_fn_isWritable); + } + + public function write($string) + { + return call_user_func($this->_fn_write, $string); + } + + public function isReadable() + { + return call_user_func($this->_fn_isReadable); + } + + public function read($length) + { + return call_user_func($this->_fn_read, $length); + } + + public function getContents() + { + return call_user_func($this->_fn_getContents); + } + + public function getMetadata($key = null) + { + return call_user_func($this->_fn_getMetadata, $key); + } +} diff --git a/instafeed/vendor/guzzlehttp/psr7/src/InflateStream.php b/instafeed/vendor/guzzlehttp/psr7/src/InflateStream.php new file mode 100755 index 0000000..5e4f602 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/psr7/src/InflateStream.php @@ -0,0 +1,52 @@ +read(10); + $filenameHeaderLength = $this->getLengthOfPossibleFilenameHeader($stream, $header); + // Skip the header, that is 10 + length of filename + 1 (nil) bytes + $stream = new LimitStream($stream, -1, 10 + $filenameHeaderLength); + $resource = StreamWrapper::getResource($stream); + stream_filter_append($resource, 'zlib.inflate', STREAM_FILTER_READ); + $this->stream = $stream->isSeekable() ? new Stream($resource) : new NoSeekStream(new Stream($resource)); + } + + /** + * @param StreamInterface $stream + * @param $header + * @return int + */ + private function getLengthOfPossibleFilenameHeader(StreamInterface $stream, $header) + { + $filename_header_length = 0; + + if (substr(bin2hex($header), 6, 2) === '08') { + // we have a filename, read until nil + $filename_header_length = 1; + while ($stream->read(1) !== chr(0)) { + $filename_header_length++; + } + } + + return $filename_header_length; + } +} diff --git a/instafeed/vendor/guzzlehttp/psr7/src/LazyOpenStream.php b/instafeed/vendor/guzzlehttp/psr7/src/LazyOpenStream.php new file mode 100755 index 0000000..02cec3a --- /dev/null +++ b/instafeed/vendor/guzzlehttp/psr7/src/LazyOpenStream.php @@ -0,0 +1,39 @@ +filename = $filename; + $this->mode = $mode; + } + + /** + * Creates the underlying stream lazily when required. + * + * @return StreamInterface + */ + protected function createStream() + { + return stream_for(try_fopen($this->filename, $this->mode)); + } +} diff --git a/instafeed/vendor/guzzlehttp/psr7/src/LimitStream.php b/instafeed/vendor/guzzlehttp/psr7/src/LimitStream.php new file mode 100755 index 0000000..e4f239e --- /dev/null +++ b/instafeed/vendor/guzzlehttp/psr7/src/LimitStream.php @@ -0,0 +1,155 @@ +stream = $stream; + $this->setLimit($limit); + $this->setOffset($offset); + } + + public function eof() + { + // Always return true if the underlying stream is EOF + if ($this->stream->eof()) { + return true; + } + + // No limit and the underlying stream is not at EOF + if ($this->limit == -1) { + return false; + } + + return $this->stream->tell() >= $this->offset + $this->limit; + } + + /** + * Returns the size of the limited subset of data + * {@inheritdoc} + */ + public function getSize() + { + if (null === ($length = $this->stream->getSize())) { + return null; + } elseif ($this->limit == -1) { + return $length - $this->offset; + } else { + return min($this->limit, $length - $this->offset); + } + } + + /** + * Allow for a bounded seek on the read limited stream + * {@inheritdoc} + */ + public function seek($offset, $whence = SEEK_SET) + { + if ($whence !== SEEK_SET || $offset < 0) { + throw new \RuntimeException(sprintf( + 'Cannot seek to offset %s with whence %s', + $offset, + $whence + )); + } + + $offset += $this->offset; + + if ($this->limit !== -1) { + if ($offset > $this->offset + $this->limit) { + $offset = $this->offset + $this->limit; + } + } + + $this->stream->seek($offset); + } + + /** + * Give a relative tell() + * {@inheritdoc} + */ + public function tell() + { + return $this->stream->tell() - $this->offset; + } + + /** + * Set the offset to start limiting from + * + * @param int $offset Offset to seek to and begin byte limiting from + * + * @throws \RuntimeException if the stream cannot be seeked. + */ + public function setOffset($offset) + { + $current = $this->stream->tell(); + + if ($current !== $offset) { + // If the stream cannot seek to the offset position, then read to it + if ($this->stream->isSeekable()) { + $this->stream->seek($offset); + } elseif ($current > $offset) { + throw new \RuntimeException("Could not seek to stream offset $offset"); + } else { + $this->stream->read($offset - $current); + } + } + + $this->offset = $offset; + } + + /** + * Set the limit of bytes that the decorator allows to be read from the + * stream. + * + * @param int $limit Number of bytes to allow to be read from the stream. + * Use -1 for no limit. + */ + public function setLimit($limit) + { + $this->limit = $limit; + } + + public function read($length) + { + if ($this->limit == -1) { + return $this->stream->read($length); + } + + // Check if the current position is less than the total allowed + // bytes + original offset + $remaining = ($this->offset + $this->limit) - $this->stream->tell(); + if ($remaining > 0) { + // Only return the amount of requested data, ensuring that the byte + // limit is not exceeded + return $this->stream->read(min($remaining, $length)); + } + + return ''; + } +} diff --git a/instafeed/vendor/guzzlehttp/psr7/src/MessageTrait.php b/instafeed/vendor/guzzlehttp/psr7/src/MessageTrait.php new file mode 100755 index 0000000..a7966d1 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/psr7/src/MessageTrait.php @@ -0,0 +1,213 @@ + array of values */ + private $headers = []; + + /** @var array Map of lowercase header name => original name at registration */ + private $headerNames = []; + + /** @var string */ + private $protocol = '1.1'; + + /** @var StreamInterface */ + private $stream; + + public function getProtocolVersion() + { + return $this->protocol; + } + + public function withProtocolVersion($version) + { + if ($this->protocol === $version) { + return $this; + } + + $new = clone $this; + $new->protocol = $version; + return $new; + } + + public function getHeaders() + { + return $this->headers; + } + + public function hasHeader($header) + { + return isset($this->headerNames[strtolower($header)]); + } + + public function getHeader($header) + { + $header = strtolower($header); + + if (!isset($this->headerNames[$header])) { + return []; + } + + $header = $this->headerNames[$header]; + + return $this->headers[$header]; + } + + public function getHeaderLine($header) + { + return implode(', ', $this->getHeader($header)); + } + + public function withHeader($header, $value) + { + $this->assertHeader($header); + $value = $this->normalizeHeaderValue($value); + $normalized = strtolower($header); + + $new = clone $this; + if (isset($new->headerNames[$normalized])) { + unset($new->headers[$new->headerNames[$normalized]]); + } + $new->headerNames[$normalized] = $header; + $new->headers[$header] = $value; + + return $new; + } + + public function withAddedHeader($header, $value) + { + $this->assertHeader($header); + $value = $this->normalizeHeaderValue($value); + $normalized = strtolower($header); + + $new = clone $this; + if (isset($new->headerNames[$normalized])) { + $header = $this->headerNames[$normalized]; + $new->headers[$header] = array_merge($this->headers[$header], $value); + } else { + $new->headerNames[$normalized] = $header; + $new->headers[$header] = $value; + } + + return $new; + } + + public function withoutHeader($header) + { + $normalized = strtolower($header); + + if (!isset($this->headerNames[$normalized])) { + return $this; + } + + $header = $this->headerNames[$normalized]; + + $new = clone $this; + unset($new->headers[$header], $new->headerNames[$normalized]); + + return $new; + } + + public function getBody() + { + if (!$this->stream) { + $this->stream = stream_for(''); + } + + return $this->stream; + } + + public function withBody(StreamInterface $body) + { + if ($body === $this->stream) { + return $this; + } + + $new = clone $this; + $new->stream = $body; + return $new; + } + + private function setHeaders(array $headers) + { + $this->headerNames = $this->headers = []; + foreach ($headers as $header => $value) { + if (is_int($header)) { + // Numeric array keys are converted to int by PHP but having a header name '123' is not forbidden by the spec + // and also allowed in withHeader(). So we need to cast it to string again for the following assertion to pass. + $header = (string) $header; + } + $this->assertHeader($header); + $value = $this->normalizeHeaderValue($value); + $normalized = strtolower($header); + if (isset($this->headerNames[$normalized])) { + $header = $this->headerNames[$normalized]; + $this->headers[$header] = array_merge($this->headers[$header], $value); + } else { + $this->headerNames[$normalized] = $header; + $this->headers[$header] = $value; + } + } + } + + private function normalizeHeaderValue($value) + { + if (!is_array($value)) { + return $this->trimHeaderValues([$value]); + } + + if (count($value) === 0) { + throw new \InvalidArgumentException('Header value can not be an empty array.'); + } + + return $this->trimHeaderValues($value); + } + + /** + * Trims whitespace from the header values. + * + * Spaces and tabs ought to be excluded by parsers when extracting the field value from a header field. + * + * header-field = field-name ":" OWS field-value OWS + * OWS = *( SP / HTAB ) + * + * @param string[] $values Header values + * + * @return string[] Trimmed header values + * + * @see https://tools.ietf.org/html/rfc7230#section-3.2.4 + */ + private function trimHeaderValues(array $values) + { + return array_map(function ($value) { + if (!is_scalar($value) && null !== $value) { + throw new \InvalidArgumentException(sprintf( + 'Header value must be scalar or null but %s provided.', + is_object($value) ? get_class($value) : gettype($value) + )); + } + + return trim((string) $value, " \t"); + }, $values); + } + + private function assertHeader($header) + { + if (!is_string($header)) { + throw new \InvalidArgumentException(sprintf( + 'Header name must be a string but %s provided.', + is_object($header) ? get_class($header) : gettype($header) + )); + } + + if ($header === '') { + throw new \InvalidArgumentException('Header name can not be empty.'); + } + } +} diff --git a/instafeed/vendor/guzzlehttp/psr7/src/MultipartStream.php b/instafeed/vendor/guzzlehttp/psr7/src/MultipartStream.php new file mode 100755 index 0000000..c0fd584 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/psr7/src/MultipartStream.php @@ -0,0 +1,153 @@ +boundary = $boundary ?: sha1(uniqid('', true)); + $this->stream = $this->createStream($elements); + } + + /** + * Get the boundary + * + * @return string + */ + public function getBoundary() + { + return $this->boundary; + } + + public function isWritable() + { + return false; + } + + /** + * Get the headers needed before transferring the content of a POST file + */ + private function getHeaders(array $headers) + { + $str = ''; + foreach ($headers as $key => $value) { + $str .= "{$key}: {$value}\r\n"; + } + + return "--{$this->boundary}\r\n" . trim($str) . "\r\n\r\n"; + } + + /** + * Create the aggregate stream that will be used to upload the POST data + */ + protected function createStream(array $elements) + { + $stream = new AppendStream(); + + foreach ($elements as $element) { + $this->addElement($stream, $element); + } + + // Add the trailing boundary with CRLF + $stream->addStream(stream_for("--{$this->boundary}--\r\n")); + + return $stream; + } + + private function addElement(AppendStream $stream, array $element) + { + foreach (['contents', 'name'] as $key) { + if (!array_key_exists($key, $element)) { + throw new \InvalidArgumentException("A '{$key}' key is required"); + } + } + + $element['contents'] = stream_for($element['contents']); + + if (empty($element['filename'])) { + $uri = $element['contents']->getMetadata('uri'); + if (substr($uri, 0, 6) !== 'php://') { + $element['filename'] = $uri; + } + } + + list($body, $headers) = $this->createElement( + $element['name'], + $element['contents'], + isset($element['filename']) ? $element['filename'] : null, + isset($element['headers']) ? $element['headers'] : [] + ); + + $stream->addStream(stream_for($this->getHeaders($headers))); + $stream->addStream($body); + $stream->addStream(stream_for("\r\n")); + } + + /** + * @return array + */ + private function createElement($name, StreamInterface $stream, $filename, array $headers) + { + // Set a default content-disposition header if one was no provided + $disposition = $this->getHeader($headers, 'content-disposition'); + if (!$disposition) { + $headers['Content-Disposition'] = ($filename === '0' || $filename) + ? sprintf('form-data; name="%s"; filename="%s"', + $name, + basename($filename)) + : "form-data; name=\"{$name}\""; + } + + // Set a default content-length header if one was no provided + $length = $this->getHeader($headers, 'content-length'); + if (!$length) { + if ($length = $stream->getSize()) { + $headers['Content-Length'] = (string) $length; + } + } + + // Set a default Content-Type if one was not supplied + $type = $this->getHeader($headers, 'content-type'); + if (!$type && ($filename === '0' || $filename)) { + if ($type = mimetype_from_filename($filename)) { + $headers['Content-Type'] = $type; + } + } + + return [$stream, $headers]; + } + + private function getHeader(array $headers, $key) + { + $lowercaseHeader = strtolower($key); + foreach ($headers as $k => $v) { + if (strtolower($k) === $lowercaseHeader) { + return $v; + } + } + + return null; + } +} diff --git a/instafeed/vendor/guzzlehttp/psr7/src/NoSeekStream.php b/instafeed/vendor/guzzlehttp/psr7/src/NoSeekStream.php new file mode 100755 index 0000000..2332218 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/psr7/src/NoSeekStream.php @@ -0,0 +1,22 @@ +source = $source; + $this->size = isset($options['size']) ? $options['size'] : null; + $this->metadata = isset($options['metadata']) ? $options['metadata'] : []; + $this->buffer = new BufferStream(); + } + + public function __toString() + { + try { + return copy_to_string($this); + } catch (\Exception $e) { + return ''; + } + } + + public function close() + { + $this->detach(); + } + + public function detach() + { + $this->tellPos = false; + $this->source = null; + } + + public function getSize() + { + return $this->size; + } + + public function tell() + { + return $this->tellPos; + } + + public function eof() + { + return !$this->source; + } + + public function isSeekable() + { + return false; + } + + public function rewind() + { + $this->seek(0); + } + + public function seek($offset, $whence = SEEK_SET) + { + throw new \RuntimeException('Cannot seek a PumpStream'); + } + + public function isWritable() + { + return false; + } + + public function write($string) + { + throw new \RuntimeException('Cannot write to a PumpStream'); + } + + public function isReadable() + { + return true; + } + + public function read($length) + { + $data = $this->buffer->read($length); + $readLen = strlen($data); + $this->tellPos += $readLen; + $remaining = $length - $readLen; + + if ($remaining) { + $this->pump($remaining); + $data .= $this->buffer->read($remaining); + $this->tellPos += strlen($data) - $readLen; + } + + return $data; + } + + public function getContents() + { + $result = ''; + while (!$this->eof()) { + $result .= $this->read(1000000); + } + + return $result; + } + + public function getMetadata($key = null) + { + if (!$key) { + return $this->metadata; + } + + return isset($this->metadata[$key]) ? $this->metadata[$key] : null; + } + + private function pump($length) + { + if ($this->source) { + do { + $data = call_user_func($this->source, $length); + if ($data === false || $data === null) { + $this->source = null; + return; + } + $this->buffer->write($data); + $length -= strlen($data); + } while ($length > 0); + } + } +} diff --git a/instafeed/vendor/guzzlehttp/psr7/src/Request.php b/instafeed/vendor/guzzlehttp/psr7/src/Request.php new file mode 100755 index 0000000..59f337d --- /dev/null +++ b/instafeed/vendor/guzzlehttp/psr7/src/Request.php @@ -0,0 +1,151 @@ +assertMethod($method); + if (!($uri instanceof UriInterface)) { + $uri = new Uri($uri); + } + + $this->method = strtoupper($method); + $this->uri = $uri; + $this->setHeaders($headers); + $this->protocol = $version; + + if (!isset($this->headerNames['host'])) { + $this->updateHostFromUri(); + } + + if ($body !== '' && $body !== null) { + $this->stream = stream_for($body); + } + } + + public function getRequestTarget() + { + if ($this->requestTarget !== null) { + return $this->requestTarget; + } + + $target = $this->uri->getPath(); + if ($target == '') { + $target = '/'; + } + if ($this->uri->getQuery() != '') { + $target .= '?' . $this->uri->getQuery(); + } + + return $target; + } + + public function withRequestTarget($requestTarget) + { + if (preg_match('#\s#', $requestTarget)) { + throw new InvalidArgumentException( + 'Invalid request target provided; cannot contain whitespace' + ); + } + + $new = clone $this; + $new->requestTarget = $requestTarget; + return $new; + } + + public function getMethod() + { + return $this->method; + } + + public function withMethod($method) + { + $this->assertMethod($method); + $new = clone $this; + $new->method = strtoupper($method); + return $new; + } + + public function getUri() + { + return $this->uri; + } + + public function withUri(UriInterface $uri, $preserveHost = false) + { + if ($uri === $this->uri) { + return $this; + } + + $new = clone $this; + $new->uri = $uri; + + if (!$preserveHost || !isset($this->headerNames['host'])) { + $new->updateHostFromUri(); + } + + return $new; + } + + private function updateHostFromUri() + { + $host = $this->uri->getHost(); + + if ($host == '') { + return; + } + + if (($port = $this->uri->getPort()) !== null) { + $host .= ':' . $port; + } + + if (isset($this->headerNames['host'])) { + $header = $this->headerNames['host']; + } else { + $header = 'Host'; + $this->headerNames['host'] = 'Host'; + } + // Ensure Host is the first header. + // See: http://tools.ietf.org/html/rfc7230#section-5.4 + $this->headers = [$header => [$host]] + $this->headers; + } + + private function assertMethod($method) + { + if (!is_string($method) || $method === '') { + throw new \InvalidArgumentException('Method must be a non-empty string.'); + } + } +} diff --git a/instafeed/vendor/guzzlehttp/psr7/src/Response.php b/instafeed/vendor/guzzlehttp/psr7/src/Response.php new file mode 100755 index 0000000..e7e04d8 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/psr7/src/Response.php @@ -0,0 +1,154 @@ + 'Continue', + 101 => 'Switching Protocols', + 102 => 'Processing', + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 207 => 'Multi-status', + 208 => 'Already Reported', + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 306 => 'Switch Proxy', + 307 => 'Temporary Redirect', + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Time-out', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Large', + 415 => 'Unsupported Media Type', + 416 => 'Requested range not satisfiable', + 417 => 'Expectation Failed', + 418 => 'I\'m a teapot', + 422 => 'Unprocessable Entity', + 423 => 'Locked', + 424 => 'Failed Dependency', + 425 => 'Unordered Collection', + 426 => 'Upgrade Required', + 428 => 'Precondition Required', + 429 => 'Too Many Requests', + 431 => 'Request Header Fields Too Large', + 451 => 'Unavailable For Legal Reasons', + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Time-out', + 505 => 'HTTP Version not supported', + 506 => 'Variant Also Negotiates', + 507 => 'Insufficient Storage', + 508 => 'Loop Detected', + 511 => 'Network Authentication Required', + ]; + + /** @var string */ + private $reasonPhrase = ''; + + /** @var int */ + private $statusCode = 200; + + /** + * @param int $status Status code + * @param array $headers Response headers + * @param string|null|resource|StreamInterface $body Response body + * @param string $version Protocol version + * @param string|null $reason Reason phrase (when empty a default will be used based on the status code) + */ + public function __construct( + $status = 200, + array $headers = [], + $body = null, + $version = '1.1', + $reason = null + ) { + $this->assertStatusCodeIsInteger($status); + $status = (int) $status; + $this->assertStatusCodeRange($status); + + $this->statusCode = $status; + + if ($body !== '' && $body !== null) { + $this->stream = stream_for($body); + } + + $this->setHeaders($headers); + if ($reason == '' && isset(self::$phrases[$this->statusCode])) { + $this->reasonPhrase = self::$phrases[$this->statusCode]; + } else { + $this->reasonPhrase = (string) $reason; + } + + $this->protocol = $version; + } + + public function getStatusCode() + { + return $this->statusCode; + } + + public function getReasonPhrase() + { + return $this->reasonPhrase; + } + + public function withStatus($code, $reasonPhrase = '') + { + $this->assertStatusCodeIsInteger($code); + $code = (int) $code; + $this->assertStatusCodeRange($code); + + $new = clone $this; + $new->statusCode = $code; + if ($reasonPhrase == '' && isset(self::$phrases[$new->statusCode])) { + $reasonPhrase = self::$phrases[$new->statusCode]; + } + $new->reasonPhrase = $reasonPhrase; + return $new; + } + + private function assertStatusCodeIsInteger($statusCode) + { + if (filter_var($statusCode, FILTER_VALIDATE_INT) === false) { + throw new \InvalidArgumentException('Status code must be an integer value.'); + } + } + + private function assertStatusCodeRange($statusCode) + { + if ($statusCode < 100 || $statusCode >= 600) { + throw new \InvalidArgumentException('Status code must be an integer value between 1xx and 5xx.'); + } + } +} diff --git a/instafeed/vendor/guzzlehttp/psr7/src/Rfc7230.php b/instafeed/vendor/guzzlehttp/psr7/src/Rfc7230.php new file mode 100755 index 0000000..505e474 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/psr7/src/Rfc7230.php @@ -0,0 +1,18 @@ +@,;:\\\"/[\]?={}\x01-\x20\x7F]++):[ \t]*+((?:[ \t]*+[\x21-\x7E\x80-\xFF]++)*+)[ \t]*+\r?\n)m"; + const HEADER_FOLD_REGEX = "(\r?\n[ \t]++)"; +} diff --git a/instafeed/vendor/guzzlehttp/psr7/src/ServerRequest.php b/instafeed/vendor/guzzlehttp/psr7/src/ServerRequest.php new file mode 100755 index 0000000..1a09a6c --- /dev/null +++ b/instafeed/vendor/guzzlehttp/psr7/src/ServerRequest.php @@ -0,0 +1,376 @@ +serverParams = $serverParams; + + parent::__construct($method, $uri, $headers, $body, $version); + } + + /** + * Return an UploadedFile instance array. + * + * @param array $files A array which respect $_FILES structure + * @throws InvalidArgumentException for unrecognized values + * @return array + */ + public static function normalizeFiles(array $files) + { + $normalized = []; + + foreach ($files as $key => $value) { + if ($value instanceof UploadedFileInterface) { + $normalized[$key] = $value; + } elseif (is_array($value) && isset($value['tmp_name'])) { + $normalized[$key] = self::createUploadedFileFromSpec($value); + } elseif (is_array($value)) { + $normalized[$key] = self::normalizeFiles($value); + continue; + } else { + throw new InvalidArgumentException('Invalid value in files specification'); + } + } + + return $normalized; + } + + /** + * Create and return an UploadedFile instance from a $_FILES specification. + * + * If the specification represents an array of values, this method will + * delegate to normalizeNestedFileSpec() and return that return value. + * + * @param array $value $_FILES struct + * @return array|UploadedFileInterface + */ + private static function createUploadedFileFromSpec(array $value) + { + if (is_array($value['tmp_name'])) { + return self::normalizeNestedFileSpec($value); + } + + return new UploadedFile( + $value['tmp_name'], + (int) $value['size'], + (int) $value['error'], + $value['name'], + $value['type'] + ); + } + + /** + * Normalize an array of file specifications. + * + * Loops through all nested files and returns a normalized array of + * UploadedFileInterface instances. + * + * @param array $files + * @return UploadedFileInterface[] + */ + private static function normalizeNestedFileSpec(array $files = []) + { + $normalizedFiles = []; + + foreach (array_keys($files['tmp_name']) as $key) { + $spec = [ + 'tmp_name' => $files['tmp_name'][$key], + 'size' => $files['size'][$key], + 'error' => $files['error'][$key], + 'name' => $files['name'][$key], + 'type' => $files['type'][$key], + ]; + $normalizedFiles[$key] = self::createUploadedFileFromSpec($spec); + } + + return $normalizedFiles; + } + + /** + * Return a ServerRequest populated with superglobals: + * $_GET + * $_POST + * $_COOKIE + * $_FILES + * $_SERVER + * + * @return ServerRequestInterface + */ + public static function fromGlobals() + { + $method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'GET'; + $headers = getallheaders(); + $uri = self::getUriFromGlobals(); + $body = new CachingStream(new LazyOpenStream('php://input', 'r+')); + $protocol = isset($_SERVER['SERVER_PROTOCOL']) ? str_replace('HTTP/', '', $_SERVER['SERVER_PROTOCOL']) : '1.1'; + + $serverRequest = new ServerRequest($method, $uri, $headers, $body, $protocol, $_SERVER); + + return $serverRequest + ->withCookieParams($_COOKIE) + ->withQueryParams($_GET) + ->withParsedBody($_POST) + ->withUploadedFiles(self::normalizeFiles($_FILES)); + } + + private static function extractHostAndPortFromAuthority($authority) + { + $uri = 'http://'.$authority; + $parts = parse_url($uri); + if (false === $parts) { + return [null, null]; + } + + $host = isset($parts['host']) ? $parts['host'] : null; + $port = isset($parts['port']) ? $parts['port'] : null; + + return [$host, $port]; + } + + /** + * Get a Uri populated with values from $_SERVER. + * + * @return UriInterface + */ + public static function getUriFromGlobals() + { + $uri = new Uri(''); + + $uri = $uri->withScheme(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ? 'https' : 'http'); + + $hasPort = false; + if (isset($_SERVER['HTTP_HOST'])) { + list($host, $port) = self::extractHostAndPortFromAuthority($_SERVER['HTTP_HOST']); + if ($host !== null) { + $uri = $uri->withHost($host); + } + + if ($port !== null) { + $hasPort = true; + $uri = $uri->withPort($port); + } + } elseif (isset($_SERVER['SERVER_NAME'])) { + $uri = $uri->withHost($_SERVER['SERVER_NAME']); + } elseif (isset($_SERVER['SERVER_ADDR'])) { + $uri = $uri->withHost($_SERVER['SERVER_ADDR']); + } + + if (!$hasPort && isset($_SERVER['SERVER_PORT'])) { + $uri = $uri->withPort($_SERVER['SERVER_PORT']); + } + + $hasQuery = false; + if (isset($_SERVER['REQUEST_URI'])) { + $requestUriParts = explode('?', $_SERVER['REQUEST_URI'], 2); + $uri = $uri->withPath($requestUriParts[0]); + if (isset($requestUriParts[1])) { + $hasQuery = true; + $uri = $uri->withQuery($requestUriParts[1]); + } + } + + if (!$hasQuery && isset($_SERVER['QUERY_STRING'])) { + $uri = $uri->withQuery($_SERVER['QUERY_STRING']); + } + + return $uri; + } + + + /** + * {@inheritdoc} + */ + public function getServerParams() + { + return $this->serverParams; + } + + /** + * {@inheritdoc} + */ + public function getUploadedFiles() + { + return $this->uploadedFiles; + } + + /** + * {@inheritdoc} + */ + public function withUploadedFiles(array $uploadedFiles) + { + $new = clone $this; + $new->uploadedFiles = $uploadedFiles; + + return $new; + } + + /** + * {@inheritdoc} + */ + public function getCookieParams() + { + return $this->cookieParams; + } + + /** + * {@inheritdoc} + */ + public function withCookieParams(array $cookies) + { + $new = clone $this; + $new->cookieParams = $cookies; + + return $new; + } + + /** + * {@inheritdoc} + */ + public function getQueryParams() + { + return $this->queryParams; + } + + /** + * {@inheritdoc} + */ + public function withQueryParams(array $query) + { + $new = clone $this; + $new->queryParams = $query; + + return $new; + } + + /** + * {@inheritdoc} + */ + public function getParsedBody() + { + return $this->parsedBody; + } + + /** + * {@inheritdoc} + */ + public function withParsedBody($data) + { + $new = clone $this; + $new->parsedBody = $data; + + return $new; + } + + /** + * {@inheritdoc} + */ + public function getAttributes() + { + return $this->attributes; + } + + /** + * {@inheritdoc} + */ + public function getAttribute($attribute, $default = null) + { + if (false === array_key_exists($attribute, $this->attributes)) { + return $default; + } + + return $this->attributes[$attribute]; + } + + /** + * {@inheritdoc} + */ + public function withAttribute($attribute, $value) + { + $new = clone $this; + $new->attributes[$attribute] = $value; + + return $new; + } + + /** + * {@inheritdoc} + */ + public function withoutAttribute($attribute) + { + if (false === array_key_exists($attribute, $this->attributes)) { + return $this; + } + + $new = clone $this; + unset($new->attributes[$attribute]); + + return $new; + } +} diff --git a/instafeed/vendor/guzzlehttp/psr7/src/Stream.php b/instafeed/vendor/guzzlehttp/psr7/src/Stream.php new file mode 100755 index 0000000..d9e7409 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/psr7/src/Stream.php @@ -0,0 +1,267 @@ +size = $options['size']; + } + + $this->customMetadata = isset($options['metadata']) + ? $options['metadata'] + : []; + + $this->stream = $stream; + $meta = stream_get_meta_data($this->stream); + $this->seekable = $meta['seekable']; + $this->readable = (bool)preg_match(self::READABLE_MODES, $meta['mode']); + $this->writable = (bool)preg_match(self::WRITABLE_MODES, $meta['mode']); + $this->uri = $this->getMetadata('uri'); + } + + /** + * Closes the stream when the destructed + */ + public function __destruct() + { + $this->close(); + } + + public function __toString() + { + try { + $this->seek(0); + return (string) stream_get_contents($this->stream); + } catch (\Exception $e) { + return ''; + } + } + + public function getContents() + { + if (!isset($this->stream)) { + throw new \RuntimeException('Stream is detached'); + } + + $contents = stream_get_contents($this->stream); + + if ($contents === false) { + throw new \RuntimeException('Unable to read stream contents'); + } + + return $contents; + } + + public function close() + { + if (isset($this->stream)) { + if (is_resource($this->stream)) { + fclose($this->stream); + } + $this->detach(); + } + } + + public function detach() + { + if (!isset($this->stream)) { + return null; + } + + $result = $this->stream; + unset($this->stream); + $this->size = $this->uri = null; + $this->readable = $this->writable = $this->seekable = false; + + return $result; + } + + public function getSize() + { + if ($this->size !== null) { + return $this->size; + } + + if (!isset($this->stream)) { + return null; + } + + // Clear the stat cache if the stream has a URI + if ($this->uri) { + clearstatcache(true, $this->uri); + } + + $stats = fstat($this->stream); + if (isset($stats['size'])) { + $this->size = $stats['size']; + return $this->size; + } + + return null; + } + + public function isReadable() + { + return $this->readable; + } + + public function isWritable() + { + return $this->writable; + } + + public function isSeekable() + { + return $this->seekable; + } + + public function eof() + { + if (!isset($this->stream)) { + throw new \RuntimeException('Stream is detached'); + } + + return feof($this->stream); + } + + public function tell() + { + if (!isset($this->stream)) { + throw new \RuntimeException('Stream is detached'); + } + + $result = ftell($this->stream); + + if ($result === false) { + throw new \RuntimeException('Unable to determine stream position'); + } + + return $result; + } + + public function rewind() + { + $this->seek(0); + } + + public function seek($offset, $whence = SEEK_SET) + { + $whence = (int) $whence; + + if (!isset($this->stream)) { + throw new \RuntimeException('Stream is detached'); + } + if (!$this->seekable) { + throw new \RuntimeException('Stream is not seekable'); + } + if (fseek($this->stream, $offset, $whence) === -1) { + throw new \RuntimeException('Unable to seek to stream position ' + . $offset . ' with whence ' . var_export($whence, true)); + } + } + + public function read($length) + { + if (!isset($this->stream)) { + throw new \RuntimeException('Stream is detached'); + } + if (!$this->readable) { + throw new \RuntimeException('Cannot read from non-readable stream'); + } + if ($length < 0) { + throw new \RuntimeException('Length parameter cannot be negative'); + } + + if (0 === $length) { + return ''; + } + + $string = fread($this->stream, $length); + if (false === $string) { + throw new \RuntimeException('Unable to read from stream'); + } + + return $string; + } + + public function write($string) + { + if (!isset($this->stream)) { + throw new \RuntimeException('Stream is detached'); + } + if (!$this->writable) { + throw new \RuntimeException('Cannot write to a non-writable stream'); + } + + // We can't know the size after writing anything + $this->size = null; + $result = fwrite($this->stream, $string); + + if ($result === false) { + throw new \RuntimeException('Unable to write to stream'); + } + + return $result; + } + + public function getMetadata($key = null) + { + if (!isset($this->stream)) { + return $key ? null : []; + } elseif (!$key) { + return $this->customMetadata + stream_get_meta_data($this->stream); + } elseif (isset($this->customMetadata[$key])) { + return $this->customMetadata[$key]; + } + + $meta = stream_get_meta_data($this->stream); + + return isset($meta[$key]) ? $meta[$key] : null; + } +} diff --git a/instafeed/vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php b/instafeed/vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php new file mode 100755 index 0000000..daec6f5 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php @@ -0,0 +1,149 @@ +stream = $stream; + } + + /** + * Magic method used to create a new stream if streams are not added in + * the constructor of a decorator (e.g., LazyOpenStream). + * + * @param string $name Name of the property (allows "stream" only). + * + * @return StreamInterface + */ + public function __get($name) + { + if ($name == 'stream') { + $this->stream = $this->createStream(); + return $this->stream; + } + + throw new \UnexpectedValueException("$name not found on class"); + } + + public function __toString() + { + try { + if ($this->isSeekable()) { + $this->seek(0); + } + return $this->getContents(); + } catch (\Exception $e) { + // Really, PHP? https://bugs.php.net/bug.php?id=53648 + trigger_error('StreamDecorator::__toString exception: ' + . (string) $e, E_USER_ERROR); + return ''; + } + } + + public function getContents() + { + return copy_to_string($this); + } + + /** + * Allow decorators to implement custom methods + * + * @param string $method Missing method name + * @param array $args Method arguments + * + * @return mixed + */ + public function __call($method, array $args) + { + $result = call_user_func_array([$this->stream, $method], $args); + + // Always return the wrapped object if the result is a return $this + return $result === $this->stream ? $this : $result; + } + + public function close() + { + $this->stream->close(); + } + + public function getMetadata($key = null) + { + return $this->stream->getMetadata($key); + } + + public function detach() + { + return $this->stream->detach(); + } + + public function getSize() + { + return $this->stream->getSize(); + } + + public function eof() + { + return $this->stream->eof(); + } + + public function tell() + { + return $this->stream->tell(); + } + + public function isReadable() + { + return $this->stream->isReadable(); + } + + public function isWritable() + { + return $this->stream->isWritable(); + } + + public function isSeekable() + { + return $this->stream->isSeekable(); + } + + public function rewind() + { + $this->seek(0); + } + + public function seek($offset, $whence = SEEK_SET) + { + $this->stream->seek($offset, $whence); + } + + public function read($length) + { + return $this->stream->read($length); + } + + public function write($string) + { + return $this->stream->write($string); + } + + /** + * Implement in subclasses to dynamically create streams when requested. + * + * @return StreamInterface + * @throws \BadMethodCallException + */ + protected function createStream() + { + throw new \BadMethodCallException('Not implemented'); + } +} diff --git a/instafeed/vendor/guzzlehttp/psr7/src/StreamWrapper.php b/instafeed/vendor/guzzlehttp/psr7/src/StreamWrapper.php new file mode 100755 index 0000000..0f3a285 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/psr7/src/StreamWrapper.php @@ -0,0 +1,161 @@ +isReadable()) { + $mode = $stream->isWritable() ? 'r+' : 'r'; + } elseif ($stream->isWritable()) { + $mode = 'w'; + } else { + throw new \InvalidArgumentException('The stream must be readable, ' + . 'writable, or both.'); + } + + return fopen('guzzle://stream', $mode, null, self::createStreamContext($stream)); + } + + /** + * Creates a stream context that can be used to open a stream as a php stream resource. + * + * @param StreamInterface $stream + * + * @return resource + */ + public static function createStreamContext(StreamInterface $stream) + { + return stream_context_create([ + 'guzzle' => ['stream' => $stream] + ]); + } + + /** + * Registers the stream wrapper if needed + */ + public static function register() + { + if (!in_array('guzzle', stream_get_wrappers())) { + stream_wrapper_register('guzzle', __CLASS__); + } + } + + public function stream_open($path, $mode, $options, &$opened_path) + { + $options = stream_context_get_options($this->context); + + if (!isset($options['guzzle']['stream'])) { + return false; + } + + $this->mode = $mode; + $this->stream = $options['guzzle']['stream']; + + return true; + } + + public function stream_read($count) + { + return $this->stream->read($count); + } + + public function stream_write($data) + { + return (int) $this->stream->write($data); + } + + public function stream_tell() + { + return $this->stream->tell(); + } + + public function stream_eof() + { + return $this->stream->eof(); + } + + public function stream_seek($offset, $whence) + { + $this->stream->seek($offset, $whence); + + return true; + } + + public function stream_cast($cast_as) + { + $stream = clone($this->stream); + + return $stream->detach(); + } + + public function stream_stat() + { + static $modeMap = [ + 'r' => 33060, + 'rb' => 33060, + 'r+' => 33206, + 'w' => 33188, + 'wb' => 33188 + ]; + + return [ + 'dev' => 0, + 'ino' => 0, + 'mode' => $modeMap[$this->mode], + 'nlink' => 0, + 'uid' => 0, + 'gid' => 0, + 'rdev' => 0, + 'size' => $this->stream->getSize() ?: 0, + 'atime' => 0, + 'mtime' => 0, + 'ctime' => 0, + 'blksize' => 0, + 'blocks' => 0 + ]; + } + + public function url_stat($path, $flags) + { + return [ + 'dev' => 0, + 'ino' => 0, + 'mode' => 0, + 'nlink' => 0, + 'uid' => 0, + 'gid' => 0, + 'rdev' => 0, + 'size' => 0, + 'atime' => 0, + 'mtime' => 0, + 'ctime' => 0, + 'blksize' => 0, + 'blocks' => 0 + ]; + } +} diff --git a/instafeed/vendor/guzzlehttp/psr7/src/UploadedFile.php b/instafeed/vendor/guzzlehttp/psr7/src/UploadedFile.php new file mode 100755 index 0000000..e62bd5c --- /dev/null +++ b/instafeed/vendor/guzzlehttp/psr7/src/UploadedFile.php @@ -0,0 +1,316 @@ +setError($errorStatus); + $this->setSize($size); + $this->setClientFilename($clientFilename); + $this->setClientMediaType($clientMediaType); + + if ($this->isOk()) { + $this->setStreamOrFile($streamOrFile); + } + } + + /** + * Depending on the value set file or stream variable + * + * @param mixed $streamOrFile + * @throws InvalidArgumentException + */ + private function setStreamOrFile($streamOrFile) + { + if (is_string($streamOrFile)) { + $this->file = $streamOrFile; + } elseif (is_resource($streamOrFile)) { + $this->stream = new Stream($streamOrFile); + } elseif ($streamOrFile instanceof StreamInterface) { + $this->stream = $streamOrFile; + } else { + throw new InvalidArgumentException( + 'Invalid stream or file provided for UploadedFile' + ); + } + } + + /** + * @param int $error + * @throws InvalidArgumentException + */ + private function setError($error) + { + if (false === is_int($error)) { + throw new InvalidArgumentException( + 'Upload file error status must be an integer' + ); + } + + if (false === in_array($error, UploadedFile::$errors)) { + throw new InvalidArgumentException( + 'Invalid error status for UploadedFile' + ); + } + + $this->error = $error; + } + + /** + * @param int $size + * @throws InvalidArgumentException + */ + private function setSize($size) + { + if (false === is_int($size)) { + throw new InvalidArgumentException( + 'Upload file size must be an integer' + ); + } + + $this->size = $size; + } + + /** + * @param mixed $param + * @return boolean + */ + private function isStringOrNull($param) + { + return in_array(gettype($param), ['string', 'NULL']); + } + + /** + * @param mixed $param + * @return boolean + */ + private function isStringNotEmpty($param) + { + return is_string($param) && false === empty($param); + } + + /** + * @param string|null $clientFilename + * @throws InvalidArgumentException + */ + private function setClientFilename($clientFilename) + { + if (false === $this->isStringOrNull($clientFilename)) { + throw new InvalidArgumentException( + 'Upload file client filename must be a string or null' + ); + } + + $this->clientFilename = $clientFilename; + } + + /** + * @param string|null $clientMediaType + * @throws InvalidArgumentException + */ + private function setClientMediaType($clientMediaType) + { + if (false === $this->isStringOrNull($clientMediaType)) { + throw new InvalidArgumentException( + 'Upload file client media type must be a string or null' + ); + } + + $this->clientMediaType = $clientMediaType; + } + + /** + * Return true if there is no upload error + * + * @return boolean + */ + private function isOk() + { + return $this->error === UPLOAD_ERR_OK; + } + + /** + * @return boolean + */ + public function isMoved() + { + return $this->moved; + } + + /** + * @throws RuntimeException if is moved or not ok + */ + private function validateActive() + { + if (false === $this->isOk()) { + throw new RuntimeException('Cannot retrieve stream due to upload error'); + } + + if ($this->isMoved()) { + throw new RuntimeException('Cannot retrieve stream after it has already been moved'); + } + } + + /** + * {@inheritdoc} + * @throws RuntimeException if the upload was not successful. + */ + public function getStream() + { + $this->validateActive(); + + if ($this->stream instanceof StreamInterface) { + return $this->stream; + } + + return new LazyOpenStream($this->file, 'r+'); + } + + /** + * {@inheritdoc} + * + * @see http://php.net/is_uploaded_file + * @see http://php.net/move_uploaded_file + * @param string $targetPath Path to which to move the uploaded file. + * @throws RuntimeException if the upload was not successful. + * @throws InvalidArgumentException if the $path specified is invalid. + * @throws RuntimeException on any error during the move operation, or on + * the second or subsequent call to the method. + */ + public function moveTo($targetPath) + { + $this->validateActive(); + + if (false === $this->isStringNotEmpty($targetPath)) { + throw new InvalidArgumentException( + 'Invalid path provided for move operation; must be a non-empty string' + ); + } + + if ($this->file) { + $this->moved = php_sapi_name() == 'cli' + ? rename($this->file, $targetPath) + : move_uploaded_file($this->file, $targetPath); + } else { + copy_to_stream( + $this->getStream(), + new LazyOpenStream($targetPath, 'w') + ); + + $this->moved = true; + } + + if (false === $this->moved) { + throw new RuntimeException( + sprintf('Uploaded file could not be moved to %s', $targetPath) + ); + } + } + + /** + * {@inheritdoc} + * + * @return int|null The file size in bytes or null if unknown. + */ + public function getSize() + { + return $this->size; + } + + /** + * {@inheritdoc} + * + * @see http://php.net/manual/en/features.file-upload.errors.php + * @return int One of PHP's UPLOAD_ERR_XXX constants. + */ + public function getError() + { + return $this->error; + } + + /** + * {@inheritdoc} + * + * @return string|null The filename sent by the client or null if none + * was provided. + */ + public function getClientFilename() + { + return $this->clientFilename; + } + + /** + * {@inheritdoc} + */ + public function getClientMediaType() + { + return $this->clientMediaType; + } +} diff --git a/instafeed/vendor/guzzlehttp/psr7/src/Uri.php b/instafeed/vendor/guzzlehttp/psr7/src/Uri.php new file mode 100755 index 0000000..825a25e --- /dev/null +++ b/instafeed/vendor/guzzlehttp/psr7/src/Uri.php @@ -0,0 +1,760 @@ + 80, + 'https' => 443, + 'ftp' => 21, + 'gopher' => 70, + 'nntp' => 119, + 'news' => 119, + 'telnet' => 23, + 'tn3270' => 23, + 'imap' => 143, + 'pop' => 110, + 'ldap' => 389, + ]; + + private static $charUnreserved = 'a-zA-Z0-9_\-\.~'; + private static $charSubDelims = '!\$&\'\(\)\*\+,;='; + private static $replaceQuery = ['=' => '%3D', '&' => '%26']; + + /** @var string Uri scheme. */ + private $scheme = ''; + + /** @var string Uri user info. */ + private $userInfo = ''; + + /** @var string Uri host. */ + private $host = ''; + + /** @var int|null Uri port. */ + private $port; + + /** @var string Uri path. */ + private $path = ''; + + /** @var string Uri query string. */ + private $query = ''; + + /** @var string Uri fragment. */ + private $fragment = ''; + + /** + * @param string $uri URI to parse + */ + public function __construct($uri = '') + { + // weak type check to also accept null until we can add scalar type hints + if ($uri != '') { + $parts = parse_url($uri); + if ($parts === false) { + throw new \InvalidArgumentException("Unable to parse URI: $uri"); + } + $this->applyParts($parts); + } + } + + public function __toString() + { + return self::composeComponents( + $this->scheme, + $this->getAuthority(), + $this->path, + $this->query, + $this->fragment + ); + } + + /** + * Composes a URI reference string from its various components. + * + * Usually this method does not need to be called manually but instead is used indirectly via + * `Psr\Http\Message\UriInterface::__toString`. + * + * PSR-7 UriInterface treats an empty component the same as a missing component as + * getQuery(), getFragment() etc. always return a string. This explains the slight + * difference to RFC 3986 Section 5.3. + * + * Another adjustment is that the authority separator is added even when the authority is missing/empty + * for the "file" scheme. This is because PHP stream functions like `file_get_contents` only work with + * `file:///myfile` but not with `file:/myfile` although they are equivalent according to RFC 3986. But + * `file:///` is the more common syntax for the file scheme anyway (Chrome for example redirects to + * that format). + * + * @param string $scheme + * @param string $authority + * @param string $path + * @param string $query + * @param string $fragment + * + * @return string + * + * @link https://tools.ietf.org/html/rfc3986#section-5.3 + */ + public static function composeComponents($scheme, $authority, $path, $query, $fragment) + { + $uri = ''; + + // weak type checks to also accept null until we can add scalar type hints + if ($scheme != '') { + $uri .= $scheme . ':'; + } + + if ($authority != ''|| $scheme === 'file') { + $uri .= '//' . $authority; + } + + $uri .= $path; + + if ($query != '') { + $uri .= '?' . $query; + } + + if ($fragment != '') { + $uri .= '#' . $fragment; + } + + return $uri; + } + + /** + * Whether the URI has the default port of the current scheme. + * + * `Psr\Http\Message\UriInterface::getPort` may return null or the standard port. This method can be used + * independently of the implementation. + * + * @param UriInterface $uri + * + * @return bool + */ + public static function isDefaultPort(UriInterface $uri) + { + return $uri->getPort() === null + || (isset(self::$defaultPorts[$uri->getScheme()]) && $uri->getPort() === self::$defaultPorts[$uri->getScheme()]); + } + + /** + * Whether the URI is absolute, i.e. it has a scheme. + * + * An instance of UriInterface can either be an absolute URI or a relative reference. This method returns true + * if it is the former. An absolute URI has a scheme. A relative reference is used to express a URI relative + * to another URI, the base URI. Relative references can be divided into several forms: + * - network-path references, e.g. '//example.com/path' + * - absolute-path references, e.g. '/path' + * - relative-path references, e.g. 'subpath' + * + * @param UriInterface $uri + * + * @return bool + * @see Uri::isNetworkPathReference + * @see Uri::isAbsolutePathReference + * @see Uri::isRelativePathReference + * @link https://tools.ietf.org/html/rfc3986#section-4 + */ + public static function isAbsolute(UriInterface $uri) + { + return $uri->getScheme() !== ''; + } + + /** + * Whether the URI is a network-path reference. + * + * A relative reference that begins with two slash characters is termed an network-path reference. + * + * @param UriInterface $uri + * + * @return bool + * @link https://tools.ietf.org/html/rfc3986#section-4.2 + */ + public static function isNetworkPathReference(UriInterface $uri) + { + return $uri->getScheme() === '' && $uri->getAuthority() !== ''; + } + + /** + * Whether the URI is a absolute-path reference. + * + * A relative reference that begins with a single slash character is termed an absolute-path reference. + * + * @param UriInterface $uri + * + * @return bool + * @link https://tools.ietf.org/html/rfc3986#section-4.2 + */ + public static function isAbsolutePathReference(UriInterface $uri) + { + return $uri->getScheme() === '' + && $uri->getAuthority() === '' + && isset($uri->getPath()[0]) + && $uri->getPath()[0] === '/'; + } + + /** + * Whether the URI is a relative-path reference. + * + * A relative reference that does not begin with a slash character is termed a relative-path reference. + * + * @param UriInterface $uri + * + * @return bool + * @link https://tools.ietf.org/html/rfc3986#section-4.2 + */ + public static function isRelativePathReference(UriInterface $uri) + { + return $uri->getScheme() === '' + && $uri->getAuthority() === '' + && (!isset($uri->getPath()[0]) || $uri->getPath()[0] !== '/'); + } + + /** + * Whether the URI is a same-document reference. + * + * A same-document reference refers to a URI that is, aside from its fragment + * component, identical to the base URI. When no base URI is given, only an empty + * URI reference (apart from its fragment) is considered a same-document reference. + * + * @param UriInterface $uri The URI to check + * @param UriInterface|null $base An optional base URI to compare against + * + * @return bool + * @link https://tools.ietf.org/html/rfc3986#section-4.4 + */ + public static function isSameDocumentReference(UriInterface $uri, UriInterface $base = null) + { + if ($base !== null) { + $uri = UriResolver::resolve($base, $uri); + + return ($uri->getScheme() === $base->getScheme()) + && ($uri->getAuthority() === $base->getAuthority()) + && ($uri->getPath() === $base->getPath()) + && ($uri->getQuery() === $base->getQuery()); + } + + return $uri->getScheme() === '' && $uri->getAuthority() === '' && $uri->getPath() === '' && $uri->getQuery() === ''; + } + + /** + * Removes dot segments from a path and returns the new path. + * + * @param string $path + * + * @return string + * + * @deprecated since version 1.4. Use UriResolver::removeDotSegments instead. + * @see UriResolver::removeDotSegments + */ + public static function removeDotSegments($path) + { + return UriResolver::removeDotSegments($path); + } + + /** + * Converts the relative URI into a new URI that is resolved against the base URI. + * + * @param UriInterface $base Base URI + * @param string|UriInterface $rel Relative URI + * + * @return UriInterface + * + * @deprecated since version 1.4. Use UriResolver::resolve instead. + * @see UriResolver::resolve + */ + public static function resolve(UriInterface $base, $rel) + { + if (!($rel instanceof UriInterface)) { + $rel = new self($rel); + } + + return UriResolver::resolve($base, $rel); + } + + /** + * Creates a new URI with a specific query string value removed. + * + * Any existing query string values that exactly match the provided key are + * removed. + * + * @param UriInterface $uri URI to use as a base. + * @param string $key Query string key to remove. + * + * @return UriInterface + */ + public static function withoutQueryValue(UriInterface $uri, $key) + { + $result = self::getFilteredQueryString($uri, [$key]); + + return $uri->withQuery(implode('&', $result)); + } + + /** + * Creates a new URI with a specific query string value. + * + * Any existing query string values that exactly match the provided key are + * removed and replaced with the given key value pair. + * + * A value of null will set the query string key without a value, e.g. "key" + * instead of "key=value". + * + * @param UriInterface $uri URI to use as a base. + * @param string $key Key to set. + * @param string|null $value Value to set + * + * @return UriInterface + */ + public static function withQueryValue(UriInterface $uri, $key, $value) + { + $result = self::getFilteredQueryString($uri, [$key]); + + $result[] = self::generateQueryString($key, $value); + + return $uri->withQuery(implode('&', $result)); + } + + /** + * Creates a new URI with multiple specific query string values. + * + * It has the same behavior as withQueryValue() but for an associative array of key => value. + * + * @param UriInterface $uri URI to use as a base. + * @param array $keyValueArray Associative array of key and values + * + * @return UriInterface + */ + public static function withQueryValues(UriInterface $uri, array $keyValueArray) + { + $result = self::getFilteredQueryString($uri, array_keys($keyValueArray)); + + foreach ($keyValueArray as $key => $value) { + $result[] = self::generateQueryString($key, $value); + } + + return $uri->withQuery(implode('&', $result)); + } + + /** + * Creates a URI from a hash of `parse_url` components. + * + * @param array $parts + * + * @return UriInterface + * @link http://php.net/manual/en/function.parse-url.php + * + * @throws \InvalidArgumentException If the components do not form a valid URI. + */ + public static function fromParts(array $parts) + { + $uri = new self(); + $uri->applyParts($parts); + $uri->validateState(); + + return $uri; + } + + public function getScheme() + { + return $this->scheme; + } + + public function getAuthority() + { + $authority = $this->host; + if ($this->userInfo !== '') { + $authority = $this->userInfo . '@' . $authority; + } + + if ($this->port !== null) { + $authority .= ':' . $this->port; + } + + return $authority; + } + + public function getUserInfo() + { + return $this->userInfo; + } + + public function getHost() + { + return $this->host; + } + + public function getPort() + { + return $this->port; + } + + public function getPath() + { + return $this->path; + } + + public function getQuery() + { + return $this->query; + } + + public function getFragment() + { + return $this->fragment; + } + + public function withScheme($scheme) + { + $scheme = $this->filterScheme($scheme); + + if ($this->scheme === $scheme) { + return $this; + } + + $new = clone $this; + $new->scheme = $scheme; + $new->removeDefaultPort(); + $new->validateState(); + + return $new; + } + + public function withUserInfo($user, $password = null) + { + $info = $this->filterUserInfoComponent($user); + if ($password !== null) { + $info .= ':' . $this->filterUserInfoComponent($password); + } + + if ($this->userInfo === $info) { + return $this; + } + + $new = clone $this; + $new->userInfo = $info; + $new->validateState(); + + return $new; + } + + public function withHost($host) + { + $host = $this->filterHost($host); + + if ($this->host === $host) { + return $this; + } + + $new = clone $this; + $new->host = $host; + $new->validateState(); + + return $new; + } + + public function withPort($port) + { + $port = $this->filterPort($port); + + if ($this->port === $port) { + return $this; + } + + $new = clone $this; + $new->port = $port; + $new->removeDefaultPort(); + $new->validateState(); + + return $new; + } + + public function withPath($path) + { + $path = $this->filterPath($path); + + if ($this->path === $path) { + return $this; + } + + $new = clone $this; + $new->path = $path; + $new->validateState(); + + return $new; + } + + public function withQuery($query) + { + $query = $this->filterQueryAndFragment($query); + + if ($this->query === $query) { + return $this; + } + + $new = clone $this; + $new->query = $query; + + return $new; + } + + public function withFragment($fragment) + { + $fragment = $this->filterQueryAndFragment($fragment); + + if ($this->fragment === $fragment) { + return $this; + } + + $new = clone $this; + $new->fragment = $fragment; + + return $new; + } + + /** + * Apply parse_url parts to a URI. + * + * @param array $parts Array of parse_url parts to apply. + */ + private function applyParts(array $parts) + { + $this->scheme = isset($parts['scheme']) + ? $this->filterScheme($parts['scheme']) + : ''; + $this->userInfo = isset($parts['user']) + ? $this->filterUserInfoComponent($parts['user']) + : ''; + $this->host = isset($parts['host']) + ? $this->filterHost($parts['host']) + : ''; + $this->port = isset($parts['port']) + ? $this->filterPort($parts['port']) + : null; + $this->path = isset($parts['path']) + ? $this->filterPath($parts['path']) + : ''; + $this->query = isset($parts['query']) + ? $this->filterQueryAndFragment($parts['query']) + : ''; + $this->fragment = isset($parts['fragment']) + ? $this->filterQueryAndFragment($parts['fragment']) + : ''; + if (isset($parts['pass'])) { + $this->userInfo .= ':' . $this->filterUserInfoComponent($parts['pass']); + } + + $this->removeDefaultPort(); + } + + /** + * @param string $scheme + * + * @return string + * + * @throws \InvalidArgumentException If the scheme is invalid. + */ + private function filterScheme($scheme) + { + if (!is_string($scheme)) { + throw new \InvalidArgumentException('Scheme must be a string'); + } + + return strtolower($scheme); + } + + /** + * @param string $component + * + * @return string + * + * @throws \InvalidArgumentException If the user info is invalid. + */ + private function filterUserInfoComponent($component) + { + if (!is_string($component)) { + throw new \InvalidArgumentException('User info must be a string'); + } + + return preg_replace_callback( + '/(?:[^%' . self::$charUnreserved . self::$charSubDelims . ']+|%(?![A-Fa-f0-9]{2}))/', + [$this, 'rawurlencodeMatchZero'], + $component + ); + } + + /** + * @param string $host + * + * @return string + * + * @throws \InvalidArgumentException If the host is invalid. + */ + private function filterHost($host) + { + if (!is_string($host)) { + throw new \InvalidArgumentException('Host must be a string'); + } + + return strtolower($host); + } + + /** + * @param int|null $port + * + * @return int|null + * + * @throws \InvalidArgumentException If the port is invalid. + */ + private function filterPort($port) + { + if ($port === null) { + return null; + } + + $port = (int) $port; + if (0 > $port || 0xffff < $port) { + throw new \InvalidArgumentException( + sprintf('Invalid port: %d. Must be between 0 and 65535', $port) + ); + } + + return $port; + } + + /** + * @param UriInterface $uri + * @param array $keys + * + * @return array + */ + private static function getFilteredQueryString(UriInterface $uri, array $keys) + { + $current = $uri->getQuery(); + + if ($current === '') { + return []; + } + + $decodedKeys = array_map('rawurldecode', $keys); + + return array_filter(explode('&', $current), function ($part) use ($decodedKeys) { + return !in_array(rawurldecode(explode('=', $part)[0]), $decodedKeys, true); + }); + } + + /** + * @param string $key + * @param string|null $value + * + * @return string + */ + private static function generateQueryString($key, $value) + { + // Query string separators ("=", "&") within the key or value need to be encoded + // (while preventing double-encoding) before setting the query string. All other + // chars that need percent-encoding will be encoded by withQuery(). + $queryString = strtr($key, self::$replaceQuery); + + if ($value !== null) { + $queryString .= '=' . strtr($value, self::$replaceQuery); + } + + return $queryString; + } + + private function removeDefaultPort() + { + if ($this->port !== null && self::isDefaultPort($this)) { + $this->port = null; + } + } + + /** + * Filters the path of a URI + * + * @param string $path + * + * @return string + * + * @throws \InvalidArgumentException If the path is invalid. + */ + private function filterPath($path) + { + if (!is_string($path)) { + throw new \InvalidArgumentException('Path must be a string'); + } + + return preg_replace_callback( + '/(?:[^' . self::$charUnreserved . self::$charSubDelims . '%:@\/]++|%(?![A-Fa-f0-9]{2}))/', + [$this, 'rawurlencodeMatchZero'], + $path + ); + } + + /** + * Filters the query string or fragment of a URI. + * + * @param string $str + * + * @return string + * + * @throws \InvalidArgumentException If the query or fragment is invalid. + */ + private function filterQueryAndFragment($str) + { + if (!is_string($str)) { + throw new \InvalidArgumentException('Query and fragment must be a string'); + } + + return preg_replace_callback( + '/(?:[^' . self::$charUnreserved . self::$charSubDelims . '%:@\/\?]++|%(?![A-Fa-f0-9]{2}))/', + [$this, 'rawurlencodeMatchZero'], + $str + ); + } + + private function rawurlencodeMatchZero(array $match) + { + return rawurlencode($match[0]); + } + + private function validateState() + { + if ($this->host === '' && ($this->scheme === 'http' || $this->scheme === 'https')) { + $this->host = self::HTTP_DEFAULT_HOST; + } + + if ($this->getAuthority() === '') { + if (0 === strpos($this->path, '//')) { + throw new \InvalidArgumentException('The path of a URI without an authority must not start with two slashes "//"'); + } + if ($this->scheme === '' && false !== strpos(explode('/', $this->path, 2)[0], ':')) { + throw new \InvalidArgumentException('A relative URI must not have a path beginning with a segment containing a colon'); + } + } elseif (isset($this->path[0]) && $this->path[0] !== '/') { + @trigger_error( + 'The path of a URI with an authority must start with a slash "/" or be empty. Automagically fixing the URI ' . + 'by adding a leading slash to the path is deprecated since version 1.4 and will throw an exception instead.', + E_USER_DEPRECATED + ); + $this->path = '/'. $this->path; + //throw new \InvalidArgumentException('The path of a URI with an authority must start with a slash "/" or be empty'); + } + } +} diff --git a/instafeed/vendor/guzzlehttp/psr7/src/UriNormalizer.php b/instafeed/vendor/guzzlehttp/psr7/src/UriNormalizer.php new file mode 100755 index 0000000..384c29e --- /dev/null +++ b/instafeed/vendor/guzzlehttp/psr7/src/UriNormalizer.php @@ -0,0 +1,216 @@ +getPath() === '' && + ($uri->getScheme() === 'http' || $uri->getScheme() === 'https') + ) { + $uri = $uri->withPath('/'); + } + + if ($flags & self::REMOVE_DEFAULT_HOST && $uri->getScheme() === 'file' && $uri->getHost() === 'localhost') { + $uri = $uri->withHost(''); + } + + if ($flags & self::REMOVE_DEFAULT_PORT && $uri->getPort() !== null && Uri::isDefaultPort($uri)) { + $uri = $uri->withPort(null); + } + + if ($flags & self::REMOVE_DOT_SEGMENTS && !Uri::isRelativePathReference($uri)) { + $uri = $uri->withPath(UriResolver::removeDotSegments($uri->getPath())); + } + + if ($flags & self::REMOVE_DUPLICATE_SLASHES) { + $uri = $uri->withPath(preg_replace('#//++#', '/', $uri->getPath())); + } + + if ($flags & self::SORT_QUERY_PARAMETERS && $uri->getQuery() !== '') { + $queryKeyValues = explode('&', $uri->getQuery()); + sort($queryKeyValues); + $uri = $uri->withQuery(implode('&', $queryKeyValues)); + } + + return $uri; + } + + /** + * Whether two URIs can be considered equivalent. + * + * Both URIs are normalized automatically before comparison with the given $normalizations bitmask. The method also + * accepts relative URI references and returns true when they are equivalent. This of course assumes they will be + * resolved against the same base URI. If this is not the case, determination of equivalence or difference of + * relative references does not mean anything. + * + * @param UriInterface $uri1 An URI to compare + * @param UriInterface $uri2 An URI to compare + * @param int $normalizations A bitmask of normalizations to apply, see constants + * + * @return bool + * @link https://tools.ietf.org/html/rfc3986#section-6.1 + */ + public static function isEquivalent(UriInterface $uri1, UriInterface $uri2, $normalizations = self::PRESERVING_NORMALIZATIONS) + { + return (string) self::normalize($uri1, $normalizations) === (string) self::normalize($uri2, $normalizations); + } + + private static function capitalizePercentEncoding(UriInterface $uri) + { + $regex = '/(?:%[A-Fa-f0-9]{2})++/'; + + $callback = function (array $match) { + return strtoupper($match[0]); + }; + + return + $uri->withPath( + preg_replace_callback($regex, $callback, $uri->getPath()) + )->withQuery( + preg_replace_callback($regex, $callback, $uri->getQuery()) + ); + } + + private static function decodeUnreservedCharacters(UriInterface $uri) + { + $regex = '/%(?:2D|2E|5F|7E|3[0-9]|[46][1-9A-F]|[57][0-9A])/i'; + + $callback = function (array $match) { + return rawurldecode($match[0]); + }; + + return + $uri->withPath( + preg_replace_callback($regex, $callback, $uri->getPath()) + )->withQuery( + preg_replace_callback($regex, $callback, $uri->getQuery()) + ); + } + + private function __construct() + { + // cannot be instantiated + } +} diff --git a/instafeed/vendor/guzzlehttp/psr7/src/UriResolver.php b/instafeed/vendor/guzzlehttp/psr7/src/UriResolver.php new file mode 100755 index 0000000..c1cb8a2 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/psr7/src/UriResolver.php @@ -0,0 +1,219 @@ +getScheme() != '') { + return $rel->withPath(self::removeDotSegments($rel->getPath())); + } + + if ($rel->getAuthority() != '') { + $targetAuthority = $rel->getAuthority(); + $targetPath = self::removeDotSegments($rel->getPath()); + $targetQuery = $rel->getQuery(); + } else { + $targetAuthority = $base->getAuthority(); + if ($rel->getPath() === '') { + $targetPath = $base->getPath(); + $targetQuery = $rel->getQuery() != '' ? $rel->getQuery() : $base->getQuery(); + } else { + if ($rel->getPath()[0] === '/') { + $targetPath = $rel->getPath(); + } else { + if ($targetAuthority != '' && $base->getPath() === '') { + $targetPath = '/' . $rel->getPath(); + } else { + $lastSlashPos = strrpos($base->getPath(), '/'); + if ($lastSlashPos === false) { + $targetPath = $rel->getPath(); + } else { + $targetPath = substr($base->getPath(), 0, $lastSlashPos + 1) . $rel->getPath(); + } + } + } + $targetPath = self::removeDotSegments($targetPath); + $targetQuery = $rel->getQuery(); + } + } + + return new Uri(Uri::composeComponents( + $base->getScheme(), + $targetAuthority, + $targetPath, + $targetQuery, + $rel->getFragment() + )); + } + + /** + * Returns the target URI as a relative reference from the base URI. + * + * This method is the counterpart to resolve(): + * + * (string) $target === (string) UriResolver::resolve($base, UriResolver::relativize($base, $target)) + * + * One use-case is to use the current request URI as base URI and then generate relative links in your documents + * to reduce the document size or offer self-contained downloadable document archives. + * + * $base = new Uri('http://example.com/a/b/'); + * echo UriResolver::relativize($base, new Uri('http://example.com/a/b/c')); // prints 'c'. + * echo UriResolver::relativize($base, new Uri('http://example.com/a/x/y')); // prints '../x/y'. + * echo UriResolver::relativize($base, new Uri('http://example.com/a/b/?q')); // prints '?q'. + * echo UriResolver::relativize($base, new Uri('http://example.org/a/b/')); // prints '//example.org/a/b/'. + * + * This method also accepts a target that is already relative and will try to relativize it further. Only a + * relative-path reference will be returned as-is. + * + * echo UriResolver::relativize($base, new Uri('/a/b/c')); // prints 'c' as well + * + * @param UriInterface $base Base URI + * @param UriInterface $target Target URI + * + * @return UriInterface The relative URI reference + */ + public static function relativize(UriInterface $base, UriInterface $target) + { + if ($target->getScheme() !== '' && + ($base->getScheme() !== $target->getScheme() || $target->getAuthority() === '' && $base->getAuthority() !== '') + ) { + return $target; + } + + if (Uri::isRelativePathReference($target)) { + // As the target is already highly relative we return it as-is. It would be possible to resolve + // the target with `$target = self::resolve($base, $target);` and then try make it more relative + // by removing a duplicate query. But let's not do that automatically. + return $target; + } + + if ($target->getAuthority() !== '' && $base->getAuthority() !== $target->getAuthority()) { + return $target->withScheme(''); + } + + // We must remove the path before removing the authority because if the path starts with two slashes, the URI + // would turn invalid. And we also cannot set a relative path before removing the authority, as that is also + // invalid. + $emptyPathUri = $target->withScheme('')->withPath('')->withUserInfo('')->withPort(null)->withHost(''); + + if ($base->getPath() !== $target->getPath()) { + return $emptyPathUri->withPath(self::getRelativePath($base, $target)); + } + + if ($base->getQuery() === $target->getQuery()) { + // Only the target fragment is left. And it must be returned even if base and target fragment are the same. + return $emptyPathUri->withQuery(''); + } + + // If the base URI has a query but the target has none, we cannot return an empty path reference as it would + // inherit the base query component when resolving. + if ($target->getQuery() === '') { + $segments = explode('/', $target->getPath()); + $lastSegment = end($segments); + + return $emptyPathUri->withPath($lastSegment === '' ? './' : $lastSegment); + } + + return $emptyPathUri; + } + + private static function getRelativePath(UriInterface $base, UriInterface $target) + { + $sourceSegments = explode('/', $base->getPath()); + $targetSegments = explode('/', $target->getPath()); + array_pop($sourceSegments); + $targetLastSegment = array_pop($targetSegments); + foreach ($sourceSegments as $i => $segment) { + if (isset($targetSegments[$i]) && $segment === $targetSegments[$i]) { + unset($sourceSegments[$i], $targetSegments[$i]); + } else { + break; + } + } + $targetSegments[] = $targetLastSegment; + $relativePath = str_repeat('../', count($sourceSegments)) . implode('/', $targetSegments); + + // A reference to am empty last segment or an empty first sub-segment must be prefixed with "./". + // This also applies to a segment with a colon character (e.g., "file:colon") that cannot be used + // as the first segment of a relative-path reference, as it would be mistaken for a scheme name. + if ('' === $relativePath || false !== strpos(explode('/', $relativePath, 2)[0], ':')) { + $relativePath = "./$relativePath"; + } elseif ('/' === $relativePath[0]) { + if ($base->getAuthority() != '' && $base->getPath() === '') { + // In this case an extra slash is added by resolve() automatically. So we must not add one here. + $relativePath = ".$relativePath"; + } else { + $relativePath = "./$relativePath"; + } + } + + return $relativePath; + } + + private function __construct() + { + // cannot be instantiated + } +} diff --git a/instafeed/vendor/guzzlehttp/psr7/src/functions.php b/instafeed/vendor/guzzlehttp/psr7/src/functions.php new file mode 100755 index 0000000..8e6dafe --- /dev/null +++ b/instafeed/vendor/guzzlehttp/psr7/src/functions.php @@ -0,0 +1,899 @@ +getMethod() . ' ' + . $message->getRequestTarget()) + . ' HTTP/' . $message->getProtocolVersion(); + if (!$message->hasHeader('host')) { + $msg .= "\r\nHost: " . $message->getUri()->getHost(); + } + } elseif ($message instanceof ResponseInterface) { + $msg = 'HTTP/' . $message->getProtocolVersion() . ' ' + . $message->getStatusCode() . ' ' + . $message->getReasonPhrase(); + } else { + throw new \InvalidArgumentException('Unknown message type'); + } + + foreach ($message->getHeaders() as $name => $values) { + $msg .= "\r\n{$name}: " . implode(', ', $values); + } + + return "{$msg}\r\n\r\n" . $message->getBody(); +} + +/** + * Returns a UriInterface for the given value. + * + * This function accepts a string or {@see Psr\Http\Message\UriInterface} and + * returns a UriInterface for the given value. If the value is already a + * `UriInterface`, it is returned as-is. + * + * @param string|UriInterface $uri + * + * @return UriInterface + * @throws \InvalidArgumentException + */ +function uri_for($uri) +{ + if ($uri instanceof UriInterface) { + return $uri; + } elseif (is_string($uri)) { + return new Uri($uri); + } + + throw new \InvalidArgumentException('URI must be a string or UriInterface'); +} + +/** + * Create a new stream based on the input type. + * + * Options is an associative array that can contain the following keys: + * - metadata: Array of custom metadata. + * - size: Size of the stream. + * + * @param resource|string|null|int|float|bool|StreamInterface|callable|\Iterator $resource Entity body data + * @param array $options Additional options + * + * @return StreamInterface + * @throws \InvalidArgumentException if the $resource arg is not valid. + */ +function stream_for($resource = '', array $options = []) +{ + if (is_scalar($resource)) { + $stream = fopen('php://temp', 'r+'); + if ($resource !== '') { + fwrite($stream, $resource); + fseek($stream, 0); + } + return new Stream($stream, $options); + } + + switch (gettype($resource)) { + case 'resource': + return new Stream($resource, $options); + case 'object': + if ($resource instanceof StreamInterface) { + return $resource; + } elseif ($resource instanceof \Iterator) { + return new PumpStream(function () use ($resource) { + if (!$resource->valid()) { + return false; + } + $result = $resource->current(); + $resource->next(); + return $result; + }, $options); + } elseif (method_exists($resource, '__toString')) { + return stream_for((string) $resource, $options); + } + break; + case 'NULL': + return new Stream(fopen('php://temp', 'r+'), $options); + } + + if (is_callable($resource)) { + return new PumpStream($resource, $options); + } + + throw new \InvalidArgumentException('Invalid resource type: ' . gettype($resource)); +} + +/** + * Parse an array of header values containing ";" separated data into an + * array of associative arrays representing the header key value pair + * data of the header. When a parameter does not contain a value, but just + * contains a key, this function will inject a key with a '' string value. + * + * @param string|array $header Header to parse into components. + * + * @return array Returns the parsed header values. + */ +function parse_header($header) +{ + static $trimmed = "\"' \n\t\r"; + $params = $matches = []; + + foreach (normalize_header($header) as $val) { + $part = []; + foreach (preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) as $kvp) { + if (preg_match_all('/<[^>]+>|[^=]+/', $kvp, $matches)) { + $m = $matches[0]; + if (isset($m[1])) { + $part[trim($m[0], $trimmed)] = trim($m[1], $trimmed); + } else { + $part[] = trim($m[0], $trimmed); + } + } + } + if ($part) { + $params[] = $part; + } + } + + return $params; +} + +/** + * Converts an array of header values that may contain comma separated + * headers into an array of headers with no comma separated values. + * + * @param string|array $header Header to normalize. + * + * @return array Returns the normalized header field values. + */ +function normalize_header($header) +{ + if (!is_array($header)) { + return array_map('trim', explode(',', $header)); + } + + $result = []; + foreach ($header as $value) { + foreach ((array) $value as $v) { + if (strpos($v, ',') === false) { + $result[] = $v; + continue; + } + foreach (preg_split('/,(?=([^"]*"[^"]*")*[^"]*$)/', $v) as $vv) { + $result[] = trim($vv); + } + } + } + + return $result; +} + +/** + * Clone and modify a request with the given changes. + * + * The changes can be one of: + * - method: (string) Changes the HTTP method. + * - set_headers: (array) Sets the given headers. + * - remove_headers: (array) Remove the given headers. + * - body: (mixed) Sets the given body. + * - uri: (UriInterface) Set the URI. + * - query: (string) Set the query string value of the URI. + * - version: (string) Set the protocol version. + * + * @param RequestInterface $request Request to clone and modify. + * @param array $changes Changes to apply. + * + * @return RequestInterface + */ +function modify_request(RequestInterface $request, array $changes) +{ + if (!$changes) { + return $request; + } + + $headers = $request->getHeaders(); + + if (!isset($changes['uri'])) { + $uri = $request->getUri(); + } else { + // Remove the host header if one is on the URI + if ($host = $changes['uri']->getHost()) { + $changes['set_headers']['Host'] = $host; + + if ($port = $changes['uri']->getPort()) { + $standardPorts = ['http' => 80, 'https' => 443]; + $scheme = $changes['uri']->getScheme(); + if (isset($standardPorts[$scheme]) && $port != $standardPorts[$scheme]) { + $changes['set_headers']['Host'] .= ':'.$port; + } + } + } + $uri = $changes['uri']; + } + + if (!empty($changes['remove_headers'])) { + $headers = _caseless_remove($changes['remove_headers'], $headers); + } + + if (!empty($changes['set_headers'])) { + $headers = _caseless_remove(array_keys($changes['set_headers']), $headers); + $headers = $changes['set_headers'] + $headers; + } + + if (isset($changes['query'])) { + $uri = $uri->withQuery($changes['query']); + } + + if ($request instanceof ServerRequestInterface) { + return (new ServerRequest( + isset($changes['method']) ? $changes['method'] : $request->getMethod(), + $uri, + $headers, + isset($changes['body']) ? $changes['body'] : $request->getBody(), + isset($changes['version']) + ? $changes['version'] + : $request->getProtocolVersion(), + $request->getServerParams() + )) + ->withParsedBody($request->getParsedBody()) + ->withQueryParams($request->getQueryParams()) + ->withCookieParams($request->getCookieParams()) + ->withUploadedFiles($request->getUploadedFiles()); + } + + return new Request( + isset($changes['method']) ? $changes['method'] : $request->getMethod(), + $uri, + $headers, + isset($changes['body']) ? $changes['body'] : $request->getBody(), + isset($changes['version']) + ? $changes['version'] + : $request->getProtocolVersion() + ); +} + +/** + * Attempts to rewind a message body and throws an exception on failure. + * + * The body of the message will only be rewound if a call to `tell()` returns a + * value other than `0`. + * + * @param MessageInterface $message Message to rewind + * + * @throws \RuntimeException + */ +function rewind_body(MessageInterface $message) +{ + $body = $message->getBody(); + + if ($body->tell()) { + $body->rewind(); + } +} + +/** + * Safely opens a PHP stream resource using a filename. + * + * When fopen fails, PHP normally raises a warning. This function adds an + * error handler that checks for errors and throws an exception instead. + * + * @param string $filename File to open + * @param string $mode Mode used to open the file + * + * @return resource + * @throws \RuntimeException if the file cannot be opened + */ +function try_fopen($filename, $mode) +{ + $ex = null; + set_error_handler(function () use ($filename, $mode, &$ex) { + $ex = new \RuntimeException(sprintf( + 'Unable to open %s using mode %s: %s', + $filename, + $mode, + func_get_args()[1] + )); + }); + + $handle = fopen($filename, $mode); + restore_error_handler(); + + if ($ex) { + /** @var $ex \RuntimeException */ + throw $ex; + } + + return $handle; +} + +/** + * Copy the contents of a stream into a string until the given number of + * bytes have been read. + * + * @param StreamInterface $stream Stream to read + * @param int $maxLen Maximum number of bytes to read. Pass -1 + * to read the entire stream. + * @return string + * @throws \RuntimeException on error. + */ +function copy_to_string(StreamInterface $stream, $maxLen = -1) +{ + $buffer = ''; + + if ($maxLen === -1) { + while (!$stream->eof()) { + $buf = $stream->read(1048576); + // Using a loose equality here to match on '' and false. + if ($buf == null) { + break; + } + $buffer .= $buf; + } + return $buffer; + } + + $len = 0; + while (!$stream->eof() && $len < $maxLen) { + $buf = $stream->read($maxLen - $len); + // Using a loose equality here to match on '' and false. + if ($buf == null) { + break; + } + $buffer .= $buf; + $len = strlen($buffer); + } + + return $buffer; +} + +/** + * Copy the contents of a stream into another stream until the given number + * of bytes have been read. + * + * @param StreamInterface $source Stream to read from + * @param StreamInterface $dest Stream to write to + * @param int $maxLen Maximum number of bytes to read. Pass -1 + * to read the entire stream. + * + * @throws \RuntimeException on error. + */ +function copy_to_stream( + StreamInterface $source, + StreamInterface $dest, + $maxLen = -1 +) { + $bufferSize = 8192; + + if ($maxLen === -1) { + while (!$source->eof()) { + if (!$dest->write($source->read($bufferSize))) { + break; + } + } + } else { + $remaining = $maxLen; + while ($remaining > 0 && !$source->eof()) { + $buf = $source->read(min($bufferSize, $remaining)); + $len = strlen($buf); + if (!$len) { + break; + } + $remaining -= $len; + $dest->write($buf); + } + } +} + +/** + * Calculate a hash of a Stream + * + * @param StreamInterface $stream Stream to calculate the hash for + * @param string $algo Hash algorithm (e.g. md5, crc32, etc) + * @param bool $rawOutput Whether or not to use raw output + * + * @return string Returns the hash of the stream + * @throws \RuntimeException on error. + */ +function hash( + StreamInterface $stream, + $algo, + $rawOutput = false +) { + $pos = $stream->tell(); + + if ($pos > 0) { + $stream->rewind(); + } + + $ctx = hash_init($algo); + while (!$stream->eof()) { + hash_update($ctx, $stream->read(1048576)); + } + + $out = hash_final($ctx, (bool) $rawOutput); + $stream->seek($pos); + + return $out; +} + +/** + * Read a line from the stream up to the maximum allowed buffer length + * + * @param StreamInterface $stream Stream to read from + * @param int $maxLength Maximum buffer length + * + * @return string + */ +function readline(StreamInterface $stream, $maxLength = null) +{ + $buffer = ''; + $size = 0; + + while (!$stream->eof()) { + // Using a loose equality here to match on '' and false. + if (null == ($byte = $stream->read(1))) { + return $buffer; + } + $buffer .= $byte; + // Break when a new line is found or the max length - 1 is reached + if ($byte === "\n" || ++$size === $maxLength - 1) { + break; + } + } + + return $buffer; +} + +/** + * Parses a request message string into a request object. + * + * @param string $message Request message string. + * + * @return Request + */ +function parse_request($message) +{ + $data = _parse_message($message); + $matches = []; + if (!preg_match('/^[\S]+\s+([a-zA-Z]+:\/\/|\/).*/', $data['start-line'], $matches)) { + throw new \InvalidArgumentException('Invalid request string'); + } + $parts = explode(' ', $data['start-line'], 3); + $version = isset($parts[2]) ? explode('/', $parts[2])[1] : '1.1'; + + $request = new Request( + $parts[0], + $matches[1] === '/' ? _parse_request_uri($parts[1], $data['headers']) : $parts[1], + $data['headers'], + $data['body'], + $version + ); + + return $matches[1] === '/' ? $request : $request->withRequestTarget($parts[1]); +} + +/** + * Parses a response message string into a response object. + * + * @param string $message Response message string. + * + * @return Response + */ +function parse_response($message) +{ + $data = _parse_message($message); + // According to https://tools.ietf.org/html/rfc7230#section-3.1.2 the space + // between status-code and reason-phrase is required. But browsers accept + // responses without space and reason as well. + if (!preg_match('/^HTTP\/.* [0-9]{3}( .*|$)/', $data['start-line'])) { + throw new \InvalidArgumentException('Invalid response string: ' . $data['start-line']); + } + $parts = explode(' ', $data['start-line'], 3); + + return new Response( + $parts[1], + $data['headers'], + $data['body'], + explode('/', $parts[0])[1], + isset($parts[2]) ? $parts[2] : null + ); +} + +/** + * Parse a query string into an associative array. + * + * If multiple values are found for the same key, the value of that key + * value pair will become an array. This function does not parse nested + * PHP style arrays into an associative array (e.g., foo[a]=1&foo[b]=2 will + * be parsed into ['foo[a]' => '1', 'foo[b]' => '2']). + * + * @param string $str Query string to parse + * @param int|bool $urlEncoding How the query string is encoded + * + * @return array + */ +function parse_query($str, $urlEncoding = true) +{ + $result = []; + + if ($str === '') { + return $result; + } + + if ($urlEncoding === true) { + $decoder = function ($value) { + return rawurldecode(str_replace('+', ' ', $value)); + }; + } elseif ($urlEncoding === PHP_QUERY_RFC3986) { + $decoder = 'rawurldecode'; + } elseif ($urlEncoding === PHP_QUERY_RFC1738) { + $decoder = 'urldecode'; + } else { + $decoder = function ($str) { return $str; }; + } + + foreach (explode('&', $str) as $kvp) { + $parts = explode('=', $kvp, 2); + $key = $decoder($parts[0]); + $value = isset($parts[1]) ? $decoder($parts[1]) : null; + if (!isset($result[$key])) { + $result[$key] = $value; + } else { + if (!is_array($result[$key])) { + $result[$key] = [$result[$key]]; + } + $result[$key][] = $value; + } + } + + return $result; +} + +/** + * Build a query string from an array of key value pairs. + * + * This function can use the return value of parse_query() to build a query + * string. This function does not modify the provided keys when an array is + * encountered (like http_build_query would). + * + * @param array $params Query string parameters. + * @param int|false $encoding Set to false to not encode, PHP_QUERY_RFC3986 + * to encode using RFC3986, or PHP_QUERY_RFC1738 + * to encode using RFC1738. + * @return string + */ +function build_query(array $params, $encoding = PHP_QUERY_RFC3986) +{ + if (!$params) { + return ''; + } + + if ($encoding === false) { + $encoder = function ($str) { return $str; }; + } elseif ($encoding === PHP_QUERY_RFC3986) { + $encoder = 'rawurlencode'; + } elseif ($encoding === PHP_QUERY_RFC1738) { + $encoder = 'urlencode'; + } else { + throw new \InvalidArgumentException('Invalid type'); + } + + $qs = ''; + foreach ($params as $k => $v) { + $k = $encoder($k); + if (!is_array($v)) { + $qs .= $k; + if ($v !== null) { + $qs .= '=' . $encoder($v); + } + $qs .= '&'; + } else { + foreach ($v as $vv) { + $qs .= $k; + if ($vv !== null) { + $qs .= '=' . $encoder($vv); + } + $qs .= '&'; + } + } + } + + return $qs ? (string) substr($qs, 0, -1) : ''; +} + +/** + * Determines the mimetype of a file by looking at its extension. + * + * @param $filename + * + * @return null|string + */ +function mimetype_from_filename($filename) +{ + return mimetype_from_extension(pathinfo($filename, PATHINFO_EXTENSION)); +} + +/** + * Maps a file extensions to a mimetype. + * + * @param $extension string The file extension. + * + * @return string|null + * @link http://svn.apache.org/repos/asf/httpd/httpd/branches/1.3.x/conf/mime.types + */ +function mimetype_from_extension($extension) +{ + static $mimetypes = [ + '3gp' => 'video/3gpp', + '7z' => 'application/x-7z-compressed', + 'aac' => 'audio/x-aac', + 'ai' => 'application/postscript', + 'aif' => 'audio/x-aiff', + 'asc' => 'text/plain', + 'asf' => 'video/x-ms-asf', + 'atom' => 'application/atom+xml', + 'avi' => 'video/x-msvideo', + 'bmp' => 'image/bmp', + 'bz2' => 'application/x-bzip2', + 'cer' => 'application/pkix-cert', + 'crl' => 'application/pkix-crl', + 'crt' => 'application/x-x509-ca-cert', + 'css' => 'text/css', + 'csv' => 'text/csv', + 'cu' => 'application/cu-seeme', + 'deb' => 'application/x-debian-package', + 'doc' => 'application/msword', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'dvi' => 'application/x-dvi', + 'eot' => 'application/vnd.ms-fontobject', + 'eps' => 'application/postscript', + 'epub' => 'application/epub+zip', + 'etx' => 'text/x-setext', + 'flac' => 'audio/flac', + 'flv' => 'video/x-flv', + 'gif' => 'image/gif', + 'gz' => 'application/gzip', + 'htm' => 'text/html', + 'html' => 'text/html', + 'ico' => 'image/x-icon', + 'ics' => 'text/calendar', + 'ini' => 'text/plain', + 'iso' => 'application/x-iso9660-image', + 'jar' => 'application/java-archive', + 'jpe' => 'image/jpeg', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'js' => 'text/javascript', + 'json' => 'application/json', + 'latex' => 'application/x-latex', + 'log' => 'text/plain', + 'm4a' => 'audio/mp4', + 'm4v' => 'video/mp4', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mov' => 'video/quicktime', + 'mkv' => 'video/x-matroska', + 'mp3' => 'audio/mpeg', + 'mp4' => 'video/mp4', + 'mp4a' => 'audio/mp4', + 'mp4v' => 'video/mp4', + 'mpe' => 'video/mpeg', + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mpg4' => 'video/mp4', + 'oga' => 'audio/ogg', + 'ogg' => 'audio/ogg', + 'ogv' => 'video/ogg', + 'ogx' => 'application/ogg', + 'pbm' => 'image/x-portable-bitmap', + 'pdf' => 'application/pdf', + 'pgm' => 'image/x-portable-graymap', + 'png' => 'image/png', + 'pnm' => 'image/x-portable-anymap', + 'ppm' => 'image/x-portable-pixmap', + 'ppt' => 'application/vnd.ms-powerpoint', + 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'ps' => 'application/postscript', + 'qt' => 'video/quicktime', + 'rar' => 'application/x-rar-compressed', + 'ras' => 'image/x-cmu-raster', + 'rss' => 'application/rss+xml', + 'rtf' => 'application/rtf', + 'sgm' => 'text/sgml', + 'sgml' => 'text/sgml', + 'svg' => 'image/svg+xml', + 'swf' => 'application/x-shockwave-flash', + 'tar' => 'application/x-tar', + 'tif' => 'image/tiff', + 'tiff' => 'image/tiff', + 'torrent' => 'application/x-bittorrent', + 'ttf' => 'application/x-font-ttf', + 'txt' => 'text/plain', + 'wav' => 'audio/x-wav', + 'webm' => 'video/webm', + 'webp' => 'image/webp', + 'wma' => 'audio/x-ms-wma', + 'wmv' => 'video/x-ms-wmv', + 'woff' => 'application/x-font-woff', + 'wsdl' => 'application/wsdl+xml', + 'xbm' => 'image/x-xbitmap', + 'xls' => 'application/vnd.ms-excel', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'xml' => 'application/xml', + 'xpm' => 'image/x-xpixmap', + 'xwd' => 'image/x-xwindowdump', + 'yaml' => 'text/yaml', + 'yml' => 'text/yaml', + 'zip' => 'application/zip', + ]; + + $extension = strtolower($extension); + + return isset($mimetypes[$extension]) + ? $mimetypes[$extension] + : null; +} + +/** + * Parses an HTTP message into an associative array. + * + * The array contains the "start-line" key containing the start line of + * the message, "headers" key containing an associative array of header + * array values, and a "body" key containing the body of the message. + * + * @param string $message HTTP request or response to parse. + * + * @return array + * @internal + */ +function _parse_message($message) +{ + if (!$message) { + throw new \InvalidArgumentException('Invalid message'); + } + + $message = ltrim($message, "\r\n"); + + $messageParts = preg_split("/\r?\n\r?\n/", $message, 2); + + if ($messageParts === false || count($messageParts) !== 2) { + throw new \InvalidArgumentException('Invalid message: Missing header delimiter'); + } + + list($rawHeaders, $body) = $messageParts; + $rawHeaders .= "\r\n"; // Put back the delimiter we split previously + $headerParts = preg_split("/\r?\n/", $rawHeaders, 2); + + if ($headerParts === false || count($headerParts) !== 2) { + throw new \InvalidArgumentException('Invalid message: Missing status line'); + } + + list($startLine, $rawHeaders) = $headerParts; + + if (preg_match("/(?:^HTTP\/|^[A-Z]+ \S+ HTTP\/)(\d+(?:\.\d+)?)/i", $startLine, $matches) && $matches[1] === '1.0') { + // Header folding is deprecated for HTTP/1.1, but allowed in HTTP/1.0 + $rawHeaders = preg_replace(Rfc7230::HEADER_FOLD_REGEX, ' ', $rawHeaders); + } + + /** @var array[] $headerLines */ + $count = preg_match_all(Rfc7230::HEADER_REGEX, $rawHeaders, $headerLines, PREG_SET_ORDER); + + // If these aren't the same, then one line didn't match and there's an invalid header. + if ($count !== substr_count($rawHeaders, "\n")) { + // Folding is deprecated, see https://tools.ietf.org/html/rfc7230#section-3.2.4 + if (preg_match(Rfc7230::HEADER_FOLD_REGEX, $rawHeaders)) { + throw new \InvalidArgumentException('Invalid header syntax: Obsolete line folding'); + } + + throw new \InvalidArgumentException('Invalid header syntax'); + } + + $headers = []; + + foreach ($headerLines as $headerLine) { + $headers[$headerLine[1]][] = $headerLine[2]; + } + + return [ + 'start-line' => $startLine, + 'headers' => $headers, + 'body' => $body, + ]; +} + +/** + * Constructs a URI for an HTTP request message. + * + * @param string $path Path from the start-line + * @param array $headers Array of headers (each value an array). + * + * @return string + * @internal + */ +function _parse_request_uri($path, array $headers) +{ + $hostKey = array_filter(array_keys($headers), function ($k) { + return strtolower($k) === 'host'; + }); + + // If no host is found, then a full URI cannot be constructed. + if (!$hostKey) { + return $path; + } + + $host = $headers[reset($hostKey)][0]; + $scheme = substr($host, -4) === ':443' ? 'https' : 'http'; + + return $scheme . '://' . $host . '/' . ltrim($path, '/'); +} + +/** + * Get a short summary of the message body + * + * Will return `null` if the response is not printable. + * + * @param MessageInterface $message The message to get the body summary + * @param int $truncateAt The maximum allowed size of the summary + * + * @return null|string + */ +function get_message_body_summary(MessageInterface $message, $truncateAt = 120) +{ + $body = $message->getBody(); + + if (!$body->isSeekable() || !$body->isReadable()) { + return null; + } + + $size = $body->getSize(); + + if ($size === 0) { + return null; + } + + $summary = $body->read($truncateAt); + $body->rewind(); + + if ($size > $truncateAt) { + $summary .= ' (truncated...)'; + } + + // Matches any printable character, including unicode characters: + // letters, marks, numbers, punctuation, spacing, and separators. + if (preg_match('/[^\pL\pM\pN\pP\pS\pZ\n\r\t]/', $summary)) { + return null; + } + + return $summary; +} + +/** @internal */ +function _caseless_remove($keys, array $data) +{ + $result = []; + + foreach ($keys as &$key) { + $key = strtolower($key); + } + + foreach ($data as $k => $v) { + if (!in_array(strtolower($k), $keys)) { + $result[$k] = $v; + } + } + + return $result; +} diff --git a/instafeed/vendor/guzzlehttp/psr7/src/functions_include.php b/instafeed/vendor/guzzlehttp/psr7/src/functions_include.php new file mode 100755 index 0000000..96a4a83 --- /dev/null +++ b/instafeed/vendor/guzzlehttp/psr7/src/functions_include.php @@ -0,0 +1,6 @@ +setFinder( + PhpCsFixer\Finder::create() + ->in(__DIR__) + ->name('/(?:^lazydoctor$|\.php$)/') + ) + ->setIndent(' ') + ->setLineEnding("\n") + ->setRules([ + '@Symfony' => true, + // Override @Symfony rules + 'pre_increment' => false, + 'blank_line_before_statement' => ['statements' => ['return', 'try', 'throw']], + 'phpdoc_align' => ['tags' => ['param', 'throws']], + 'method_argument_space' => ['ensure_fully_multiline' => false], + 'binary_operator_spaces' => [ + 'align_double_arrow' => true, + 'align_equals' => false, + ], + 'phpdoc_annotation_without_dot' => false, + 'yoda_style' => [ + // Symfony writes their conditions backwards; we use normal order. + 'equal' => false, + 'identical' => false, + 'less_and_greater' => false, + ], + 'is_null' => [ + // Replaces all is_null() with === null. + 'use_yoda_style' => false, + ], + // Custom rules + 'align_multiline_comment' => true, + 'phpdoc_add_missing_param_annotation' => ['only_untyped' => false], + 'ordered_imports' => true, + 'phpdoc_order' => true, + 'array_syntax' => ['syntax' => 'short'], + ]); diff --git a/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/.pre-commit.hook b/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/.pre-commit.hook new file mode 100755 index 0000000..4a87bd2 --- /dev/null +++ b/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/.pre-commit.hook @@ -0,0 +1,67 @@ +#!/bin/sh +# +# Copyright 2017 The LazyJsonMapper Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ---------------------------------------------------------------------------- +# +# Verifies that all files in the worktree follow our codestyle standards. +# +# Note that this script can't check that they're actually committing the nicely +# formatted code. It just checks that the worktree is clean. So if they've fixed +# all files but haven't added the codestyle fixes to their commit, they'll still +# pass this check. But it's still a great protection against most mistakes. +# +# To install this hook, just run the following in the project's root folder: +# ln -fs "../../.pre-commit.hook" .git/hooks/pre-commit +# + +# Redirect output to stderr. +exec 1>&2 + +# Git ensures that CWD is always the root of the project folder, so we can just +# run all tests without verifying what folder we are in... + +failed="no" +echo "[pre-commit] Checking work-tree codestyle..." + +# Check the general codestyle format. +echo "> Verifying php-cs-fixer..." +vendor/bin/php-cs-fixer fix --config=.php_cs.dist --allow-risky yes --dry-run +if [ $? -ne 0 ]; then + failed="yes" +fi + +# Look for specific problems with the style, related to our project. +echo "> Verifying checkStyle..." +/usr/bin/env php devtools/checkStyle.php x +if [ $? -ne 0 ]; then + failed="yes" +fi + +# Refuse to commit if there were problems. Instruct the user about solving it. +if [ "${failed}" = "yes" ]; then + # Yes there are lots of "echo" commands, because "\n" is not cross-platform. + echo "[commit failed] There are problems with your code..." + echo "" + echo "Run 'composer codestyle' to fix the code in your worktree." + echo "" + echo "But beware that the process is automatic, and that the result" + echo "isn't always perfect and won't be automatically staged." + echo "" + echo "So remember to manually read through the changes, then further" + echo "fix them if necessary, and finally stage the updated code" + echo "afterwards so that the fixed code gets committed." + exit 1 +fi diff --git a/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/LICENSE b/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/LICENSE new file mode 100755 index 0000000..8dada3e --- /dev/null +++ b/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/NOTICE b/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/NOTICE new file mode 100755 index 0000000..27c6183 --- /dev/null +++ b/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/NOTICE @@ -0,0 +1,5 @@ +LazyJsonMapper +Copyright 2017 The LazyJsonMapper Project + +This product includes software developed at +The LazyJsonMapper Project (https://github.com/SteveJobzniak/LazyJsonMapper). \ No newline at end of file diff --git a/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/README.md b/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/README.md new file mode 100755 index 0000000..7d3a997 --- /dev/null +++ b/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/README.md @@ -0,0 +1,211 @@ +# LazyJsonMapper + +## Advanced, intelligent & automatic object-oriented JSON containers for PHP. + +Implements a highly efficient, automatic, object-oriented and lightweight +(memory-wise) JSON data container. It provides intelligent data conversion +and parsing, to give you a nice, reliable interface to your JSON data, +without having to worry about doing any of the tedious parsing yourself. + +### Features: + +- Provides a completely object-oriented interface to all of your JSON data. + +- Automatically maps complex, nested JSON data structures onto real PHP + objects, with total support for nested objects and multi-level arrays. + +- Extremely optimized for very high performance and very low memory usage. + Much lower than other PHP JSON mappers that people have used in the past. + + For example, normal PHP objects with manually defined `$properties`, which + is what's used by _other_ JSON mappers, will consume memory for every + property even if that property wasn't in the JSON data (is a `NULL`). Our + system on the other hand takes up ZERO bytes of RAM for any properties + that don't exist in the current object's JSON data! + +- Automatically provides "direct virtual properties", which lets you + interact with the JSON data as if it were regular object properties, + such as `echo $item->some_value` and `$item->some_value = 'foo'`. + + The virtual properties can be disabled via an option. + +- Automatically provides object-oriented "virtual functions", which let you + interact with the data in a fully object-oriented way via functions such + as `$item->getSomeValue()` and `$item->setSomeValue('foo')`. We support a + large range of different functions for manipulating the JSON data, and you + can see a list of all available function names for all of your properties + by simply running `$item->printPropertyDescriptions()`. + + The virtual functions can be disabled via an option. + +- Includes the `LazyDoctor` tool, which _automatically_ documents all of + your `LazyJsonMapper`-based classes so that their virtual properties and + functions become _fully_ visible to your IDE and to various intelligent + code analysis tools. It also performs class diagnostics by compiling all + of your class property maps, which means that you can be 100% sure that + all of your maps are valid (compilable) if this tool runs successfully. + +- We provide a complete, internal API which your subclasses can use to + interact with the data inside of the JSON container. This allows you to + easily override the automatic functions or create additional functions + for your objects. To override core functions, just define a function with + the exact same name on your object and make it do whatever you want to. + + Here are some examples of function overriding: + + ```php + public function getFoo() + { + // try to read property, and handle a special "current_time" value. + $value = $this->_getProperty('foo'); + if ($value === 'current_time') { return time(); } + return $value; + } + public function setFoo( + $value) + { + // if they try to set value to "md5", we use a special value instead + if ($value === 'md5') { $value = md5(time()); } + return $this->_setProperty('foo', $value); + } + ``` + +- All mapping/data conversion is done "lazily", on a per-property basis. + When you access a property, that specific property is mapped/converted to + the proper type as defined by your class property map. No time or memory + is wasted converting properties that you never touch. + +- Strong type-system. The class property map controls the exact types and + array depths. You can fully trust that the data you get/set will match + your specifications. Invalid data that mismatches the spec is impossible. + +- Advanced settings system. Everything is easily configured via PHP class + constants, which means that your class-settings are stateless (there's no + need for any special "settings/mapper object" to keep track of settings), + and that all settings are immutable constants (which means that they are + reliable and can never mutate at runtime, so that you can fully trust that + classes will always behave as-defined in their code). + + If you want to override multiple core settings identically for all of your + classes, then simply create a subclass of `LazyJsonMapper` and configure + all of your settings on that, and then derive all of your other classes + from your re-configured subclass! + +- The world's most advanced mapper definition system. Your class property + maps are defined in an easy PHPdoc-style format, and support multilevel + arrays (such as `int[][]` for "an array of arrays of ints"), relative + types (so you can map properties to classes/objects that are relative to + the namespace of the class property map), parent inheritance (all of your + parent `extends`-hierarchy's maps will be included in your final property + map) and even multiple inheritance (you can literally "import" an infinite + number of other maps into your class, which don't come from your own + parent `extends`-hierarchy). + +- Inheriting properties from parent classes or importing properties from + other classes is a zero-cost operation thanks to how efficient our + property map compiler is. So feel free to import everything you need. + You can even use this system to create importable classes that just hold + "collections" of shared properties, which you import into other classes. + +- The class property maps are compiled a single time per-class at runtime, + the first time a class is used. The compilation process fully verifies + and compiles all property definitions, all parent maps, all inherited + maps, and all maps of all classes you link properties to. + + If there are any compilation problems due to a badly written map anywhere + in your hierarchy, you will be shown the exact problem in great detail. + + In case of success, the compiled and verified maps are all stored in an + incredibly memory-efficient format in a global cache which is shared by + your whole PHP runtime, which means that anything in your code or in any + other libraries which accesses the same classes will all share the cached + compilations of those classes, for maximum memory efficiency. + +- You are also able to access JSON properties that haven't been defined in + the class property map. In that case, they are treated as undefined and + untyped (`mixed`) and there won't be any automatic type-conversion of such + properties, but it can still be handy in a pinch. + +- There are lots of data export/output options for your object's JSON data, + to get it back out of the object again: As a multi-level array, as nested + stdClass objects, or as a JSON string representation of your object. + +- We include a whole assortment of incredibly advanced debugging features: + + You can run the constructor with `$requireAnalysis` to ensure that all + of your JSON data is successfully mapped according to your class property + map, and that you haven't missed defining any properties that exist in the + data. In case of any problems, the analysis message will give you a full + list of all problems encountered in your entire JSON data hierarchy. + + For your class property maps themselves, you can run functions such as + `printPropertyDescriptions()` to see a complete list of all properties and + how they are defined. This helps debug your class inheritance and imports + to visually see what your final class map looks like, and it also helps + users see all available properties and all of their virtual functions. + + And for the JSON data, you can use functions such as `printJson()` to get + a beautiful view of all internal JSON data, which is incredibly helpful + when you (or your users) need to figure out what's available inside the + current object instance's data storage. + +- A fine-grained and logical exception-system which ensures that you can + always trust the behavior of your objects and can catch problems easily. + And everything we throw is _always_ based on `LazyJsonMapperException`, + which means that you can simply catch that single "root" exception + whenever you don't care about fine-grained differentiation. + +- Clean and modular code ensures stability and future extensibility. + +- Deep code documentation explains everything you could ever wonder about. + +- Lastly, we implement super-efficient object serialization. Everything is + stored in a tightly packed format which minimizes data size when you need + to transfer your objects between runtimes. + +### Installation + +You need at least PHP 5.6 or higher. PHP 7+ is also fully supported and is recommended. + +Run the following [Composer](https://getcomposer.org/download/) installation command: + +``` +composer require lazyjsonmapper/lazyjsonmapper +``` + +### Examples + +View the contents of the [`examples/`](https://github.com/SteveJobzniak/LazyJsonMapper/tree/master/examples) folder. + +### Documentation + +Everything is fully documented directly within the source code of this library. + +You can also [read the same documentation online](https://mgp25.github.io/lazyjsonmapper-docs/namespaces/LazyJsonMapper.html) as nicely formatted HTML pages. + +### LazyDoctor + +Our automatic class-documentation and diagnostic utility will be placed within +your project's `./vendor/bin/` folder. Simply run it without any parameters to +see a list of all available options. You can also open that file in a regular +text editor to read some general usage tips and tricks at the top of the +utility's source code. + +### Copyright + +Copyright 2017 The LazyJsonMapper Project + +### License + +[Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) + +### Author + +SteveJobzniak + +### Contributing + +If you would like to contribute to this project, please feel free to submit a +pull request, or perhaps even sending a donation to a team member as a token of +your appreciation. + diff --git a/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/bin/lazydoctor b/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/bin/lazydoctor new file mode 100755 index 0000000..c142b53 --- /dev/null +++ b/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/bin/lazydoctor @@ -0,0 +1,738 @@ +#!/usr/bin/env php +/dev/null" to send STDOUT to the void... + * That way you'll ONLY see critical status messages during the processing. + */ + +set_time_limit(0); +date_default_timezone_set('UTC'); + +// Verify minimum PHP version. +if (!defined('PHP_VERSION_ID') || PHP_VERSION_ID < 50600) { + fwrite(STDERR, 'LazyDoctor requires PHP 5.6 or higher.'.PHP_EOL); + exit(1); +} + +// Register a simple GetOptionKit autoloader. This is fine because +// GetOptionKit has no 3rd party library dependencies. +spl_autoload_register(function ($class) { + // Check if this is a "GetOptionKit" load-request. + static $prefix = 'GetOptionKit\\'; + static $len = 13; // strlen($prefix) + if (strncmp($prefix, $class, $len) !== 0) { + return; + } + + // Find the "GetOptionKit" source folder. + static $dirs = [ + __DIR__.'/../../../corneltek/getoptionkit/src', + __DIR__.'/../vendor/corneltek/getoptionkit/src', + ]; + $baseDir = null; + foreach ($dirs as $dir) { + if (is_dir($dir) && ($dir = realpath($dir)) !== false) { + $baseDir = $dir; + break; + } + } + if ($baseDir === null) { + return; + } + + // Get the relative class name. + $relativeClass = substr($class, $len); + + // Generate PSR-4 file path to the class. + $file = sprintf('%s/%s.php', $baseDir, str_replace('\\', '/', $relativeClass)); + if (is_file($file)) { + require $file; + } +}); + +// Parse command line options... +use GetOptionKit\OptionCollection; +use GetOptionKit\OptionParser; +use GetOptionKit\OptionPrinter\ConsoleOptionPrinter; + +$specs = new OptionCollection(); +$specs->add('c|composer:=file', 'Path to your project\'s composer.json file.'); +$specs->add('p|properties?=boolean', 'Document virtual properties (if enabled for the classes).'); +$specs->add('f|functions?=boolean', 'Document virtual functions (if enabled for the classes).'); +$specs->add('o|document-overridden?=boolean', 'Always document virtual functions/properties even when they have been manually overridden by the class (or its parents).'); +$specs->add('w|windows?=boolean', 'Generate Windows-style ("\r\n") documentation line endings instead of the default Unix-style ("\n").'); +$specs->add('validate-only?=boolean', 'Validate current docs for all classes but don\'t write anything to disk.'); +$specs->add('h|help?=boolean', 'Show all available options.'); + +try { + $parser = new OptionParser($specs); + $result = $parser->parse($argv); + $options = [ + 'composer' => isset($result->keys['composer']) ? $result->keys['composer']->value : null, + 'properties' => isset($result->keys['properties']) && $result->keys['properties']->value !== false, + 'functions' => isset($result->keys['functions']) && $result->keys['functions']->value !== false, + 'document-overridden' => isset($result->keys['document-overridden']) && $result->keys['document-overridden']->value !== false, + 'windows' => isset($result->keys['windows']) && $result->keys['windows']->value !== false, + 'validate-only' => isset($result->keys['validate-only']) && $result->keys['validate-only']->value !== false, + 'help' => isset($result->keys['help']) && $result->keys['help']->value !== false, + ]; +} catch (Exception $e) { + // Warns in case of invalid option values. + fwrite(STDERR, $e->getMessage().PHP_EOL); + exit(1); +} + +// Verify options... +echo '[ LazyDoctor ]'.PHP_EOL.PHP_EOL; +if ($options['composer'] === null || $options['help']) { + if ($options['composer'] === null) { + fwrite(STDERR, 'You must provide the --composer option.'.PHP_EOL.PHP_EOL); + } + $printer = new ConsoleOptionPrinter(); + echo 'Available options:'.PHP_EOL.PHP_EOL; + echo $printer->render($specs); + exit($options['composer'] === null && !$options['help'] ? 1 : 0); +} + +if ($options['composer']->getBasename() !== 'composer.json') { + fwrite(STDERR, 'You must point to your project\'s composer.json file.'.PHP_EOL.'You used: "'.$options['composer']->getRealPath().'".'.PHP_EOL); + exit(1); +} + +// Decode the composer.json file... +$json = @json_decode(file_get_contents($options['composer']->getRealPath()), true); +if ($json === null) { + fwrite(STDERR, 'Unable to decode composer.json.'.PHP_EOL); + exit(1); +} + +// Determine the project folder's real root path... +$projectRoot = $options['composer']->getPathInfo()->getRealPath(); + +// Determine their namespace PSR-4 paths via their project's composer.json... +$namespaces = []; +foreach (['autoload', 'autoload-dev'] as $type) { + if (!isset($json[$type]['psr-4']) || !is_array($json[$type]['psr-4'])) { + continue; + } + + foreach ($json[$type]['psr-4'] as $namespace => $dir) { + // We don't support composer's empty "fallback" namespaces. + if ($namespace === '') { + fwrite(STDERR, 'Encountered illegal unnamed PSR-4 autoload namespace in composer.json.'.PHP_EOL); + exit(1); + } + + // Ensure that the namespace ends in backslash. + if (substr_compare($namespace, '\\', strlen($namespace) - 1, 1) !== 0) { + fwrite(STDERR, 'Encountered illegal namespace "'.$namespace.'" (does not end in backslash) in composer.json.'.PHP_EOL); + exit(1); + } + + // Ensure that the value is a string. + // NOTE: We allow empty strings, which corresponds to root folder. + if (!is_string($dir)) { + fwrite(STDERR, 'Encountered illegal non-string value for namespace "'.$namespace.'".'.PHP_EOL); + exit(1); + } + + // Now resolve the path name... + $path = sprintf('%s/%s', $projectRoot, $dir); + $realpath = realpath($path); + if ($realpath === false) { + fwrite(STDERR, 'Unable to resolve real path for "'.$path.'".'.PHP_EOL); + exit(1); + } + + // We don't allow the same directory to be defined multiple times. + if (isset($namespaces[$realpath])) { + fwrite(STDERR, 'Encountered duplicate namespace directory "'.$realpath.'" in composer.json.'.PHP_EOL); + exit(1); + } + + // And we're done! The namespace and its path have been resolved. + $namespaces[$realpath] = $namespace; + } +} + +// Verify that we found some namespaces... +if (empty($namespaces)) { + fwrite(STDERR, 'There are no PSR-4 autoload namespaces in your composer.json.'.PHP_EOL); + exit(1); +} + +// Now load the project's autoload.php file. +// NOTE: This is necessary so that we can autoload their classes... +$autoload = sprintf('%s/vendor/autoload.php', $projectRoot); +$realautoload = realpath($autoload); +if ($realautoload === false) { + fwrite(STDERR, 'Unable to find the project\'s Composer autoloader ("'.$autoload.'").'.PHP_EOL); + exit(1); +} +require $realautoload; + +// Verify that their project's autoloader contains LazyJsonMapper... +if (!class_exists('\LazyJsonMapper\LazyJsonMapper', true)) { // TRUE = Autoload. + fwrite(STDERR, 'Target project doesn\'t contain the LazyJsonMapper library.'.PHP_EOL); + exit(1); +} + +// Alright, display the current options... +echo 'Project: "'.$projectRoot.'".'.PHP_EOL + .'- Documentation Line Endings: '.($options['windows'] ? 'Windows ("\r\n")' : 'Unix ("\n")').'.'.PHP_EOL + .'- ['.($options['properties'] ? 'X' : ' ').'] Document Virtual Properties ("@property").'.PHP_EOL + .'- ['.($options['functions'] ? 'X' : ' ').'] Document Virtual Functions ("@method").'.PHP_EOL + .'- ['.($options['document-overridden'] ? 'X' : ' ').'] Document Overridden Properties/Functions.'.PHP_EOL; +if ($options['validate-only']) { + echo '- This is a validation run. Nothing will be written to disk.'.PHP_EOL; +} + +// We can now use our custom classes, since the autoloader has been imported... +use LazyJsonMapper\Exception\LazyJsonMapperException; +use LazyJsonMapper\Export\PropertyDescription; +use LazyJsonMapper\Property\PropertyMapCache; +use LazyJsonMapper\Property\PropertyMapCompiler; +use LazyJsonMapper\Utilities; + +/** + * Automatic LazyJsonMapper-class documentation generator. + * + * @copyright 2017 The LazyJsonMapper Project + * @license http://www.apache.org/licenses/LICENSE-2.0 + * @author SteveJobzniak (https://github.com/SteveJobzniak) + */ +class LazyClassDocumentor +{ + /** @var PropertyMapCache */ + private static $_propertyMapCache; + + /** @var array */ + private $_compiledPropertyMapLink; + + /** @var ReflectionClass */ + private $_reflector; + + /** @var array */ + private $_options; + + /** @var string Newline sequence. */ + private $_nl; + + /** + * Constructor. + * + * @param string $class + * @param array $options + * + * @throws ReflectionException + */ + public function __construct( + $class, + array $options) + { + if (self::$_propertyMapCache === null) { + self::$_propertyMapCache = new PropertyMapCache(); + } + $this->_reflector = new ReflectionClass($class); + $this->_options = $options; + $this->_nl = $options['windows'] ? "\r\n" : "\n"; + } + + /** + * Process the current class. + * + * @throws ReflectionException + * @throws LazyJsonMapperException + * + * @return bool `TRUE` if on-disk file has correct docs, otherwise `FALSE`. + */ + public function process() + { + // Only process user-defined classes (never any built-in PHP classes). + if (!$this->_reflector->isUserDefined()) { + return true; + } + + // There's nothing to do if this isn't a LazyJsonMapper subclass. + // NOTE: This properly skips "\LazyJsonMapper\LazyJsonMapper" itself. + if (!$this->_reflector->isSubclassOf('\LazyJsonMapper\LazyJsonMapper')) { + return true; + } + + // Compile this class property map if not yet built and cached. + $thisClassName = $this->_reflector->getName(); + if (!isset(self::$_propertyMapCache->classMaps[$thisClassName])) { + try { + PropertyMapCompiler::compileClassPropertyMap( // Throws. + self::$_propertyMapCache, + $thisClassName + ); + } catch (Exception $e) { + fwrite(STDERR, '> Unable to compile the class property map for "'.$thisClassName.'". Reason: '.$e->getMessage().PHP_EOL); + + return false; + } + } + + // Now link to the property map cache for our current class. + $this->_compiledPropertyMapLink = &self::$_propertyMapCache->classMaps[$thisClassName]; + + // Get the current class comment (string if ok, FALSE if none exists). + $currentDocComment = $this->_reflector->getDocComment(); + if (is_string($currentDocComment)) { + $currentDocComment = trim($currentDocComment); + } + + // Extract all relevant lines from the current comment. + $finalDocLines = $this->_extractRelevantLines($currentDocComment); + + // Generate the automatic summary line (classname followed by period). + $autoSummaryLine = $this->_reflector->getShortName().'.'; + + // If the 1st line is a classname followed by a period, update the name. + // NOTE: This ensures that we update all outdated auto-added classnames, + // and the risk of false positives is very low since we only document + // `LazyJsonMapper`-based classes with a `OneWord.`-style summary line. + // NOTE: Regex is from http://php.net/manual/en/language.oop5.basic.php, + // and yes we must run it in NON-UNICODE MODE, so that it parses on a + // byte by byte basis exactly like the real PHP classname interpreter. + if ( + isset($finalDocLines[0]) // The 1st line MUST exist to proceed. + && preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*\.$/', $finalDocLines[0]) + ) { + $finalDocLines[0] = $autoSummaryLine; + } + + // Generate the magic documentation lines for the current class. + $magicDocLines = $this->_generateMagicDocs(); + if (!empty($magicDocLines)) { + // If there are no lines already... add the automatic summary line. + if (empty($finalDocLines)) { + $finalDocLines[] = $autoSummaryLine; + } + + // Check the 1st char of the 1st line. If it's an @tag of any kind, + // insert automatic summary line at top and empty line after that. + elseif ($finalDocLines[0][0] === '@') { + array_unshift( + $finalDocLines, + $autoSummaryLine, + '' + ); + } + + $finalDocLines[] = ''; // Add empty line before our magic docs. + $finalDocLines = array_merge($finalDocLines, array_values($magicDocLines)); + } + unset($magicDocLines); + + // Generate the final doc-comment that this class is supposed to have. + if (!empty($finalDocLines)) { + // This will generate even if the class only contained an existing + // summary/tags and nothing was added by our magic handler. + foreach ($finalDocLines as &$line) { + $line = ($line === '' ? ' *' : " * {$line}"); + } + unset($line); + $finalDocComment = sprintf( + '/**%s%s%s */', + $this->_nl, + implode($this->_nl, $finalDocLines), + $this->_nl + ); + } else { + // The FALSE signifies that we want no class doc-block at all... + $finalDocComment = false; + } + unset($finalDocLines); + + // There's nothing to do if the doc-comment is already correct. + // NOTE: Both values are FALSE if no doc-comment exists and none wanted. + if ($currentDocComment === $finalDocComment) { + return true; + } + + // The docs mismatch. If this is a validate-run, just return false now. + if ($this->_options['validate-only']) { + fwrite(STDERR, '> Outdated class docs encountered in "'.$thisClassName.'". Aborting scan...'.PHP_EOL); + + return false; + } + + // Load the contents of the file... + $classFileName = $this->_reflector->getFileName(); + $fileLines = @file($classFileName); + if ($fileLines === false) { + fwrite(STDERR, '> Unable to read class file from disk: "'.$classFileName.'".'.PHP_EOL); + + return false; + } + + // Split the file into lines BEFORE the class and lines AFTER the class. + $classLine = $this->_reflector->getStartLine(); + $startLines = array_slice($fileLines, 0, $classLine - 1); + $endLines = array_slice($fileLines, $classLine - 1); + unset($fileLines); + + // Insert the new class documentation using a very careful algorithm. + if ($currentDocComment !== false) { + // Since the class already had PHPdoc, remove it and insert new doc. + // NOTE: A valid PHPdoc (getDocComment()) always starts with + // "/**[whitespace]". If it's just a "/*" or something like + // "/**Foo", then it's not detected by getDocComment(). However, the + // comment may be several lines above the class. So we'll have to do + // an intelligent search to find the old class-comment. As for the + // ending tag "*/", PHP doesn't care about whitespace around that. + // And it also doesn't let the user escape the "*/", which means + // that if we see that sequence we KNOW it's the end of a comment! + // NOTE: We'll search for the latest "/**[whitespace]" block and + // remove all lines from that until its closest "*/". + $deleteFrom = null; + $deleteTo = null; + for ($i = count($startLines) - 1; $i >= 0; --$i) { + if (strpos($startLines[$i], '*/') !== false) { + $deleteTo = $i; + } + if (preg_match('/^\s*\/\*\*\s/u', $startLines[$i])) { + $deleteFrom = $i; + break; + } + } + + // Ensure that we have found valid comment-offsets. + if ($deleteFrom === null || $deleteTo === null || $deleteTo < $deleteFrom) { + fwrite(STDERR, '> Unable to parse current class comment on disk: "'.$classFileName.'".'.PHP_EOL); + + return false; + } + + // Now update the startLines array to replace the doc-comment... + foreach ($startLines as $k => $v) { + if ($k === $deleteFrom && $finalDocComment !== false) { + // We've found the first line of the old comment, and we + // have a new comment. So replace that array entry. + $startLines[$k] = $finalDocComment.$this->_nl; + } elseif ($k >= $deleteFrom && $k <= $deleteTo) { + // Delete all other comment lines, including the first line + // if we had no new doc-comment. + unset($startLines[$k]); + } + + // Break if we've reached the final line to delete. + if ($k >= $deleteTo) { + break; + } + } + } elseif ($finalDocComment !== false) { + // There's no existing doc-comment. Just add ours above the class. + // NOTE: This only does something if we had a new comment to insert, + // which we SHOULD have since we came this far in this scenario... + $startLines[] = $finalDocComment.$this->_nl; + } + + // Generate the new file contents. + $newFileContent = implode($startLines).implode($endLines); + unset($startLines); + unset($endLines); + + // Perform an atomic file-write to disk, which ensures that we will + // never be able to corrupt the class-files on disk via partial writes. + $written = Utilities::atomicWrite($classFileName, $newFileContent); + if ($written !== false) { + echo '> Wrote updated class documentation to disk: "'.$classFileName.'".'.PHP_EOL; + + return true; + } else { + fwrite(STDERR, '> Unable to write new class documentation to disk: "'.$classFileName.'".'.PHP_EOL); + + return false; + } + } + + /** + * Extracts all relevant lines from a doc-comment. + * + * @param string $currentDocComment + * + * @return array + */ + private function _extractRelevantLines( + $currentDocComment) + { + if (!is_string($currentDocComment)) { + return []; + } + + // Remove the leading and trailing doc-comment tags (/** and */). + $currentDocComment = preg_replace('/(^\s*\/\*\*\s*|\s*\*\/$)/u', '', $currentDocComment); + + // Process all lines. Skip all @method and @property lines. + $relevantLines = []; + $lines = preg_split('/\r?\n|\r/u', $currentDocComment); + foreach ($lines as $line) { + // Remove leading and trailing whitespace, and leading asterisks. + $line = trim(preg_replace('/^\s*\*+/u', '', $line)); + + // Skip this line if it's a @method or @property line. + // NOTE: Removing them is totally safe, because the LazyJsonMapper + // class has marked all of its magic property/function handlers as + // final, which means that people's subclasses CANNOT override them + // to add their own magic methods/properties. So therefore we KNOW + // that ALL existing @method/@property class doc lines belong to us! + if (preg_match('/^@(?:method|property)/u', $line)) { + continue; + } + + $relevantLines[] = $line; + } + + // Remove trailing empty lines from the relevant lines. + for ($i = count($relevantLines) - 1; $i >= 0; --$i) { + if ($relevantLines[$i] === '') { + unset($relevantLines[$i]); + } else { + break; + } + } + + // Remove leading empty lines from the relevant lines. + foreach ($relevantLines as $k => $v) { + if ($v !== '') { + break; + } + + unset($relevantLines[$k]); + } + + // Return a re-indexed (properly 0-indexed) array. + return array_values($relevantLines); + } + + /** + * Generate PHPdoc lines for all magic properties and functions. + * + * @throws ReflectionException + * @throws LazyJsonMapperException + * + * @return array + */ + private function _generateMagicDocs() + { + // Check whether we should (and can) document properties and functions. + $documentProperties = $this->_options['properties'] && $this->_reflector->getConstant('ALLOW_VIRTUAL_PROPERTIES'); + $documentFunctions = $this->_options['functions'] && $this->_reflector->getConstant('ALLOW_VIRTUAL_FUNCTIONS'); + if (!$documentProperties && !$documentFunctions) { + return []; + } + + // Export all JSON properties, with RELATIVE class-paths when possible. + // NOTE: We will document ALL properties. Even ones inherited from + // parents/imported maps. This ensures that users who are manually + // reading the source code can see EVERYTHING without needing an IDE. + $properties = []; + $ownerClassName = $this->_reflector->getName(); + foreach ($this->_compiledPropertyMapLink as $propName => $propDef) { + $properties[$propName] = new PropertyDescription( // Throws. + $ownerClassName, + $propName, + $propDef, + true // Use relative class-paths when possible. + ); + } + + // Build the magic documentation... + $magicDocLines = []; + foreach (['functions', 'properties'] as $docType) { + if (($docType === 'functions' && !$documentFunctions) + || ($docType === 'properties' && !$documentProperties)) { + continue; + } + + // Generate all lines for the current magic tag type... + $lineStorage = []; + foreach ($properties as $property) { + if ($docType === 'functions') { + // We will only document useful functions (not the "has", + // since those are useless for properties that are fully + // defined in the class map). + foreach (['get', 'set', 'is', 'unset'] as $funcType) { + // Generate the function name, ie "getSomething", and + // skip this function if it's already defined as a REAL + // (overridden) function in this class or its parents. + $functionName = $funcType.$property->func_case; + if (!$this->_options['document-overridden'] && $this->_reflector->hasMethod($functionName)) { + continue; + } + + // Alright, the function doesn't exist as a real class + // function, or the user wants to document it anyway... + // Document it via its calculated signature. + // NOTE: Classtypes use paths relative to current class! + $functionSignature = $property->{'function_'.$funcType}; + $lineStorage[$functionName] = sprintf('@method %s', $functionSignature); + } + } elseif ($docType === 'properties') { + // Skip this property if it's already defined as a REAL + // (overridden) property in this class or its parents. + if (!$this->_options['document-overridden'] && $this->_reflector->hasProperty($property->name)) { + continue; + } + + // Alright, the property doesn't exist as a real class + // property, or the user wants to document it anyway... + // Document it via its calculated signature. + // NOTE: Classtypes use paths relative to current class! + $lineStorage[$property->name] = sprintf( + '@property %s $%s', + $property->type, + $property->name + ); + } + } + + // Skip this tag type if there was nothing to document... + if (empty($lineStorage)) { + continue; + } + + // Insert empty line separators between different magic tag types. + if (!empty($magicDocLines)) { + $magicDocLines[] = ''; + } + + // Reorder lines by name and add them to the magic doc lines. + // NOTE: We use case sensitivity so that "getComments" and + // "getCommentThreads" etc aren't placed next to each other. + ksort($lineStorage, SORT_NATURAL); // Case-sensitive natural order. + $magicDocLines = array_merge($magicDocLines, array_values($lineStorage)); + } + + return $magicDocLines; + } +} + +// Now process all PHP files under all of the project's namespace folders. +foreach ($namespaces as $realpath => $namespace) { + echo PHP_EOL.'Processing namespace "'.$namespace.'".'.PHP_EOL.'- Path: "'.$realpath.'".'.PHP_EOL; + $realpathlen = strlen($realpath); + + $iterator = new RegexIterator( + new RecursiveIteratorIterator(new RecursiveDirectoryIterator($realpath)), + '/\.php$/i', RecursiveRegexIterator::GET_MATCH + ); + foreach ($iterator as $file => $ext) { + // Determine the real path to the file (compatible with $realpath). + $realfile = realpath($file); + if ($realfile === false) { + fwrite(STDERR, 'Unable to determine real path to file "'.$file.'".'.PHP_EOL); + exit(1); + } + + // Now ensure that the file starts with the expected path... + if (strncmp($realpath, $realfile, $realpathlen) !== 0) { + fwrite(STDERR, 'Unexpected path to file "'.$realfile.'". Does not match project path.'.PHP_EOL); + exit(1); + } + $class = substr($realfile, $realpathlen); + + // Remove the leading slash for the folder... + if ($class[0] !== '/' && $class[0] !== '\\') { + fwrite(STDERR, 'Unexpected path to file "'.$realfile.'". Does not match project path.'.PHP_EOL); + exit(1); + } + $class = substr($class, 1); + + // And now just generate the final class name... + $class = sprintf( + '%s%s', + $namespace, + str_replace('/', '\\', preg_replace('/\.php$/ui', '', $class)) + ); + + // Some files may not contain classes. For example, some people have + // functions.php files with functions, etc. So before we proceed, just + // ensure that the generated class name actually exists. + // NOTE: class_exists() ignores interfaces. Only finds classes. Good. + if (!class_exists($class, true)) { // TRUE = Autoload. + continue; + } + + // Now process the current class. + $documentor = new LazyClassDocumentor($class, $options); + $result = $documentor->process(); + if (!$result) { + if ($options['validate-only']) { + fwrite(STDERR, '> One or more files need updated class documentation or contain other errors.'.PHP_EOL); + } else { + fwrite(STDERR, '> Error while processing class "'.$class.'". Aborting...'.PHP_EOL); + } + exit(1); + } + } +} diff --git a/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/composer.json b/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/composer.json new file mode 100755 index 0000000..3b21d54 --- /dev/null +++ b/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/composer.json @@ -0,0 +1,48 @@ +{ + "name": "lazyjsonmapper/lazyjsonmapper", + "type": "library", + "description": "Advanced, intelligent & automatic object-oriented JSON containers for PHP.", + "keywords": ["json", "development"], + "homepage": "https://github.com/SteveJobzniak/LazyJsonMapper", + "license": "Apache-2.0", + "authors": [ + { + "name": "SteveJobzniak", + "homepage": "https://github.com/SteveJobzniak", + "role": "Developer" + } + ], + "require": { + "php": ">=5.6", + "corneltek/getoptionkit": "2.*" + }, + "require-dev": { + "phpunit/phpunit": "6.*", + "friendsofphp/php-cs-fixer": "^2.7.1" + }, + "bin": [ + "bin/lazydoctor" + ], + "autoload": { + "psr-4": { + "LazyJsonMapper\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "LazyJsonMapper\\Tests\\": "tests/" + } + }, + "scripts": { + "codestyle": [ + "php-cs-fixer fix --config=.php_cs.dist --allow-risky yes", + "php devtools/checkStyle.php x" + ], + "generatedocs": [ + "rm -rf docs/output/ && phpdoc" + ], + "generatefreshdocs": [ + "rm -rf docs/ && phpdoc" + ] + } +} diff --git a/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/devtools/checkStyle.php b/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/devtools/checkStyle.php new file mode 100755 index 0000000..5a0fc6c --- /dev/null +++ b/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/devtools/checkStyle.php @@ -0,0 +1,225 @@ +run(); +if (!empty($badFiles)) { + // Exit with non-zero code to signal that there are problems. + exit(1); +} + +/** + * Code style checker. + * + * @copyright 2017 The LazyJsonMapper Project + * @license http://www.apache.org/licenses/LICENSE-2.0 + * @author SteveJobzniak (https://github.com/SteveJobzniak) + */ +class styleChecker +{ + /** + * @var string + */ + private $_baseDir; + + /** + * @var string[] + */ + private $_inspectFolders; + + /** + * @var bool + */ + private $_onlyShowInvalidFiles; + + /** + * Constructor. + * + * @param string $baseDir + * @param string[] $inspectFolders + * @param bool $onlyShowInvalidFiles + */ + public function __construct( + $baseDir, + array $inspectFolders, + $onlyShowInvalidFiles) + { + $this->_baseDir = realpath($baseDir); + if ($this->_baseDir === false) { + throw new InvalidArgumentException(sprintf('"%s" is not a valid path.', $baseDir)); + } + $this->_inspectFolders = $inspectFolders; + $this->_onlyShowInvalidFiles = $onlyShowInvalidFiles; + } + + /** + * Process single file. + * + * @param string $filePath + * + * @return bool TRUE if the file has codestyle problems, otherwise FALSE. + */ + private function _processFile( + $filePath) + { + $hasProblems = false; + $hasVisibilityProblems = false; + $fileName = basename($filePath); + $inputLines = @file($filePath); + $outputLines = []; + + if ($inputLines === false) { + // We were unable to read the input file. Ignore if broken symlink. + if (is_link($filePath)) { + return false; // File is okay, since the symlink is invalid. + } else { + echo "- {$filePath}: UNABLE TO READ FILE!".PHP_EOL; + + return true; // File has problems... + } + } + + foreach ($inputLines as $line) { + // Function arguments on separate lines. + if (preg_match('/^(.*?(?:(?:final|static)\s+)*(?:public|private|protected)(?:\s+(?:final|static))*\s+function\s+.+?)\((.+)\)(.*)$/', $line, $matches)) { + $hasProblems = true; + + $funcstart = $matches[1]; + $params = $matches[2]; + $trail = $matches[3]; + $params = explode(', ', $params); + + $outputLines[] = $funcstart.'('.PHP_EOL; + for ($i = 0, $len = count($params); $i < $len; ++$i) { + $newline = ' '.$params[$i]; + if ($i == ($len - 1)) { + $newline .= ')'.PHP_EOL; + } else { + $newline .= ','.PHP_EOL; + } + $outputLines[] = $newline; + } + // } else { + // $outputLines[] = $line; + } + + // Appropriate public, private and protected member prefixes. + if (preg_match('/^\s*(?:(?:final|static)\s+)*(public|private|protected)(?:\s+(?:final|static))*\s+(function|\$)\s*&?([^;\(\s]+)/', $line, $matches)) { + $visibility = &$matches[1]; // public, private, protected + $type = &$matches[2]; // $, function + $name = &$matches[3]; // Member name + + if ($visibility == 'public') { + if ($name[0] == '_' && ( + $name != '__construct' + && $name != '__destruct' + && $name != '__call' + && $name != '__get' + && $name != '__set' + && $name != '__isset' + && $name != '__unset' + && $name != '__invoke' + && $name != '__toString' + )) { + $hasProblems = true; + $hasVisibilityProblems = true; + echo "- {$filePath}: BAD PUBLIC NAME:".trim($matches[0]).PHP_EOL; + } + } else { // private, protected + if ($name[0] != '_') { + $hasProblems = true; + $hasVisibilityProblems = true; + echo "- {$filePath}: BAD PRIVATE/PROTECTED NAME:".trim($matches[0]).PHP_EOL; + } + } + } + } + + $newFile = implode('', $outputLines); + if (!$hasProblems) { + if (!$this->_onlyShowInvalidFiles) { + echo " {$filePath}: Already formatted correctly.\n"; + } + } elseif (!$hasVisibilityProblems) { + // Has problems, but no visibility problems. Output fixed file. + echo "- {$filePath}: Has function parameter problems:\n"; + echo $newFile; + } else { + echo "- {$filePath}: Had member visibility problems.\n"; + } + + return $hasProblems; + } + + /** + * Process all *.php files in given path. + * + * @return string[] An array with all files that have codestyle problems. + */ + public function run() + { + $filesWithProblems = []; + foreach ($this->_inspectFolders as $inspectFolder) { + $directoryIterator = new RecursiveDirectoryIterator($this->_baseDir.'/'.$inspectFolder); + $recursiveIterator = new RecursiveIteratorIterator($directoryIterator); + $phpIterator = new RegexIterator($recursiveIterator, '/^.+\.php$/i', RecursiveRegexIterator::GET_MATCH); + + foreach ($phpIterator as $filePath => $dummy) { + $hasProblems = $this->_processFile($filePath); + if ($hasProblems) { + $filesWithProblems[] = $filePath; + } + } + } + + return $filesWithProblems; + } +} diff --git a/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/devtools/funcListData.serialized b/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/devtools/funcListData.serialized new file mode 100755 index 0000000..589e5be --- /dev/null +++ b/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/devtools/funcListData.serialized @@ -0,0 +1 @@ +a:10000:{i:0;s:3:"Pol";i:1;s:3:"Unp";i:2;s:8:"DeDiveSt";i:3;s:2:"Un";i:4;s:5:"Oyste";i:5;s:5:"Postm";i:6;s:2:"Sa";i:7;s:10:"SorroWaddl";i:8;s:6:"ApoCys";i:9;s:10:"GenRevisTe";i:10;s:11:"FarcGenoGoy";i:11;s:3:"Cro";i:12;s:12:"BasDefoHorse";i:13;s:6:"PsycSu";i:14;s:8:"DemPresa";i:15;s:4:"PeSc";i:16;s:2:"Va";i:17;s:6:"AnsKaz";i:18;s:3:"Ove";i:19;s:10:"HanovRaoul";i:20;s:3:"Twi";i:21;s:13:"NonsTortWhapu";i:22;s:5:"Areco";i:23;s:13:"BacilMaladUna";i:24;s:5:"Unflu";i:25;s:13:"CheyHarmSands";i:26;s:11:"OtheOvUltra";i:27;s:2:"Lo";i:28;s:4:"Shos";i:29;s:5:"Scler";i:30;s:5:"Predi";i:31;s:4:"Unre";i:32;s:4:"Punj";i:33;s:9:"HosanUnig";i:34;s:7:"BetaWis";i:35;s:13:"ShalVerteWasp";i:36;s:7:"InhSuWi";i:37;s:5:"Pluto";i:38;s:2:"Ph";i:39;s:9:"IrPhResur";i:40;s:10:"UnconVisco";i:41;s:12:"DrMessaSizzl";i:42;s:6:"LaPrea";i:43;s:2:"Zo";i:44;s:4:"Suck";i:45;s:5:"Monop";i:46;s:5:"Pinci";i:47;s:8:"PatroPha";i:48;s:9:"HogPicrTh";i:49;s:4:"Picn";i:50;s:8:"DisasGib";i:51;s:5:"Popul";i:52;s:10:"ExordPseSh";i:53;s:4:"Unpo";i:54;s:8:"HabiTaci";i:55;s:11:"SalsSanStep";i:56;s:11:"ProaeRepUnd";i:57;s:8:"InflLaSu";i:58;s:6:"ReTens";i:59;s:7:"CarnaHy";i:60;s:3:"Tur";i:61;s:6:"NympRe";i:62;s:5:"PacUp";i:63;s:12:"SpangWritZul";i:64;s:5:"LonPa";i:65;s:9:"SanxScrew";i:66;s:4:"KePo";i:67;s:3:"Eno";i:68;s:4:"Rich";i:69;s:9:"PlushThUn";i:70;s:4:"Seid";i:71;s:4:"GaPi";i:72;s:8:"SupeUpwh";i:73;s:4:"Sest";i:74;s:9:"ResoScler";i:75;s:5:"QuRep";i:76;s:8:"SquSuspe";i:77;s:12:"ExtRefunUnwi";i:78;s:4:"FlUn";i:79;s:6:"RenSar";i:80;s:8:"PierrRet";i:81;s:7:"HarNona";i:82;s:8:"PhanPian";i:83;s:11:"MiasPoachPs";i:84;s:5:"Refor";i:85;s:5:"PrUnf";i:86;s:11:"HedeProarRe";i:87;s:13:"SpieTurnUnesc";i:88;s:13:"EmboOgallSpin";i:89;s:11:"MelanUnstWi";i:90;s:10:"FroPutatUn";i:91;s:4:"PaVa";i:92;s:8:"TanisYot";i:93;s:12:"ConDaddFulgu";i:94;s:2:"Pl";i:95;s:11:"HaKerauProt";i:96;s:12:"DecoHinWhigg";i:97;s:4:"OvTh";i:98;s:5:"Gunat";i:99;s:3:"Pot";i:100;s:5:"Virgi";i:101;s:10:"CopoDiGast";i:102;s:10:"DefHexitUn";i:103;s:14:"InertLeckeNaut";i:104;s:13:"IodomUnreeYac";i:105;s:3:"Tul";i:106;s:9:"BailaPrZe";i:107;s:12:"LaminRecoSpi";i:108;s:6:"CoPoly";i:109;s:5:"Irida";i:110;s:3:"Syl";i:111;s:4:"SuVe";i:112;s:13:"GravMiswiTher";i:113;s:4:"Shem";i:114;s:6:"SolToo";i:115;s:5:"Unsta";i:116;s:10:"ForeReTicx";i:117;s:5:"Outra";i:118;s:12:"InteRoripSph";i:119;s:8:"FroTapst";i:120;s:13:"DislMultWhore";i:121;s:9:"CirInPili";i:122;s:3:"Woo";i:123;s:12:"MustePreceRa";i:124;s:12:"PeavPolRootl";i:125;s:6:"KnopSc";i:126;s:10:"TaTormiTub";i:127;s:8:"OvPrefSu";i:128;s:9:"MetTymVen";i:129;s:6:"PreTri";i:130;s:9:"NemSatUni";i:131;s:4:"DiTe";i:132;s:7:"ManSuUn";i:133;s:10:"CoaxFurRes";i:134;s:3:"Yua";i:135;s:4:"Pygo";i:136;s:4:"Indi";i:137;s:4:"Spon";i:138;s:10:"CrumePurch";i:139;s:8:"HemTerri";i:140;s:5:"ProRe";i:141;s:10:"DispuRoyal";i:142;s:6:"SympUn";i:143;s:4:"Nons";i:144;s:5:"Trime";i:145;s:3:"Non";i:146;s:8:"ReproUnd";i:147;s:10:"ExaPhlPrec";i:148;s:4:"Prot";i:149;s:8:"CurasDev";i:150;s:9:"EchiPlica";i:151;s:11:"FairOrbiUna";i:152;s:15:"GriffPutorRepro";i:153;s:11:"CalmOtolSpo";i:154;s:8:"FigPurUn";i:155;s:9:"GlIgInhum";i:156;s:2:"Tr";i:157;s:8:"HauMetaz";i:158;s:2:"On";i:159;s:11:"PluSnapWoor";i:160;s:11:"EuShosUnbre";i:161;s:15:"FilteRocheSemim";i:162;s:9:"ReviRoUns";i:163;s:2:"Ov";i:164;s:10:"HydrJesUnd";i:165;s:10:"SipSortTur";i:166;s:5:"LixSe";i:167;s:5:"NuUra";i:168;s:8:"MelWhisp";i:169;s:9:"DiJumpyTr";i:170;s:10:"LaMurUpyok";i:171;s:3:"Pty";i:172;s:4:"Ithy";i:173;s:4:"Bloo";i:174;s:4:"UnUn";i:175;s:5:"EsSwa";i:176;s:9:"NeobOvPar";i:177;s:5:"DemLa";i:178;s:8:"NonvoUnm";i:179;s:9:"MutiRimux";i:180;s:2:"Pr";i:181;s:11:"UninVillaWh";i:182;s:3:"Unl";i:183;s:10:"ProscSownx";i:184;s:11:"HiRescUnint";i:185;s:3:"Cau";i:186;s:13:"HittoHustlSub";i:187;s:8:"JeremSer";i:188;s:10:"DeMetagPet";i:189;s:8:"MisexRig";i:190;s:10:"ChildCogRe";i:191;s:11:"HyPennoRech";i:192;s:3:"Lig";i:193;s:7:"OuUnmem";i:194;s:2:"Re";i:195;s:4:"VoWa";i:196;s:8:"UnicUnju";i:197;s:5:"SecSn";i:198;s:9:"NumSaiUnc";i:199;s:13:"DoubtHeteUnse";i:200;s:4:"Smee";i:201;s:4:"PrTh";i:202;s:7:"MoVindh";i:203;s:8:"GasZodia";i:204;s:2:"Do";i:205;s:12:"UllUnfatWord";i:206;s:7:"IconNov";i:207;s:4:"Juda";i:208;s:9:"GrReptSei";i:209;s:14:"OutpPenelPolan";i:210;s:4:"Poly";i:211;s:2:"No";i:212;s:11:"SchiUnaVera";i:213;s:4:"Ther";i:214;s:9:"EbFlooPes";i:215;s:6:"MoOchi";i:216;s:5:"Triol";i:217;s:2:"Ra";i:218;s:5:"ProTe";i:219;s:10:"EuryoGrade";i:220;s:12:"EntOligPulpy";i:221;s:8:"RepRepSa";i:222;s:7:"NeskPap";i:223;s:3:"Unf";i:224;s:9:"FreMeUntr";i:225;s:7:"PeriUlt";i:226;s:5:"Varyi";i:227;s:8:"CorrPhTo";i:228;s:8:"ProfeSmo";i:229;s:6:"SuUncr";i:230;s:4:"Rurb";i:231;s:3:"Lan";i:232;s:9:"HearSuper";i:233;s:10:"AngulTsuga";i:234;s:10:"PertUnhYer";i:235;s:11:"GrandNitNoc";i:236;s:2:"Le";i:237;s:11:"HypeOverWei";i:238;s:11:"MentSoSumle";i:239;s:6:"LaShip";i:240;s:9:"PrenUnawa";i:241;s:13:"ExceKotaPonti";i:242;s:7:"HaMelod";i:243;s:6:"PaTyUn";i:244;s:2:"Fo";i:245;s:5:"Verse";i:246;s:10:"OverProTuk";i:247;s:9:"SubvTitan";i:248;s:10:"PondeSight";i:249;s:4:"Thar";i:250;s:7:"InRedho";i:251;s:10:"ReSynuThou";i:252;s:5:"Unpaw";i:253;s:2:"We";i:254;s:4:"Peco";i:255;s:8:"PlacaRoz";i:256;s:9:"RecoUnfre";i:257;s:9:"GyHaloHyp";i:258;s:3:"Lit";i:259;s:12:"NapOverUnfor";i:260;s:13:"CrosIntraNumi";i:261;s:11:"CreHandeSup";i:262;s:2:"Sc";i:263;s:10:"MucOzVerte";i:264;s:4:"Prim";i:265;s:9:"HomMonPhi";i:266;s:5:"Unene";i:267;s:2:"Sl";i:268;s:6:"LimRec";i:269;s:5:"Trebl";i:270;s:13:"ForehNetmResp";i:271;s:9:"NonprUnbe";i:272;s:9:"CarbuRaRe";i:273;s:10:"LihyOverSt";i:274;s:10:"CrotaPurpu";i:275;s:4:"Inel";i:276;s:7:"CenMast";i:277;s:9:"SlaxTaTit";i:278;s:11:"SuTrimUncha";i:279;s:2:"Dw";i:280;s:7:"EcaudSy";i:281;s:11:"FormHeteVog";i:282;s:4:"Stra";i:283;s:13:"IncuLushePelv";i:284;s:13:"LuthUnmaWiste";i:285;s:8:"ApnSivve";i:286;s:7:"CouUnme";i:287;s:7:"PsTrepa";i:288;s:4:"Wiss";i:289;s:10:"BorHomelRi";i:290;s:7:"ThUtrVe";i:291;s:2:"Te";i:292;s:8:"SaTeaUnd";i:293;s:6:"CoHuSo";i:294;s:2:"Th";i:295;s:9:"FlObSubco";i:296;s:4:"FlPa";i:297;s:9:"OverSeagh";i:298;s:7:"LympYaw";i:299;s:7:"IrreJac";i:300;s:10:"TholUnhUnw";i:301;s:4:"DeEl";i:302;s:9:"MervePrTi";i:303;s:8:"NatPrefa";i:304;s:7:"HumInco";i:305;s:7:"RiRibby";i:306;s:8:"BalosPer";i:307;s:8:"BepluDet";i:308;s:8:"EulogRed";i:309;s:8:"HydroVar";i:310;s:6:"OvePse";i:311;s:4:"FuPa";i:312;s:2:"Da";i:313;s:6:"MaSeSh";i:314;s:7:"PanisPr";i:315;s:4:"Pudi";i:316;s:2:"Su";i:317;s:7:"HyMeSqu";i:318;s:7:"MacPlPu";i:319;s:5:"PtiWo";i:320;s:2:"Ja";i:321;s:9:"FoShutTar";i:322;s:15:"HypoiRoughTrilo";i:323;s:10:"MePhylProc";i:324;s:13:"ExpIncreSugge";i:325;s:7:"PhyllTh";i:326;s:4:"Knoc";i:327;s:12:"CrTilloUnder";i:328;s:5:"CaPen";i:329;s:8:"ItaRequi";i:330;s:10:"NitReaTrop";i:331;s:4:"Kelp";i:332;s:7:"TelepVa";i:333;s:10:"MultiTriph";i:334;s:4:"Synu";i:335;s:13:"PremRobenUnre";i:336;s:6:"OveUnp";i:337;s:5:"DraSu";i:338;s:8:"FluProar";i:339;s:5:"UnjWi";i:340;s:4:"Tume";i:341;s:5:"ApEat";i:342;s:12:"BerEsopHippo";i:343;s:4:"Weig";i:344;s:7:"GloSupe";i:345;s:5:"Morib";i:346;s:7:"HaOzoRu";i:347;s:5:"EfMys";i:348;s:7:"BuoSpot";i:349;s:5:"Unhop";i:350;s:8:"IntNaiXa";i:351;s:8:"ReSerpVi";i:352;s:7:"RaUnVer";i:353;s:6:"CuEndl";i:354;s:8:"PopiWood";i:355;s:8:"LionPrSa";i:356;s:10:"SuppUneWre";i:357;s:5:"Vaing";i:358;s:4:"Pale";i:359;s:7:"OveSpha";i:360;s:7:"MiRossx";i:361;s:4:"High";i:362;s:9:"PlaWhZama";i:363;s:11:"GalacMicPan";i:364;s:5:"Tonop";i:365;s:4:"Hera";i:366;s:8:"DermPrUn";i:367;s:5:"Branc";i:368;s:10:"StchUpVulg";i:369;s:12:"OverPecSerol";i:370;s:5:"QuaRe";i:371;s:13:"SubiSyllaUltr";i:372;s:3:"Tar";i:373;s:8:"RigwiSqu";i:374;s:4:"Wabb";i:375;s:12:"LotifSaZooto";i:376;s:5:"Strat";i:377;s:7:"DisDiLa";i:378;s:3:"Una";i:379;s:10:"LandfMesSu";i:380;s:7:"PeavRol";i:381;s:13:"GrudgPerTeuto";i:382;s:12:"ChiCrypUnflo";i:383;s:5:"Front";i:384;s:4:"Erud";i:385;s:7:"PenSqua";i:386;s:11:"BeyonCheMal";i:387;s:3:"Sli";i:388;s:4:"Uter";i:389;s:8:"CloaPenc";i:390;s:8:"PoShiUnp";i:391;s:5:"Deami";i:392;s:9:"LeucMelli";i:393;s:6:"PresQu";i:394;s:6:"HepUnr";i:395;s:9:"VioloVisc";i:396;s:10:"SeTolyWate";i:397;s:5:"Lestr";i:398;s:4:"Wood";i:399;s:10:"IntOddmeTr";i:400;s:10:"PerioRotul";i:401;s:4:"Stol";i:402;s:11:"EntomSouSyn";i:403;s:4:"Semi";i:404;s:6:"DurrUn";i:405;s:7:"DiscoOv";i:406;s:9:"PangiProl";i:407;s:9:"NapRecSqu";i:408;s:3:"Unr";i:409;s:13:"GamesTetanUns";i:410;s:8:"FalMetRa";i:411;s:14:"BataDenouSaxon";i:412;s:5:"Solvo";i:413;s:14:"ScytaTighUnjoy";i:414;s:4:"Glut";i:415;s:4:"EkFi";i:416;s:6:"ErNoPa";i:417;s:6:"LaSpal";i:418;s:11:"JoRackRepat";i:419;s:10:"ReSorreUnd";i:420;s:12:"MisenRiTextu";i:421;s:4:"Wayb";i:422;s:6:"CampQu";i:423;s:11:"BrNapolSulp";i:424;s:4:"Snow";i:425;s:2:"Mo";i:426;s:5:"Tehua";i:427;s:13:"PastPressUnwi";i:428;s:5:"Entom";i:429;s:4:"SoTr";i:430;s:4:"Soft";i:431;s:10:"ChElephUnd";i:432;s:3:"Whi";i:433;s:6:"UpkZoo";i:434;s:5:"PaPre";i:435;s:7:"LobxVal";i:436;s:2:"Di";i:437;s:7:"NarNonp";i:438;s:9:"HorUnexVe";i:439;s:5:"Summe";i:440;s:10:"CouLocoPli";i:441;s:11:"ReSnortUnas";i:442;s:7:"HeKnPre";i:443;s:5:"Shavi";i:444;s:2:"Fi";i:445;s:7:"KiOstra";i:446;s:6:"HeliJu";i:447;s:7:"MasdeNo";i:448;s:5:"Tapst";i:449;s:5:"NazVe";i:450;s:4:"Sque";i:451;s:6:"FrIcht";i:452;s:3:"Hyd";i:453;s:8:"InirLevi";i:454;s:7:"ElasIna";i:455;s:3:"Inc";i:456;s:8:"UnfonUns";i:457;s:10:"PeuceProma";i:458;s:10:"ParomPeSom";i:459;s:7:"ImpOuUn";i:460;s:7:"PeiseUn";i:461;s:3:"Tri";i:462;s:6:"IntrWa";i:463;s:13:"CapryRemigStr";i:464;s:5:"FulPr";i:465;s:9:"MuncRhomb";i:466;s:14:"LiteNaphtSarco";i:467;s:7:"TelecTr";i:468;s:8:"EngaMePr";i:469;s:5:"Minco";i:470;s:4:"HaMa";i:471;s:5:"Skitt";i:472;s:12:"MidWharWonde";i:473;s:5:"ArSto";i:474;s:5:"OrbPi";i:475;s:5:"Sulfo";i:476;s:11:"PeVoluWhalp";i:477;s:7:"MusToas";i:478;s:3:"Spi";i:479;s:6:"PhilSu";i:480;s:3:"Foc";i:481;s:11:"MansNardxPo";i:482;s:11:"RongaTheUnc";i:483;s:12:"ParsSemiSynt";i:484;s:5:"SuToc";i:485;s:7:"OsiSnib";i:486;s:3:"Dee";i:487;s:5:"Throa";i:488;s:12:"HydroReZealo";i:489;s:9:"MetapUnha";i:490;s:10:"HeHoastPen";i:491;s:3:"Het";i:492;s:4:"CaCo";i:493;s:7:"LimitPr";i:494;s:2:"Ho";i:495;s:8:"RodlVive";i:496;s:8:"UpsWatWe";i:497;s:6:"PerSin";i:498;s:4:"Righ";i:499;s:11:"OstraReUnde";i:500;s:5:"ScSko";i:501;s:3:"End";i:502;s:6:"NimPle";i:503;s:12:"QuadrUndoVet";i:504;s:6:"EkMeMu";i:505;s:12:"DefLacquTrad";i:506;s:7:"PrUndWa";i:507;s:12:"ProtoStroUnf";i:508;s:6:"ReTheo";i:509;s:3:"Pre";i:510;s:6:"OvUnfo";i:511;s:3:"Loi";i:512;s:5:"Cultu";i:513;s:5:"Fabul";i:514;s:4:"Succ";i:515;s:8:"MegaRegr";i:516;s:3:"Rur";i:517;s:3:"Ter";i:518;s:10:"ResisUnire";i:519;s:10:"HeloOveSem";i:520;s:12:"NatrSavoScaw";i:521;s:9:"MusoNeuri";i:522;s:6:"ChClia";i:523;s:5:"ParPs";i:524;s:11:"LabxMetaUnd";i:525;s:10:"SilkStuVux";i:526;s:12:"BroaScoSpouc";i:527;s:6:"PaUnde";i:528;s:10:"HovePanoRe";i:529;s:9:"InsulTric";i:530;s:9:"ExtForUnm";i:531;s:6:"FlatUn";i:532;s:3:"Var";i:533;s:11:"HypoeRevSup";i:534;s:14:"StasSubitUncry";i:535;s:13:"BattoStewSupe";i:536;s:6:"SneThe";i:537;s:6:"ConnSi";i:538;s:2:"Po";i:539;s:7:"BraQuid";i:540;s:11:"StomaUnVana";i:541;s:4:"TrUn";i:542;s:15:"SicyoStinkUnspe";i:543;s:10:"InManOcean";i:544;s:8:"IndLymPr";i:545;s:7:"OsseoVa";i:546;s:10:"HeteHyUnsu";i:547;s:6:"TricUn";i:548;s:7:"PlSeTal";i:549;s:7:"DyEpsom";i:550;s:3:"Stu";i:551;s:3:"Nem";i:552;s:5:"Spina";i:553;s:13:"EusFowlfOleag";i:554;s:8:"HeRadiSt";i:555;s:7:"NonjPar";i:556;s:8:"ChelaDai";i:557;s:10:"CommePeSem";i:558;s:9:"EhxMisOve";i:559;s:6:"FoUret";i:560;s:2:"Pi";i:561;s:4:"Inte";i:562;s:4:"GoUn";i:563;s:7:"PhilaSe";i:564;s:10:"ReaUnprVas";i:565;s:14:"ClapHuzzaWalli";i:566;s:7:"LaconTy";i:567;s:8:"EagThioc";i:568;s:3:"Uns";i:569;s:7:"DingaSe";i:570;s:2:"Ve";i:571;s:11:"ParaPyophTu";i:572;s:10:"KasbaOsteo";i:573;s:4:"Tang";i:574;s:8:"StiStrTo";i:575;s:12:"MiSubdrTurbu";i:576;s:11:"PostPrPreal";i:577;s:9:"EquHypoNi";i:578;s:9:"HiIndZeug";i:579;s:4:"Scat";i:580;s:14:"MesoSloomSpasm";i:581;s:10:"ProcoUnyie";i:582;s:4:"Nonr";i:583;s:6:"CompCo";i:584;s:4:"Kymo";i:585;s:12:"NuanPolygSel";i:586;s:11:"LeafaLegZin";i:587;s:8:"RhomVent";i:588;s:9:"PeteRaSul";i:589;s:12:"DesSemblUret";i:590;s:4:"Zoog";i:591;s:8:"ExamiPre";i:592;s:10:"AmpManoRet";i:593;s:4:"Same";i:594;s:2:"Ty";i:595;s:3:"Spr";i:596;s:4:"Eels";i:597;s:5:"Motor";i:598;s:9:"ScyphUnli";i:599;s:12:"DefGanoUnalo";i:600;s:7:"EpEstRo";i:601;s:4:"Pana";i:602;s:10:"CanaEnlSca";i:603;s:9:"HomRoamRo";i:604;s:7:"HanKeWh";i:605;s:5:"DeIch";i:606;s:8:"CapInSto";i:607;s:7:"PenneVo";i:608;s:11:"ElatTardiVi";i:609;s:7:"ManUnvi";i:610;s:10:"OutbPhPrem";i:611;s:11:"AttrBarDros";i:612;s:8:"InofUrod";i:613;s:3:"Oli";i:614;s:11:"EncImpSchai";i:615;s:4:"Palm";i:616;s:3:"Vit";i:617;s:10:"OphthPyret";i:618;s:2:"So";i:619;s:6:"ExacPr";i:620;s:7:"MonPrTr";i:621;s:12:"MaraMystUnam";i:622;s:4:"Vela";i:623;s:6:"PoSupe";i:624;s:10:"CathCoaThu";i:625;s:5:"ToUpc";i:626;s:3:"Toc";i:627;s:9:"GrePraSli";i:628;s:4:"Sams";i:629;s:8:"ChaHoMon";i:630;s:2:"My";i:631;s:7:"HypItin";i:632;s:11:"GloKoxScrib";i:633;s:9:"SalaUnpro";i:634;s:8:"ImpacSan";i:635;s:3:"Rep";i:636;s:9:"ParaPuggi";i:637;s:8:"RebSheUn";i:638;s:9:"MaObRedis";i:639;s:9:"GuStomTir";i:640;s:4:"Isos";i:641;s:9:"SopoUnUnr";i:642;s:8:"PedroUnd";i:643;s:4:"Ward";i:644;s:3:"Val";i:645;s:4:"Plat";i:646;s:6:"RotUns";i:647;s:7:"PermiRe";i:648;s:6:"ConvSc";i:649;s:4:"Knic";i:650;s:7:"HemImme";i:651;s:10:"HandOutSti";i:652;s:12:"DioEpirrNesi";i:653;s:5:"Yodel";i:654;s:11:"BrangOdUnem";i:655;s:4:"Rebr";i:656;s:3:"Sys";i:657;s:10:"PeRegrTong";i:658;s:8:"GlResiUn";i:659;s:10:"NeapxOsteo";i:660;s:9:"LappPsRev";i:661;s:12:"PrePromTrica";i:662;s:5:"Prota";i:663;s:6:"TablUr";i:664;s:9:"CimbMenta";i:665;s:12:"DesHoaxxUnca";i:666;s:10:"LithoSteat";i:667;s:4:"Goyx";i:668;s:13:"PignoPrehSigx";i:669;s:2:"St";i:670;s:13:"BacDigreMontr";i:671;s:7:"TraUnro";i:672;s:7:"HikRiva";i:673;s:7:"CoPeUnp";i:674;s:5:"Thoro";i:675;s:5:"Vibro";i:676;s:7:"HoRudim";i:677;s:5:"Satis";i:678;s:14:"SulkiSyndTugri";i:679;s:8:"AnEpSius";i:680;s:7:"MyPomWe";i:681;s:11:"PathPepsiPl";i:682;s:7:"PerPlut";i:683;s:8:"MorPrete";i:684;s:8:"PiProUnf";i:685;s:7:"SleUnem";i:686;s:10:"BimPolypVa";i:687;s:6:"DzeThi";i:688;s:6:"DolEmp";i:689;s:5:"Skibb";i:690;s:3:"Pen";i:691;s:12:"SpinSymWaysi";i:692;s:9:"CamHomoTe";i:693;s:7:"HemopRa";i:694;s:13:"ConsParsoReri";i:695;s:7:"HumorKh";i:696;s:3:"Meg";i:697;s:9:"HuckInval";i:698;s:5:"CoSto";i:699;s:5:"Wykeh";i:700;s:8:"PeUnscUn";i:701;s:13:"LaminLeisOvov";i:702;s:4:"Unco";i:703;s:5:"HiUnd";i:704;s:9:"ForMeUnri";i:705;s:6:"RhymUn";i:706;s:8:"UnderUrn";i:707;s:13:"QuadrRuntTort";i:708;s:9:"HemiRepur";i:709;s:9:"EuPiSemin";i:710;s:6:"SubcSu";i:711;s:9:"MorNitcSl";i:712;s:10:"PreteVitel";i:713;s:7:"ReducTr";i:714;s:7:"NonSkew";i:715;s:5:"Rejec";i:716;s:6:"ImNuTe";i:717;s:10:"FlaForeHyp";i:718;s:5:"Stech";i:719;s:11:"CypHasteSpo";i:720;s:5:"Ocell";i:721;s:10:"ObtaiThrim";i:722;s:14:"DihyPhenaSmili";i:723;s:14:"ForwaPeripSius";i:724;s:15:"DropcOlivaPippe";i:725;s:10:"SubbeWhite";i:726;s:5:"Pneum";i:727;s:8:"OpprRaga";i:728;s:5:"DesDi";i:729;s:8:"HelioSup";i:730;s:10:"ParaPiSauc";i:731;s:3:"Sor";i:732;s:6:"SeptSu";i:733;s:5:"DrPot";i:734;s:8:"CoOutWau";i:735;s:6:"CeWold";i:736;s:8:"VivenWhi";i:737;s:10:"BandiQuadr";i:738;s:13:"MesiPlanSunni";i:739;s:3:"Urs";i:740;s:10:"KiUnchUnes";i:741;s:12:"MynaxSeTroch";i:742;s:4:"Shut";i:743;s:7:"ObSpurg";i:744;s:3:"Boy";i:745;s:10:"GuitaUnsub";i:746;s:4:"GrPr";i:747;s:11:"MonStrumSus";i:748;s:9:"DiSenTurr";i:749;s:4:"Unop";i:750;s:10:"CansoPrion";i:751;s:12:"HieMesaParoa";i:752;s:3:"Sil";i:753;s:8:"OpPoScio";i:754;s:9:"TrumpTwUn";i:755;s:14:"DoomsEtymoUnch";i:756;s:5:"HypUn";i:757;s:11:"InterMetOoz";i:758;s:9:"NondOcSta";i:759;s:9:"OinOtorRe";i:760;s:11:"IndiJapaUnd";i:761;s:8:"SnufUnsc";i:762;s:2:"Gr";i:763;s:10:"OpePupilUn";i:764;s:12:"IrreLiqueTra";i:765;s:5:"Uncol";i:766;s:4:"MeVe";i:767;s:6:"BlepVa";i:768;s:8:"PreSubva";i:769;s:6:"MaPrep";i:770;s:7:"PuUnnat";i:771;s:6:"JuriKh";i:772;s:9:"CoprGastr";i:773;s:9:"EuergOvSi";i:774;s:5:"Hyper";i:775;s:9:"StoSulpUn";i:776;s:5:"Unfop";i:777;s:4:"Retr";i:778;s:8:"BunchPos";i:779;s:5:"NorSu";i:780;s:12:"HordRunnUnea";i:781;s:7:"InterLi";i:782;s:11:"EnvOdonStew";i:783;s:10:"DiaMesNonc";i:784;s:8:"HematThe";i:785;s:3:"Ski";i:786;s:5:"Kodak";i:787;s:9:"DoRedouRe";i:788;s:2:"Sp";i:789;s:5:"Unfil";i:790;s:9:"RubicWarb";i:791;s:9:"HemiUinta";i:792;s:7:"OulSupr";i:793;s:10:"HieMuPelop";i:794;s:3:"Dis";i:795;s:5:"Telot";i:796;s:3:"Cza";i:797;s:5:"KidLa";i:798;s:4:"Nase";i:799;s:3:"Sul";i:800;s:8:"CubbyRed";i:801;s:8:"OrPectTo";i:802;s:7:"SeSvaUp";i:803;s:6:"SerWit";i:804;s:3:"Spo";i:805;s:10:"MeProbSmyr";i:806;s:5:"Tunab";i:807;s:9:"HolInVene";i:808;s:10:"ClaInlieRe";i:809;s:4:"Carb";i:810;s:13:"ReseSteeUncon";i:811;s:9:"UnbarWhit";i:812;s:11:"OstPhthiStu";i:813;s:5:"Whenn";i:814;s:4:"Ungl";i:815;s:3:"Sch";i:816;s:9:"PictuReco";i:817;s:10:"LyPremoTyp";i:818;s:7:"PagStal";i:819;s:5:"Sogdi";i:820;s:5:"UnUno";i:821;s:5:"Anast";i:822;s:11:"IsocMasOtos";i:823;s:7:"RayReli";i:824;s:4:"Umbe";i:825;s:7:"CortMil";i:826;s:8:"ProSaddl";i:827;s:6:"LigWob";i:828;s:6:"ReSawi";i:829;s:3:"Inw";i:830;s:5:"Recre";i:831;s:7:"PantaPs";i:832;s:7:"LuNoUna";i:833;s:9:"FeliPoTar";i:834;s:13:"RajaxSoweWhor";i:835;s:6:"HeHorn";i:836;s:2:"Vi";i:837;s:5:"Sulph";i:838;s:7:"ScalSco";i:839;s:6:"SuSwin";i:840;s:12:"PleTempUnspl";i:841;s:4:"Disl";i:842;s:6:"PuTach";i:843;s:5:"Methy";i:844;s:9:"StThreeWa";i:845;s:8:"MetaPeri";i:846;s:12:"KoMuttoPseud";i:847;s:3:"Yot";i:848;s:8:"UnleaUnt";i:849;s:12:"NoTammyZoosp";i:850;s:8:"TendUnre";i:851;s:11:"ChunSiSlate";i:852;s:11:"ReverTheUni";i:853;s:14:"SynoWhipsYokel";i:854;s:4:"Undi";i:855;s:10:"HoLodgeTum";i:856;s:10:"GoralLaWul";i:857;s:7:"OnPrede";i:858;s:6:"LieSac";i:859;s:9:"BemuGyrin";i:860;s:3:"Ung";i:861;s:4:"Retu";i:862;s:9:"DesipSavi";i:863;s:8:"RouSandi";i:864;s:4:"Prec";i:865;s:10:"GastPrStra";i:866;s:4:"Muli";i:867;s:6:"DeFebr";i:868;s:11:"IcacPoltrUn";i:869;s:5:"Trach";i:870;s:7:"MisadUn";i:871;s:15:"GaywiOverdScorp";i:872;s:11:"ChiliIsSono";i:873;s:4:"Than";i:874;s:4:"Tube";i:875;s:5:"Mermi";i:876;s:10:"KuskUbUnwo";i:877;s:9:"GeHeMicro";i:878;s:2:"Py";i:879;s:2:"Vo";i:880;s:9:"FrThTrans";i:881;s:13:"RipiSubuWorme";i:882;s:11:"AssigCoenOu";i:883;s:4:"DiUn";i:884;s:12:"NonsRotiWhat";i:885;s:5:"NoUni";i:886;s:6:"OmPhle";i:887;s:4:"Para";i:888;s:3:"Rev";i:889;s:4:"ShSt";i:890;s:8:"MahaPunn";i:891;s:7:"StatoUn";i:892;s:10:"GalliNecta";i:893;s:12:"JuMintmWouch";i:894;s:9:"SempeSpir";i:895;s:13:"OphthSemidSty";i:896;s:8:"CenTripe";i:897;s:8:"SipunUne";i:898;s:5:"Uvulo";i:899;s:4:"Verb";i:900;s:8:"CuprMaPa";i:901;s:4:"PhUn";i:902;s:7:"PulTher";i:903;s:5:"Suppo";i:904;s:8:"BombCeGl";i:905;s:11:"CoccPaSerot";i:906;s:6:"CoExTr";i:907;s:9:"PaPatPedi";i:908;s:5:"Sillo";i:909;s:10:"DrysPyreSy";i:910;s:7:"NoVermi";i:911;s:8:"SaddVuln";i:912;s:10:"NonTessUns";i:913;s:8:"HeSemiUn";i:914;s:5:"ReUns";i:915;s:10:"IntePiSiki";i:916;s:6:"BoleCo";i:917;s:6:"DisgSa";i:918;s:3:"Rem";i:919;s:9:"ChroOverd";i:920;s:3:"Unt";i:921;s:7:"HeSands";i:922;s:6:"PrShTi";i:923;s:10:"NecroPrere";i:924;s:10:"OoeciPhUns";i:925;s:3:"Ham";i:926;s:7:"GaReful";i:927;s:7:"BunyPed";i:928;s:13:"IntoParalZigz";i:929;s:5:"Jacks";i:930;s:2:"Wr";i:931;s:6:"SalUnc";i:932;s:9:"GeSlovTic";i:933;s:9:"NeighPain";i:934;s:3:"Und";i:935;s:6:"OutTri";i:936;s:4:"Teii";i:937;s:5:"Taint";i:938;s:5:"Guill";i:939;s:10:"AzonxFaMoa";i:940;s:10:"InterSeUnc";i:941;s:7:"OveSobr";i:942;s:8:"ReTrihUn";i:943;s:7:"CyWordi";i:944;s:9:"TurnaWash";i:945;s:8:"RevoUngy";i:946;s:6:"PhoRer";i:947;s:5:"Whizz";i:948;s:13:"ProUndesUnsal";i:949;s:11:"StitcUpWitx";i:950;s:4:"Pert";i:951;s:3:"Tac";i:952;s:9:"EmulsProu";i:953;s:6:"UnwaUs";i:954;s:8:"FelTaxia";i:955;s:8:"ReResScl";i:956;s:6:"SemUnp";i:957;s:8:"OvTopWhi";i:958;s:8:"IntReaWo";i:959;s:4:"PlTo";i:960;s:14:"OveraPositUnpl";i:961;s:5:"Trest";i:962;s:10:"BixaChloVi";i:963;s:6:"CharGr";i:964;s:7:"ImSemUn";i:965;s:5:"Leadw";i:966;s:4:"Ples";i:967;s:11:"PsStepVirul";i:968;s:8:"NoOoSpir";i:969;s:10:"HeraImWhee";i:970;s:6:"SaTrag";i:971;s:10:"ForweSubre";i:972;s:11:"BegloEffHyd";i:973;s:8:"ChatTido";i:974;s:5:"RiSup";i:975;s:5:"Nonex";i:976;s:10:"JonMaParap";i:977;s:10:"ChapeDovEn";i:978;s:5:"Twizz";i:979;s:7:"MuckPra";i:980;s:8:"HurklLus";i:981;s:10:"MortProVal";i:982;s:9:"FroLimiRe";i:983;s:9:"HoliMimTr";i:984;s:5:"Besca";i:985;s:7:"MetroRu";i:986;s:13:"SawfSemibUngu";i:987;s:9:"ForePicco";i:988;s:9:"ParSysTee";i:989;s:4:"Pres";i:990;s:7:"HeJaile";i:991;s:3:"Ref";i:992;s:6:"MusgRe";i:993;s:3:"Her";i:994;s:4:"Hyet";i:995;s:7:"HelioPo";i:996;s:6:"BasiYa";i:997;s:8:"ProtVell";i:998;s:7:"NonoWea";i:999;s:7:"SubrUnm";i:1000;s:11:"ConEboniVer";i:1001;s:3:"Uni";i:1002;s:9:"IdePlasUn";i:1003;s:9:"PronStabi";i:1004;s:14:"QuinSquabUnrig";i:1005;s:2:"Ob";i:1006;s:7:"NatchOx";i:1007;s:5:"IsXan";i:1008;s:6:"JaReel";i:1009;s:8:"ProRhota";i:1010;s:4:"Vell";i:1011;s:5:"Hemop";i:1012;s:5:"Searc";i:1013;s:8:"EntaUnst";i:1014;s:12:"MonolNoRajax";i:1015;s:13:"DanPolynStere";i:1016;s:13:"TubUnattZogan";i:1017;s:5:"Raini";i:1018;s:5:"SpiSp";i:1019;s:8:"PosolPro";i:1020;s:6:"AlysEc";i:1021;s:10:"PoRedetThe";i:1022;s:10:"RecoiUglil";i:1023;s:10:"AntifCrIns";i:1024;s:3:"Pse";i:1025;s:12:"NonuSemSeria";i:1026;s:12:"PanSourUncom";i:1027;s:7:"FiMatan";i:1028;s:6:"RattVe";i:1029;s:6:"SpaTom";i:1030;s:12:"GebbiPasTess";i:1031;s:9:"PhotUncUn";i:1032;s:6:"RicTar";i:1033;s:10:"PlagiSeque";i:1034;s:3:"Zer";i:1035;s:5:"Denom";i:1036;s:14:"LentiPrecaUnpr";i:1037;s:6:"PathSu";i:1038;s:4:"PuSh";i:1039;s:10:"LaLinoPrem";i:1040;s:11:"IsPteryViru";i:1041;s:6:"HerbLo";i:1042;s:10:"FebKobusOv";i:1043;s:8:"ChiloWai";i:1044;s:12:"PosTetraTong";i:1045;s:5:"StUni";i:1046;s:11:"SuccTriliUn";i:1047;s:12:"PertSymmUnsu";i:1048;s:8:"UnshrZea";i:1049;s:6:"KonMar";i:1050;s:8:"LimTidbi";i:1051;s:6:"IsMePe";i:1052;s:7:"MarWher";i:1053;s:8:"OssifPen";i:1054;s:3:"Nig";i:1055;s:9:"NoRevuWar";i:1056;s:14:"CatalNympUnfal";i:1057;s:4:"Prep";i:1058;s:2:"Pe";i:1059;s:13:"InsciProSulph";i:1060;s:5:"DysHa";i:1061;s:2:"Wa";i:1062;s:5:"Goldb";i:1063;s:8:"DiHaWhey";i:1064;s:5:"SpThr";i:1065;s:8:"MesoUncu";i:1066;s:8:"SaUncoVi";i:1067;s:5:"SemUn";i:1068;s:13:"EmplHumanPitt";i:1069;s:5:"AmpHa";i:1070;s:6:"LoPrec";i:1071;s:9:"OnRegleSn";i:1072;s:8:"CurDesUn";i:1073;s:7:"NasUnex";i:1074;s:9:"ChulPrudy";i:1075;s:7:"SacUnUp";i:1076;s:10:"InMesMisca";i:1077;s:5:"StrVe";i:1078;s:10:"ForHymeTap";i:1079;s:4:"Ruin";i:1080;s:9:"KurbaOxhe";i:1081;s:9:"DamScyTet";i:1082;s:3:"Int";i:1083;s:9:"ClypeHair";i:1084;s:10:"DoNicolSpe";i:1085;s:6:"PraUne";i:1086;s:2:"He";i:1087;s:9:"SigniUnbr";i:1088;s:10:"IntePlaTor";i:1089;s:3:"Kol";i:1090;s:11:"MesRomUnshu";i:1091;s:5:"Calfl";i:1092;s:5:"Pyrol";i:1093;s:5:"Trous";i:1094;s:7:"LanSpew";i:1095;s:12:"SylTyranUnre";i:1096;s:8:"PodRetra";i:1097;s:2:"Ox";i:1098;s:6:"PosTel";i:1099;s:5:"SynWa";i:1100;s:6:"DauUnb";i:1101;s:13:"PseuThorUropt";i:1102;s:13:"ScleThermVirt";i:1103;s:3:"Imp";i:1104;s:4:"Repr";i:1105;s:8:"SacriSub";i:1106;s:6:"GrSolo";i:1107;s:8:"OvPoShad";i:1108;s:11:"ConfParisRe";i:1109;s:8:"CnidoUna";i:1110;s:2:"Gl";i:1111;s:3:"Inf";i:1112;s:11:"OxyneRehaWi";i:1113;s:3:"Cou";i:1114;s:4:"PhSh";i:1115;s:7:"ExcSubt";i:1116;s:7:"PlProto";i:1117;s:8:"HelleWor";i:1118;s:10:"MyOvertThu";i:1119;s:4:"Unsu";i:1120;s:10:"UnUnindWor";i:1121;s:4:"Tabo";i:1122;s:8:"SoSphTuz";i:1123;s:7:"SeptSpi";i:1124;s:10:"IsoOcRedic";i:1125;s:5:"Shane";i:1126;s:8:"HydroSem";i:1127;s:14:"MattyNeossSemi";i:1128;s:9:"MetaPeSac";i:1129;s:5:"Under";i:1130;s:11:"MonopNodiSk";i:1131;s:5:"RecSu";i:1132;s:10:"PensiSnipt";i:1133;s:7:"EnchoKe";i:1134;s:8:"DisDrTae";i:1135;s:5:"Indem";i:1136;s:5:"DefUp";i:1137;s:5:"EpiIn";i:1138;s:12:"GastrSephSub";i:1139;s:8:"FosUnlie";i:1140;s:5:"Tekya";i:1141;s:8:"NonfeZib";i:1142;s:12:"StyloSubmTha";i:1143;s:7:"UnhUnst";i:1144;s:12:"DispNontPern";i:1145;s:4:"Cary";i:1146;s:2:"Fr";i:1147;s:2:"Cr";i:1148;s:7:"PhlebTs";i:1149;s:8:"FletcUne";i:1150;s:8:"HypeMaUn";i:1151;s:12:"ImprMalSuper";i:1152;s:5:"SacUn";i:1153;s:11:"RubSchatSup";i:1154;s:7:"HeSurfb";i:1155;s:13:"MagTaipoTript";i:1156;s:7:"LesSubs";i:1157;s:7:"PrTreTr";i:1158;s:7:"IvUnUns";i:1159;s:10:"HidagIntRe";i:1160;s:11:"NoSaleSpinu";i:1161;s:5:"Myalg";i:1162;s:10:"DeLenSubla";i:1163;s:7:"SurgyTr";i:1164;s:5:"OuPes";i:1165;s:7:"MarMuWe";i:1166;s:6:"FestFl";i:1167;s:7:"PhQuiUn";i:1168;s:5:"Scabr";i:1169;s:5:"SpTra";i:1170;s:4:"Dact";i:1171;s:5:"Reorg";i:1172;s:8:"LuPapSab";i:1173;s:3:"Epi";i:1174;s:11:"HairSpStrat";i:1175;s:13:"IlleiSemicSha";i:1176;s:14:"DespPropoRepla";i:1177;s:5:"Chymi";i:1178;s:7:"FoUnwre";i:1179;s:5:"Unper";i:1180;s:3:"Jel";i:1181;s:3:"Ego";i:1182;s:6:"CryPre";i:1183;s:5:"PrUnt";i:1184;s:2:"Rh";i:1185;s:6:"FaVamp";i:1186;s:8:"WesteYel";i:1187;s:4:"Limn";i:1188;s:9:"ClamHeUra";i:1189;s:10:"CrooEupShe";i:1190;s:11:"ChDandiMort";i:1191;s:7:"EsEveNe";i:1192;s:14:"LatitToniVoidl";i:1193;s:10:"HadLitNong";i:1194;s:4:"Psyc";i:1195;s:3:"Oct";i:1196;s:3:"Une";i:1197;s:9:"RaScuseSp";i:1198;s:8:"InherRor";i:1199;s:6:"ParStr";i:1200;s:7:"MetaPau";i:1201;s:9:"SeiUnUnva";i:1202;s:9:"TaUntVerj";i:1203;s:8:"CheTelot";i:1204;s:10:"InceKiMurz";i:1205;s:15:"DomicImoliLiter";i:1206;s:5:"CopDe";i:1207;s:3:"Amo";i:1208;s:10:"ParalUnent";i:1209;s:2:"Ea";i:1210;s:4:"Unbi";i:1211;s:13:"PachPlagiTrep";i:1212;s:4:"LyUn";i:1213;s:2:"Wo";i:1214;s:7:"PlatRom";i:1215;s:12:"ChiQuitSulph";i:1216;s:11:"AuPennoRoll";i:1217;s:13:"LarriPallPron";i:1218;s:8:"MailpPyg";i:1219;s:4:"PiTa";i:1220;s:4:"Unam";i:1221;s:9:"HedgeStra";i:1222;s:6:"GynKit";i:1223;s:2:"Sh";i:1224;s:10:"CubInStati";i:1225;s:2:"Mi";i:1226;s:8:"PeRefSou";i:1227;s:5:"InInf";i:1228;s:5:"OveVi";i:1229;s:10:"ReacUnsVal";i:1230;s:5:"Silve";i:1231;s:5:"GooUn";i:1232;s:6:"ThVict";i:1233;s:10:"MalPerShin";i:1234;s:4:"Chuc";i:1235;s:7:"RiaSerr";i:1236;s:11:"OverPhyUlli";i:1237;s:8:"CepEnvie";i:1238;s:8:"ManSubmi";i:1239;s:10:"CarErysPyr";i:1240;s:8:"PlaSpecu";i:1241;s:9:"DraOveOve";i:1242;s:9:"GiddyHyWe";i:1243;s:4:"Cala";i:1244;s:5:"ChHyr";i:1245;s:10:"HeterPoSyn";i:1246;s:7:"FrMaPer";i:1247;s:7:"SeeUnbe";i:1248;s:5:"Redto";i:1249;s:13:"FonsxIllPreli";i:1250;s:7:"InOvers";i:1251;s:5:"Thril";i:1252;s:10:"DisplMicVo";i:1253;s:9:"ImpeInSem";i:1254;s:4:"Osch";i:1255;s:4:"Duog";i:1256;s:9:"NonPuShre";i:1257;s:14:"OstreSergToolm";i:1258;s:9:"HyakMahPa";i:1259;s:7:"PalRefr";i:1260;s:7:"PoTroch";i:1261;s:12:"GiddyInWhipp";i:1262;s:7:"BroDrys";i:1263;s:6:"IllRef";i:1264;s:10:"CommaLabSt";i:1265;s:5:"Apoll";i:1266;s:2:"Ne";i:1267;s:3:"Kat";i:1268;s:7:"PoTinct";i:1269;s:10:"NasSynosUn";i:1270;s:4:"Pant";i:1271;s:5:"Unmis";i:1272;s:5:"UnVin";i:1273;s:4:"Ordi";i:1274;s:10:"MazocPrebe";i:1275;s:3:"Unw";i:1276;s:4:"TyUn";i:1277;s:6:"MetaUn";i:1278;s:4:"Thri";i:1279;s:11:"OokiSilkwSt";i:1280;s:5:"OvPed";i:1281;s:6:"MesoSq";i:1282;s:3:"Per";i:1283;s:9:"ImTraWrit";i:1284;s:2:"In";i:1285;s:9:"NaSliceWh";i:1286;s:8:"PolTherm";i:1287;s:4:"Topm";i:1288;s:9:"CasPnTita";i:1289;s:5:"Unsur";i:1290;s:6:"HeInPr";i:1291;s:8:"AtGiMisp";i:1292;s:14:"BathuEpicyPhor";i:1293;s:5:"Windb";i:1294;s:7:"DoDomUn";i:1295;s:7:"LetPrer";i:1296;s:4:"Subu";i:1297;s:12:"MacroOrtSwee";i:1298;s:11:"DarapEelHoo";i:1299;s:7:"UnaWeWu";i:1300;s:10:"HydrJoiOve";i:1301;s:4:"Rein";i:1302;s:9:"GuarPedia";i:1303;s:5:"Primo";i:1304;s:5:"Pregn";i:1305;s:2:"Tw";i:1306;s:11:"KinMemorTou";i:1307;s:14:"LandbMantSuper";i:1308;s:4:"Pers";i:1309;s:5:"DiRho";i:1310;s:9:"KabaRinne";i:1311;s:2:"To";i:1312;s:4:"Stom";i:1313;s:5:"Movie";i:1314;s:12:"ProtoSpoUnsy";i:1315;s:12:"LiceSharUpso";i:1316;s:9:"SlStereWo";i:1317;s:4:"Tubb";i:1318;s:7:"EradiPe";i:1319;s:10:"OrthoVolum";i:1320;s:10:"LeaMattNon";i:1321;s:11:"PeSemiThrom";i:1322;s:3:"Yar";i:1323;s:12:"DetraGalvSki";i:1324;s:6:"ThyUna";i:1325;s:8:"DiRainRo";i:1326;s:12:"HarakHerVide";i:1327;s:5:"Plata";i:1328;s:4:"Occi";i:1329;s:7:"PaQuadr";i:1330;s:7:"SanToot";i:1331;s:10:"DecExploIn";i:1332;s:2:"Cl";i:1333;s:7:"UnbeUns";i:1334;s:6:"OphRel";i:1335;s:13:"SangSphenUnco";i:1336;s:4:"Unli";i:1337;s:15:"HorneNonopSnake";i:1338;s:11:"PhiSubjUnim";i:1339;s:6:"SaniSu";i:1340;s:3:"Man";i:1341;s:7:"UnUntre";i:1342;s:10:"DihRecapUn";i:1343;s:9:"IntNonrTr";i:1344;s:10:"CoryLerrWh";i:1345;s:10:"GimMonRetr";i:1346;s:4:"Phry";i:1347;s:6:"MyoWea";i:1348;s:3:"Was";i:1349;s:6:"GrHemi";i:1350;s:9:"InInfePre";i:1351;s:4:"NuPn";i:1352;s:14:"ChlorHeteProca";i:1353;s:7:"FroImpu";i:1354;s:6:"DiStee";i:1355;s:7:"CarnUnd";i:1356;s:11:"SentiTimeUn";i:1357;s:11:"ScrawVeYach";i:1358;s:5:"Utero";i:1359;s:8:"InfOfPho";i:1360;s:10:"MolSulphUn";i:1361;s:6:"CaDePr";i:1362;s:5:"InRea";i:1363;s:4:"Scra";i:1364;s:12:"JaPosteUnder";i:1365;s:7:"MuscVer";i:1366;s:12:"DimerEupaMeg";i:1367;s:4:"Grap";i:1368;s:11:"CadeHectoHo";i:1369;s:6:"SulThi";i:1370;s:4:"Hama";i:1371;s:9:"LymOuRemn";i:1372;s:4:"Mart";i:1373;s:6:"KaTrum";i:1374;s:3:"Way";i:1375;s:11:"KitLatPomon";i:1376;s:14:"IsotLeiomTrich";i:1377;s:9:"SteTarrUn";i:1378;s:10:"DeFoxTurnc";i:1379;s:8:"LeucNoPe";i:1380;s:9:"PeriRelSi";i:1381;s:9:"NoParTien";i:1382;s:6:"PupUna";i:1383;s:5:"PanSa";i:1384;s:9:"DeoKerxTu";i:1385;s:5:"Unwor";i:1386;s:2:"Mu";i:1387;s:14:"IodosOverWrith";i:1388;s:4:"HuPa";i:1389;s:7:"JocLame";i:1390;s:4:"Mame";i:1391;s:5:"Unspi";i:1392;s:11:"IthyLeisPos";i:1393;s:7:"ParapWa";i:1394;s:7:"TriliUn";i:1395;s:8:"TarTheer";i:1396;s:5:"Frust";i:1397;s:6:"GuTrun";i:1398;s:9:"MembrReco";i:1399;s:10:"KwartRheUr";i:1400;s:3:"Sem";i:1401;s:4:"Naph";i:1402;s:11:"ParRecUsuar";i:1403;s:12:"LegaMallPowd";i:1404;s:8:"OveProet";i:1405;s:11:"FinesOvXeno";i:1406;s:10:"PresuRattl";i:1407;s:3:"Dem";i:1408;s:13:"FrienRevSeque";i:1409;s:12:"BriHyperKrem";i:1410;s:13:"HomoMoribTouc";i:1411;s:4:"Wauk";i:1412;s:12:"SupeTonsiUns";i:1413;s:8:"KikuScyt";i:1414;s:5:"Scare";i:1415;s:12:"HippoPlPneum";i:1416;s:8:"GraNonfo";i:1417;s:7:"EuphVel";i:1418;s:5:"Undar";i:1419;s:12:"GampShriSurr";i:1420;s:2:"Wi";i:1421;s:7:"OsmPrUn";i:1422;s:10:"TarahUnabi";i:1423;s:7:"CaDawTy";i:1424;s:10:"GeomeLater";i:1425;s:8:"GelPaSup";i:1426;s:8:"RegreVol";i:1427;s:7:"LepMeio";i:1428;s:12:"HeMesolSpeec";i:1429;s:4:"Pred";i:1430;s:12:"EpParagRedex";i:1431;s:10:"MiPhilTyro";i:1432;s:8:"ParaSaga";i:1433;s:6:"EstNih";i:1434;s:11:"OrdiRevolSe";i:1435;s:3:"Pla";i:1436;s:12:"LepiRetSuper";i:1437;s:12:"HoloOperWild";i:1438;s:6:"InteVe";i:1439;s:4:"Whit";i:1440;s:11:"JuMinkoXant";i:1441;s:10:"BowmHelMal";i:1442;s:11:"KetazPrVeld";i:1443;s:4:"Intr";i:1444;s:7:"LeucSub";i:1445;s:7:"OveUnUn";i:1446;s:7:"MyelPos";i:1447;s:6:"PrSche";i:1448;s:2:"Ze";i:1449;s:5:"Pyrrh";i:1450;s:5:"ChaPh";i:1451;s:8:"RelUltra";i:1452;s:5:"Prere";i:1453;s:7:"FoProSe";i:1454;s:11:"MyoUnyiWeir";i:1455;s:8:"InLiTaro";i:1456;s:8:"QuintSty";i:1457;s:4:"Euch";i:1458;s:5:"Consu";i:1459;s:5:"Incon";i:1460;s:11:"HypoMagnPyc";i:1461;s:5:"PeUbi";i:1462;s:13:"HistoIlokaRas";i:1463;s:8:"ManoMiPl";i:1464;s:11:"LeathRedeSu";i:1465;s:11:"PalStucToba";i:1466;s:13:"HabitPriSaber";i:1467;s:5:"Table";i:1468;s:11:"SeneSubcToo";i:1469;s:11:"DiOffeSuper";i:1470;s:4:"Ruff";i:1471;s:12:"HyperPoiStor";i:1472;s:13:"BigwDirtUnasp";i:1473;s:3:"Pro";i:1474;s:6:"EpiExe";i:1475;s:10:"HypMetNahu";i:1476;s:10:"DimiValeWi";i:1477;s:4:"FiRa";i:1478;s:11:"DoSomnaUnta";i:1479;s:8:"BupMazam";i:1480;s:13:"CurtaOntSuppl";i:1481;s:5:"Waben";i:1482;s:10:"EpicrLymph";i:1483;s:7:"CasUnca";i:1484;s:8:"HatchWhe";i:1485;s:8:"HarmMole";i:1486;s:3:"Thi";i:1487;s:3:"Hal";i:1488;s:10:"CisSpuddVe";i:1489;s:11:"LiMismiPsyc";i:1490;s:11:"PolliSiVign";i:1491;s:10:"SeducShSta";i:1492;s:8:"SaugScab";i:1493;s:6:"IslaTr";i:1494;s:10:"ComImParat";i:1495;s:4:"Peri";i:1496;s:11:"GritLaRambe";i:1497;s:7:"EddZygo";i:1498;s:12:"BumInteMidma";i:1499;s:15:"InterOrnitPreme";i:1500;s:9:"PrSugSulf";i:1501;s:8:"IdePsych";i:1502;s:4:"Proc";i:1503;s:7:"CosmTri";i:1504;s:6:"CommUn";i:1505;s:5:"Sawfl";i:1506;s:5:"CusSe";i:1507;s:7:"TrUlste";i:1508;s:4:"Thyr";i:1509;s:5:"Persp";i:1510;s:9:"PedulSiVi";i:1511;s:6:"AmMyrm";i:1512;s:5:"Physi";i:1513;s:8:"SurcTolx";i:1514;s:5:"Cytop";i:1515;s:5:"GaRav";i:1516;s:13:"CarMelanUnuni";i:1517;s:6:"EdSubt";i:1518;s:7:"DotGeph";i:1519;s:7:"InNativ";i:1520;s:5:"Ozoty";i:1521;s:7:"PriScal";i:1522;s:4:"SeUn";i:1523;s:13:"FlexuIntYoudi";i:1524;s:3:"Tra";i:1525;s:8:"SubbiTap";i:1526;s:11:"ChloDrucGra";i:1527;s:8:"MarrMimi";i:1528;s:9:"DooSeSpec";i:1529;s:7:"GauzySe";i:1530;s:4:"Chio";i:1531;s:10:"RatafThVip";i:1532;s:6:"FruiGy";i:1533;s:15:"FoddeSulfoZeuct";i:1534;s:7:"BromiRe";i:1535;s:8:"OvPhrUlt";i:1536;s:3:"Unv";i:1537;s:5:"Lidfl";i:1538;s:5:"Uncon";i:1539;s:12:"SapodUnblUno";i:1540;s:13:"DomiHakimMola";i:1541;s:4:"Unth";i:1542;s:5:"FoOve";i:1543;s:9:"LendePoUn";i:1544;s:14:"ConfScrofTelle";i:1545;s:11:"MonoOutboTe";i:1546;s:11:"FeckTeUnfea";i:1547;s:5:"Wopsx";i:1548;s:8:"StaSulfa";i:1549;s:9:"CoalInapa";i:1550;s:6:"RisSqu";i:1551;s:12:"SubtWacexZyg";i:1552;s:11:"MonoMorSqua";i:1553;s:7:"DisFrTr";i:1554;s:9:"BonaIllit";i:1555;s:4:"Pron";i:1556;s:13:"LuculThaThutt";i:1557;s:3:"Unm";i:1558;s:9:"FelsOpine";i:1559;s:13:"LumpiStaiUnhu";i:1560;s:12:"HoIneliUnlat";i:1561;s:2:"Or";i:1562;s:11:"GoatrNoUnes";i:1563;s:12:"AlBenthPsych";i:1564;s:4:"Insu";i:1565;s:5:"Unver";i:1566;s:4:"Eulo";i:1567;s:7:"OmThroa";i:1568;s:9:"HyUnsUnwo";i:1569;s:5:"PeRes";i:1570;s:5:"DecTe";i:1571;s:6:"SpoZym";i:1572;s:5:"Satyr";i:1573;s:4:"Subc";i:1574;s:2:"As";i:1575;s:3:"Ret";i:1576;s:7:"JarblMy";i:1577;s:4:"Noni";i:1578;s:10:"BalusCacon";i:1579;s:10:"HazReiWard";i:1580;s:7:"QuisThe";i:1581;s:12:"SawflStrumTr";i:1582;s:4:"Witc";i:1583;s:12:"BenzoCheOver";i:1584;s:4:"Preo";i:1585;s:11:"DisseGaName";i:1586;s:8:"EleReadm";i:1587;s:8:"UnaUnZig";i:1588;s:7:"EskNeur";i:1589;s:9:"DisFanSco";i:1590;s:8:"FesJolRe";i:1591;s:10:"ConfuDoPre";i:1592;s:12:"CaptPseuRush";i:1593;s:12:"ScreXenomYar";i:1594;s:9:"RidiTarVa";i:1595;s:10:"HippMbaSaf";i:1596;s:14:"ElateNonreOver";i:1597;s:6:"MiPara";i:1598;s:10:"PterPuSear";i:1599;s:5:"Marsh";i:1600;s:5:"Stram";i:1601;s:8:"SheWippe";i:1602;s:9:"StreWench";i:1603;s:13:"SubauSycopZoo";i:1604;s:14:"LarunNonaPhyto";i:1605;s:8:"OrdeVigi";i:1606;s:11:"KumOphthPar";i:1607;s:4:"Depe";i:1608;s:10:"DecumPerio";i:1609;s:8:"ArchClMe";i:1610;s:7:"TawieWy";i:1611;s:5:"SupWr";i:1612;s:10:"ReshSaTydi";i:1613;s:8:"BeverDis";i:1614;s:11:"HypNeuroTru";i:1615;s:15:"DrinkFulmiSaper";i:1616;s:14:"GlycMachiNonre";i:1617;s:8:"TapiUrbi";i:1618;s:5:"InNun";i:1619;s:11:"ProTalegTot";i:1620;s:5:"GarUn";i:1621;s:5:"Signa";i:1622;s:10:"ProofUltra";i:1623;s:5:"Parti";i:1624;s:10:"HoWanlWitc";i:1625;s:5:"Ossua";i:1626;s:11:"ImpPreSuper";i:1627;s:8:"CurIsoRi";i:1628;s:4:"Play";i:1629;s:4:"Fore";i:1630;s:11:"DecuExtrInd";i:1631;s:12:"DysanHydPost";i:1632;s:7:"ReTelev";i:1633;s:9:"CionxPrSh";i:1634;s:6:"NatiUn";i:1635;s:4:"Grav";i:1636;s:4:"Tran";i:1637;s:3:"Kan";i:1638;s:12:"LichMakiUnne";i:1639;s:9:"LacMesPer";i:1640;s:7:"CoHidJi";i:1641;s:11:"MahSurUnlam";i:1642;s:10:"DeOutsWauk";i:1643;s:13:"MarPylorRoots";i:1644;s:6:"ExPehu";i:1645;s:5:"PaTak";i:1646;s:5:"Rubas";i:1647;s:8:"PhaseWit";i:1648;s:5:"Inhar";i:1649;s:12:"OsteRhyScapu";i:1650;s:13:"MonotRedeSupe";i:1651;s:10:"BlephDaPer";i:1652;s:8:"SylvTrUn";i:1653;s:6:"MePuSa";i:1654;s:4:"DuUn";i:1655;s:7:"DistSem";i:1656;s:5:"HuPer";i:1657;s:11:"PlPolyUnpea";i:1658;s:5:"FeeNe";i:1659;s:12:"ElegGrinZygo";i:1660;s:13:"PanteUnbloUns";i:1661;s:6:"LifTur";i:1662;s:4:"Unsi";i:1663;s:7:"PeoplUr";i:1664;s:5:"CerMa";i:1665;s:8:"TechnUns";i:1666;s:7:"ScleWhe";i:1667;s:11:"MazanNecPro";i:1668;s:10:"RecovSergi";i:1669;s:5:"Unidi";i:1670;s:8:"SunUninf";i:1671;s:9:"MoSciUnfa";i:1672;s:10:"FelsOstTri";i:1673;s:7:"ConSang";i:1674;s:3:"Unh";i:1675;s:7:"CrHyTri";i:1676;s:3:"Kne";i:1677;s:10:"EpiNonPhoe";i:1678;s:7:"HydNeig";i:1679;s:6:"BoOnse";i:1680;s:7:"UnhorVe";i:1681;s:13:"OtherSirTobex";i:1682;s:8:"LaPrebUn";i:1683;s:12:"MyeNitidRace";i:1684;s:11:"DisDorsPseu";i:1685;s:4:"Unne";i:1686;s:10:"ComatTashr";i:1687;s:5:"Chrom";i:1688;s:8:"ScolTene";i:1689;s:6:"PholTi";i:1690;s:5:"EnoUn";i:1691;s:8:"MaxNonsp";i:1692;s:11:"MisStaphTal";i:1693;s:6:"PsSlUn";i:1694;s:7:"SeUlste";i:1695;s:10:"PreThrepUn";i:1696;s:3:"Min";i:1697;s:6:"SplZoo";i:1698;s:4:"Obit";i:1699;s:9:"LiRicoSar";i:1700;s:9:"PsReReind";i:1701;s:2:"Gu";i:1702;s:11:"PioSemUncha";i:1703;s:10:"DebamInsPl";i:1704;s:4:"CoNo";i:1705;s:3:"Pat";i:1706;s:5:"Rhino";i:1707;s:12:"GladdInUnscr";i:1708;s:5:"Gargo";i:1709;s:3:"Hog";i:1710;s:4:"Tany";i:1711;s:9:"FacepUngr";i:1712;s:6:"DesUnd";i:1713;s:3:"Tap";i:1714;s:6:"ReUnde";i:1715;s:2:"Qu";i:1716;s:7:"IntuPar";i:1717;s:5:"LopPr";i:1718;s:4:"Unta";i:1719;s:3:"Tin";i:1720;s:3:"Mot";i:1721;s:10:"MemorRemic";i:1722;s:11:"FistlPanTyi";i:1723;s:12:"GhoGuarRelbu";i:1724;s:3:"Coo";i:1725;s:6:"NotPig";i:1726;s:4:"Orna";i:1727;s:9:"InNonalTh";i:1728;s:7:"AlConen";i:1729;s:8:"QuiSyncx";i:1730;s:12:"HematLarTear";i:1731;s:4:"Link";i:1732;s:7:"PolarVa";i:1733;s:14:"MacroParvRuina";i:1734;s:7:"KeelRep";i:1735;s:4:"UnWo";i:1736;s:13:"PolygRemTucun";i:1737;s:9:"LoxoReSin";i:1738;s:12:"OcPassaSciss";i:1739;s:9:"CopeCorre";i:1740;s:12:"GawLaborTeta";i:1741;s:8:"CoHeSupe";i:1742;s:7:"ProtTel";i:1743;s:7:"RaUnplu";i:1744;s:3:"Try";i:1745;s:4:"Tols";i:1746;s:2:"Sn";i:1747;s:8:"CincUnco";i:1748;s:11:"CondPytUnre";i:1749;s:7:"CeMePro";i:1750;s:5:"Myrme";i:1751;s:4:"Skew";i:1752;s:3:"Usa";i:1753;s:8:"SabbaSla";i:1754;s:9:"GlePrThyr";i:1755;s:7:"FlRhina";i:1756;s:11:"ReaSilicUnb";i:1757;s:9:"InPahTeer";i:1758;s:4:"Gude";i:1759;s:9:"KaxPyosUn";i:1760;s:5:"Bianc";i:1761;s:10:"SelenSquas";i:1762;s:9:"RhinUntuc";i:1763;s:8:"PesSupUn";i:1764;s:7:"SyTeneb";i:1765;s:3:"Gly";i:1766;s:12:"MethaMucPara";i:1767;s:14:"DemaIcelaWhome";i:1768;s:3:"Pri";i:1769;s:10:"ChapaLiMet";i:1770;s:11:"EhaNonseVot";i:1771;s:3:"Xan";i:1772;s:12:"MerocTuppWal";i:1773;s:3:"Dyn";i:1774;s:11:"MystaPseSem";i:1775;s:3:"Org";i:1776;s:13:"MelanParaWear";i:1777;s:9:"ReissUnVi";i:1778;s:2:"Me";i:1779;s:9:"JokisPrTh";i:1780;s:13:"CraEluviFlood";i:1781;s:7:"NavStee";i:1782;s:7:"PasqRep";i:1783;s:7:"FeMildh";i:1784;s:3:"Sub";i:1785;s:12:"IntMeisTetra";i:1786;s:8:"CherPrec";i:1787;s:7:"MegaSex";i:1788;s:4:"GrSp";i:1789;s:8:"PePinfSc";i:1790;s:9:"FluxTripp";i:1791;s:8:"GaliWeak";i:1792;s:3:"Req";i:1793;s:9:"InOvereRo";i:1794;s:8:"NicRamen";i:1795;s:8:"AlfMaUnd";i:1796;s:7:"NonabPi";i:1797;s:8:"ResiTing";i:1798;s:8:"GoParTub";i:1799;s:4:"Sili";i:1800;s:5:"PhPre";i:1801;s:10:"ApartTuske";i:1802;s:6:"HereRe";i:1803;s:4:"ElPr";i:1804;s:13:"HiruMonoTownw";i:1805;s:11:"ChalGuaWord";i:1806;s:11:"ChrNonpuSub";i:1807;s:11:"LazyxQuUndi";i:1808;s:4:"QuRe";i:1809;s:8:"DiGulfWi";i:1810;s:7:"PunUroc";i:1811;s:9:"CulFurHyp";i:1812;s:5:"Salty";i:1813;s:9:"PitcPoStu";i:1814;s:2:"La";i:1815;s:7:"UropZoo";i:1816;s:11:"HermiKaRoup";i:1817;s:4:"Cons";i:1818;s:10:"RapiSprTri";i:1819;s:8:"DenoMiPi";i:1820;s:8:"NairyPhy";i:1821;s:2:"Um";i:1822;s:10:"JuScopUnwi";i:1823;s:4:"Radi";i:1824;s:8:"CribExtr";i:1825;s:6:"SalThr";i:1826;s:3:"Dil";i:1827;s:7:"DefEwde";i:1828;s:9:"PalliUnac";i:1829;s:9:"SafraUnst";i:1830;s:13:"PrefSidaUnder";i:1831;s:11:"SatSporUnin";i:1832;s:5:"MoSch";i:1833;s:8:"CrRepSym";i:1834;s:2:"Pt";i:1835;s:4:"OvPr";i:1836;s:6:"DeUnab";i:1837;s:4:"Unde";i:1838;s:5:"Negro";i:1839;s:5:"MarPu";i:1840;s:5:"Ortho";i:1841;s:12:"MatroThesTra";i:1842;s:2:"Ma";i:1843;s:8:"ActFaKee";i:1844;s:12:"SalacThecUnd";i:1845;s:10:"MuMurPrein";i:1846;s:9:"PtiloTall";i:1847;s:2:"Oc";i:1848;s:10:"ScattSphen";i:1849;s:5:"MotUn";i:1850;s:7:"CuStuUn";i:1851;s:4:"Unqu";i:1852;s:4:"Decr";i:1853;s:5:"TalTr";i:1854;s:4:"Sexd";i:1855;s:7:"ShaUnmo";i:1856;s:6:"EuoNat";i:1857;s:8:"MahiSemi";i:1858;s:9:"GiglTacho";i:1859;s:10:"GneisMaulx";i:1860;s:11:"HemaLaPrors";i:1861;s:10:"FlubPacTyp";i:1862;s:9:"AlNonjTel";i:1863;s:9:"RecSuiUnc";i:1864;s:8:"ConsiSar";i:1865;s:4:"Prei";i:1866;s:4:"Lami";i:1867;s:11:"ManPelecSau";i:1868;s:5:"Ducat";i:1869;s:7:"PsSauci";i:1870;s:7:"HolTerp";i:1871;s:3:"She";i:1872;s:10:"DisDowerSt";i:1873;s:5:"Timan";i:1874;s:3:"Pal";i:1875;s:8:"QuinSuTo";i:1876;s:12:"HeathNonsuSo";i:1877;s:11:"GenMicrTeno";i:1878;s:5:"RecUn";i:1879;s:4:"Unpr";i:1880;s:7:"FlamIrr";i:1881;s:13:"DonarManaPero";i:1882;s:6:"ShTopi";i:1883;s:4:"Scit";i:1884;s:10:"OmmatScour";i:1885;s:5:"Ethen";i:1886;s:4:"Xylo";i:1887;s:5:"EryTr";i:1888;s:6:"BraHol";i:1889;s:13:"ImpalRummaVar";i:1890;s:4:"Trav";i:1891;s:11:"JotLoxUnfun";i:1892;s:8:"MuscSeme";i:1893;s:8:"GiLiPara";i:1894;s:9:"ViscoVouc";i:1895;s:3:"Scr";i:1896;s:6:"HaMaNo";i:1897;s:6:"StaSwe";i:1898;s:9:"InteRadio";i:1899;s:12:"PrThynnUnvol";i:1900;s:8:"PhilRecr";i:1901;s:9:"OmelTecpa";i:1902;s:9:"PettyUnwe";i:1903;s:5:"Melan";i:1904;s:14:"GasteQuinqTric";i:1905;s:12:"CommuDaInter";i:1906;s:5:"Megam";i:1907;s:12:"MiPickpTakam";i:1908;s:4:"Hydr";i:1909;s:4:"Torv";i:1910;s:7:"ExtiInt";i:1911;s:13:"EuphoFruOutdw";i:1912;s:5:"Tinni";i:1913;s:4:"Subs";i:1914;s:5:"OvPho";i:1915;s:11:"GlyphMonRig";i:1916;s:4:"Pion";i:1917;s:10:"IndagPieTi";i:1918;s:11:"TakiUnprUpa";i:1919;s:5:"LoVar";i:1920;s:5:"FaPla";i:1921;s:7:"MiscOns";i:1922;s:7:"DusStee";i:1923;s:9:"BeetlHang";i:1924;s:12:"FigMarmUnend";i:1925;s:5:"Fordo";i:1926;s:4:"Soci";i:1927;s:12:"HygrHypShopk";i:1928;s:11:"HabPlowWenc";i:1929;s:8:"PerSpent";i:1930;s:4:"Camp";i:1931;s:5:"PatRu";i:1932;s:8:"GhaSuthe";i:1933;s:10:"OuttPaUnim";i:1934;s:9:"StockTacc";i:1935;s:7:"InneUns";i:1936;s:4:"Trac";i:1937;s:4:"Unin";i:1938;s:8:"PolUninv";i:1939;s:9:"PheSterUn";i:1940;s:8:"DysYukia";i:1941;s:5:"PenSc";i:1942;s:7:"DaInShi";i:1943;s:14:"HepaNonshOrbic";i:1944;s:7:"PluPrPr";i:1945;s:4:"Semp";i:1946;s:3:"Unb";i:1947;s:9:"LancNonco";i:1948;s:10:"FiKlockNon";i:1949;s:4:"InSw";i:1950;s:8:"RoUnsuWi";i:1951;s:6:"ObscSe";i:1952;s:8:"ScythSub";i:1953;s:7:"HistMot";i:1954;s:5:"GoUtt";i:1955;s:5:"Inter";i:1956;s:7:"MiThUnp";i:1957;s:8:"BreComus";i:1958;s:8:"HurMemTu";i:1959;s:6:"OdylSn";i:1960;s:5:"Misec";i:1961;s:10:"PolypScler";i:1962;s:10:"OverSintTh";i:1963;s:8:"OverSexu";i:1964;s:12:"DruMagneSant";i:1965;s:10:"ParoPawPri";i:1966;s:7:"MaPrVar";i:1967;s:12:"LancNeolShas";i:1968;s:7:"SelUnsh";i:1969;s:5:"Nephr";i:1970;s:11:"NaSeismSept";i:1971;s:9:"DrivaNySi";i:1972;s:8:"PePheUnt";i:1973;s:9:"BusElSilp";i:1974;s:10:"SarclSever";i:1975;s:10:"ChirpDeEde";i:1976;s:10:"SarSigUndi";i:1977;s:8:"PyxidSup";i:1978;s:4:"Nubb";i:1979;s:8:"HederPne";i:1980;s:10:"PhthiTeneb";i:1981;s:7:"FrFreon";i:1982;s:12:"QuareResiSke";i:1983;s:6:"GangNo";i:1984;s:6:"NoonSp";i:1985;s:5:"StrUn";i:1986;s:11:"CrDarogYera";i:1987;s:9:"PhPogamPr";i:1988;s:11:"EneugInceOs";i:1989;s:5:"Carac";i:1990;s:11:"NaysaOvSupe";i:1991;s:5:"BlaHu";i:1992;s:10:"RoselUnWoo";i:1993;s:10:"CorinGraTe";i:1994;s:7:"InUnobv";i:1995;s:9:"BaChuroIn";i:1996;s:8:"MulSerol";i:1997;s:4:"Cast";i:1998;s:6:"DeQuXy";i:1999;s:8:"MiPnVeri";i:2000;s:7:"MyrStor";i:2001;s:8:"IncavZin";i:2002;s:9:"KayRulaTr";i:2003;s:3:"Ser";i:2004;s:7:"PaucRus";i:2005;s:2:"Tu";i:2006;s:9:"OddmWindb";i:2007;s:8:"UnpWhang";i:2008;s:4:"GaMe";i:2009;s:6:"MaPost";i:2010;s:7:"PomRach";i:2011;s:2:"Wy";i:2012;s:11:"MaQueeTapst";i:2013;s:5:"Denar";i:2014;s:10:"MethoSneer";i:2015;s:9:"CovaSpell";i:2016;s:8:"SlanTuis";i:2017;s:12:"ChiReprTaunt";i:2018;s:9:"LaMeasUne";i:2019;s:5:"PoPre";i:2020;s:5:"SpaSs";i:2021;s:4:"Salm";i:2022;s:5:"Endop";i:2023;s:3:"Tut";i:2024;s:6:"PeriPh";i:2025;s:13:"IrreSodaVeget";i:2026;s:5:"Locri";i:2027;s:7:"UnZooma";i:2028;s:4:"Prou";i:2029;s:5:"Outdw";i:2030;s:12:"MesneReThrou";i:2031;s:2:"Se";i:2032;s:4:"Sate";i:2033;s:4:"AtDi";i:2034;s:5:"OrPhe";i:2035;s:5:"Quond";i:2036;s:5:"Samar";i:2037;s:2:"Ke";i:2038;s:5:"Nonmu";i:2039;s:12:"ForcTaggyTea";i:2040;s:4:"Uncr";i:2041;s:5:"PlaRa";i:2042;s:7:"FeFemPr";i:2043;s:5:"ClDis";i:2044;s:14:"BackbIlluvPseu";i:2045;s:10:"BrDemiLuxu";i:2046;s:10:"GaMyoplOut";i:2047;s:14:"MeteOutwoPeriu";i:2048;s:8:"GaMycSha";i:2049;s:5:"Unapp";i:2050;s:4:"Gibb";i:2051;s:3:"Hem";i:2052;s:5:"Syngn";i:2053;s:5:"Lophi";i:2054;s:7:"OvReUnl";i:2055;s:6:"ProfWo";i:2056;s:11:"BaElasmElop";i:2057;s:2:"Eq";i:2058;s:5:"Workm";i:2059;s:9:"AppelUnic";i:2060;s:4:"Rabb";i:2061;s:8:"DovInspi";i:2062;s:5:"Katip";i:2063;s:12:"MiPortmTecto";i:2064;s:8:"ScSiVola";i:2065;s:3:"Cra";i:2066;s:14:"HexaPassaPhila";i:2067;s:9:"FrHoUndet";i:2068;s:7:"DipLint";i:2069;s:4:"IsPa";i:2070;s:3:"New";i:2071;s:3:"Ton";i:2072;s:11:"ImshMacrScr";i:2073;s:5:"Pampr";i:2074;s:9:"InnocUnch";i:2075;s:13:"LithxUnrZipsx";i:2076;s:13:"MingyUngeUnor";i:2077;s:10:"NonnoUngUn";i:2078;s:4:"Pict";i:2079;s:8:"RemeSpon";i:2080;s:4:"Plas";i:2081;s:5:"IntRu";i:2082;s:3:"Hyb";i:2083;s:3:"Tec";i:2084;s:3:"Dia";i:2085;s:4:"Unme";i:2086;s:11:"CarlDeliVam";i:2087;s:8:"NeUncrUn";i:2088;s:3:"Ker";i:2089;s:10:"ObjuProlTw";i:2090;s:8:"DeOverSu";i:2091;s:2:"An";i:2092;s:7:"GraduUn";i:2093;s:9:"PalaeSyTr";i:2094;s:3:"Sen";i:2095;s:6:"SubTet";i:2096;s:9:"MadMultSu";i:2097;s:10:"GermaUnpre";i:2098;s:10:"GibbNonsVo";i:2099;s:3:"Phr";i:2100;s:4:"Macr";i:2101;s:4:"ExUn";i:2102;s:11:"KaibMeUnpar";i:2103;s:5:"Mutil";i:2104;s:12:"PentaPoVentr";i:2105;s:3:"For";i:2106;s:10:"PedeUnciUp";i:2107;s:9:"FranTheop";i:2108;s:10:"InterJerUn";i:2109;s:6:"CyaUnt";i:2110;s:4:"Chic";i:2111;s:10:"RemTanThri";i:2112;s:4:"BeSa";i:2113;s:3:"Fec";i:2114;s:10:"CozilMoUht";i:2115;s:2:"Pa";i:2116;s:5:"Mispr";i:2117;s:4:"PrSt";i:2118;s:4:"DiHe";i:2119;s:10:"FakoMoQuix";i:2120;s:11:"PtePyrUnarr";i:2121;s:6:"PhRoSt";i:2122;s:4:"BeEn";i:2123;s:8:"GuiHypha";i:2124;s:8:"GuInoPre";i:2125;s:9:"PatRhaUnt";i:2126;s:4:"Holo";i:2127;s:8:"PrioUnme";i:2128;s:11:"GradRubytUn";i:2129;s:10:"CoeCredeSi";i:2130;s:9:"ReifUndis";i:2131;s:6:"LuVaWi";i:2132;s:7:"AnBuDia";i:2133;s:4:"Trad";i:2134;s:11:"CardOvRocks";i:2135;s:7:"UnhaWas";i:2136;s:13:"MaggeOthWilli";i:2137;s:8:"MycNoPol";i:2138;s:9:"FasGriNig";i:2139;s:6:"LanPar";i:2140;s:9:"FeoffPrSu";i:2141;s:11:"GonoGreUnco";i:2142;s:8:"SmotStom";i:2143;s:4:"Tute";i:2144;s:6:"MellRa";i:2145;s:4:"Pter";i:2146;s:9:"ThrThroTu";i:2147;s:5:"InTap";i:2148;s:7:"CollTub";i:2149;s:2:"Fa";i:2150;s:9:"PiRibbiSu";i:2151;s:7:"ExuPaSe";i:2152;s:9:"DownHoriz";i:2153;s:12:"JudicMockUnb";i:2154;s:9:"MeloSemia";i:2155;s:6:"CoFeRa";i:2156;s:5:"Macaw";i:2157;s:6:"NeurSe";i:2158;s:8:"LattSouc";i:2159;s:8:"UninWart";i:2160;s:9:"GiSubTown";i:2161;s:12:"PaveTitheUnl";i:2162;s:10:"DragsSubUn";i:2163;s:9:"SlopUncha";i:2164;s:5:"OrgTh";i:2165;s:8:"MixScole";i:2166;s:2:"Em";i:2167;s:5:"Remon";i:2168;s:2:"Bo";i:2169;s:7:"SabSupe";i:2170;s:4:"Neri";i:2171;s:5:"TuUnd";i:2172;s:9:"FlagSciss";i:2173;s:3:"Mas";i:2174;s:9:"InIsaThar";i:2175;s:11:"SemiStiThio";i:2176;s:3:"Oth";i:2177;s:10:"NeePoUnhel";i:2178;s:9:"CarInMate";i:2179;s:9:"DenuRefRi";i:2180;s:11:"ChiwColleMo";i:2181;s:12:"EquidPrecUnv";i:2182;s:6:"KrSpha";i:2183;s:10:"ShTeeTotte";i:2184;s:3:"Tou";i:2185;s:8:"IncRoUpb";i:2186;s:7:"IlMengx";i:2187;s:6:"CephSa";i:2188;s:11:"InkPhthaTet";i:2189;s:8:"IncuMoco";i:2190;s:7:"OveSeis";i:2191;s:3:"Rub";i:2192;s:12:"ObUnctiVeloc";i:2193;s:9:"OpalPodli";i:2194;s:3:"Dec";i:2195;s:8:"CymMoPyo";i:2196;s:9:"PhSpicaTr";i:2197;s:5:"Nondy";i:2198;s:4:"Roma";i:2199;s:9:"InsitSuTr";i:2200;s:3:"Poi";i:2201;s:8:"ImInhRos";i:2202;s:9:"HemoInsPr";i:2203;s:7:"RecoSan";i:2204;s:9:"IntraLepr";i:2205;s:7:"PartiRo";i:2206;s:7:"PlacPla";i:2207;s:9:"MedieSeSy";i:2208;s:4:"BaSc";i:2209;s:2:"Sk";i:2210;s:5:"UnfUn";i:2211;s:8:"CadavEpi";i:2212;s:5:"LagPh";i:2213;s:5:"MalMo";i:2214;s:8:"SpSupeUn";i:2215;s:8:"HiOgrePo";i:2216;s:2:"Ca";i:2217;s:5:"Infol";i:2218;s:10:"DyassUninu";i:2219;s:6:"UnUnsa";i:2220;s:12:"ChoSchwZarni";i:2221;s:9:"InReUnpay";i:2222;s:9:"UntrYello";i:2223;s:12:"OraOverSland";i:2224;s:6:"HePrSt";i:2225;s:10:"OpPropiQuo";i:2226;s:9:"PeriTheUn";i:2227;s:12:"AbuPhysiRheg";i:2228;s:7:"DisShad";i:2229;s:12:"CheGalyaTeax";i:2230;s:11:"CorrKaUncol";i:2231;s:9:"HypLandLu";i:2232;s:4:"Yegu";i:2233;s:4:"Prig";i:2234;s:12:"MultiTubbaVi";i:2235;s:14:"RehaSchnoStrin";i:2236;s:8:"PelSquat";i:2237;s:8:"HexRacTu";i:2238;s:12:"SemblSomeUpb";i:2239;s:11:"GymnImplOve";i:2240;s:6:"NondUn";i:2241;s:11:"HelIneStasi";i:2242;s:7:"EstelTi";i:2243;s:10:"InterPoThe";i:2244;s:5:"Degge";i:2245;s:3:"Rel";i:2246;s:4:"Rock";i:2247;s:9:"FlaGaSpas";i:2248;s:7:"MayaTel";i:2249;s:7:"EnteFic";i:2250;s:4:"Penn";i:2251;s:7:"OvUndaz";i:2252;s:5:"Spinu";i:2253;s:11:"CoPhotoRake";i:2254;s:13:"HomocUnioUnli";i:2255;s:7:"FidGirl";i:2256;s:6:"FaceSt";i:2257;s:9:"HardePyre";i:2258;s:5:"Brand";i:2259;s:5:"Enqui";i:2260;s:5:"Workl";i:2261;s:7:"HoIncom";i:2262;s:10:"SittUloUnk";i:2263;s:2:"Ps";i:2264;s:8:"PertiTra";i:2265;s:11:"EngOverTins";i:2266;s:5:"Uncom";i:2267;s:3:"Jaw";i:2268;s:7:"PiStrSy";i:2269;s:5:"Sotti";i:2270;s:14:"AnthrNonsSiusl";i:2271;s:5:"PseQu";i:2272;s:5:"Reedl";i:2273;s:8:"DikerPea";i:2274;s:6:"NoSwar";i:2275;s:10:"PonTiTrans";i:2276;s:6:"StomWh";i:2277;s:5:"Prepa";i:2278;s:11:"EctoTraWeap";i:2279;s:7:"CeJuKet";i:2280;s:7:"CaStiTo";i:2281;s:11:"CholaScalSl";i:2282;s:5:"LeSpu";i:2283;s:7:"NonexPa";i:2284;s:5:"CuUnd";i:2285;s:5:"CoHas";i:2286;s:7:"PresVet";i:2287;s:10:"EfEndotSph";i:2288;s:4:"GeHo";i:2289;s:10:"ConEnkraEx";i:2290;s:9:"SeakUrano";i:2291;s:10:"RebScutSta";i:2292;s:11:"AmetFoStaph";i:2293;s:4:"LoPu";i:2294;s:4:"Unmo";i:2295;s:12:"ChamaCufRetr";i:2296;s:9:"CuttiPoun";i:2297;s:7:"OxToote";i:2298;s:4:"Hali";i:2299;s:14:"OverSuperTickt";i:2300;s:9:"DefeElong";i:2301;s:10:"RozumTypif";i:2302;s:11:"OnomReinhUn";i:2303;s:5:"HePat";i:2304;s:6:"PrRaff";i:2305;s:7:"UrWorld";i:2306;s:12:"MacTanyoVoli";i:2307;s:6:"KeUnbe";i:2308;s:11:"FooliPeSoun";i:2309;s:8:"FiMaStra";i:2310;s:10:"PrRevuSubc";i:2311;s:3:"Kri";i:2312;s:6:"EnneMe";i:2313;s:3:"Mar";i:2314;s:10:"PaSubmeUna";i:2315;s:14:"MitiNoncaPelta";i:2316;s:7:"TroTylo";i:2317;s:4:"Curv";i:2318;s:5:"QuaTy";i:2319;s:7:"NonsaPr";i:2320;s:5:"Urete";i:2321;s:5:"Hynex";i:2322;s:5:"Tillo";i:2323;s:4:"EnMa";i:2324;s:10:"IonPresuPr";i:2325;s:9:"OverwTale";i:2326;s:9:"EnmaSenda";i:2327;s:13:"HiodTrihUnvis";i:2328;s:3:"Viz";i:2329;s:9:"NayarSuVa";i:2330;s:9:"VasoWallo";i:2331;s:8:"CrypMans";i:2332;s:9:"CondyEpip";i:2333;s:7:"GlMalle";i:2334;s:3:"Pag";i:2335;s:7:"ScUltra";i:2336;s:11:"ScholTaUnba";i:2337;s:13:"DisarGestNonc";i:2338;s:3:"Sir";i:2339;s:9:"PugmiUltr";i:2340;s:5:"Pangl";i:2341;s:8:"MetMyoge";i:2342;s:7:"HydroSp";i:2343;s:5:"Anaca";i:2344;s:6:"OrthPs";i:2345;s:5:"Nicco";i:2346;s:12:"DactTonnUnbo";i:2347;s:3:"Tox";i:2348;s:14:"ChorExcitPhaco";i:2349;s:3:"Tab";i:2350;s:10:"BasUnUnrui";i:2351;s:6:"RefUne";i:2352;s:3:"Ope";i:2353;s:9:"OverQuWar";i:2354;s:5:"Antid";i:2355;s:9:"ExFinRust";i:2356;s:14:"PuggiSecedStri";i:2357;s:7:"IriRaWa";i:2358;s:11:"MormProrPse";i:2359;s:5:"StUno";i:2360;s:7:"GastrPh";i:2361;s:2:"Li";i:2362;s:12:"NonOndiRocam";i:2363;s:7:"MollUnt";i:2364;s:2:"Ti";i:2365;s:4:"Podu";i:2366;s:5:"SanTe";i:2367;s:13:"ChieDanaiFell";i:2368;s:7:"CentFar";i:2369;s:6:"OutTab";i:2370;s:14:"MitigNoncoPiro";i:2371;s:13:"HondMarsObnun";i:2372;s:4:"Rest";i:2373;s:7:"NostrOb";i:2374;s:7:"IconLob";i:2375;s:5:"Virag";i:2376;s:9:"ImmePhane";i:2377;s:5:"Scran";i:2378;s:10:"LeNonalUnv";i:2379;s:7:"NiParUn";i:2380;s:8:"PhyTosUn";i:2381;s:12:"HydMarinSpec";i:2382;s:8:"LocSuUnv";i:2383;s:11:"LympTreUnse";i:2384;s:10:"CondxTenta";i:2385;s:8:"LerUnmis";i:2386;s:14:"GhanKeratNekto";i:2387;s:9:"InMalPrav";i:2388;s:3:"Mul";i:2389;s:8:"PilRever";i:2390;s:13:"PrepSialaTrum";i:2391;s:10:"DammKatKir";i:2392;s:11:"KnobProloRu";i:2393;s:4:"Thie";i:2394;s:3:"Win";i:2395;s:3:"Def";i:2396;s:8:"StrWanch";i:2397;s:7:"LatriRa";i:2398;s:2:"Si";i:2399;s:6:"DiSlSm";i:2400;s:9:"HoStelaUm";i:2401;s:4:"SwTe";i:2402;s:10:"LanToVaira";i:2403;s:6:"OzonTo";i:2404;s:6:"MirSci";i:2405;s:14:"DaisiMyeloUret";i:2406;s:12:"DigitMounUnt";i:2407;s:6:"UniUnm";i:2408;s:6:"HallUn";i:2409;s:3:"Rea";i:2410;s:13:"FrushLitRecry";i:2411;s:4:"JuNa";i:2412;s:6:"SeSuev";i:2413;s:7:"RunTetr";i:2414;s:12:"NoneRoenThio";i:2415;s:4:"Wolf";i:2416;s:9:"HetaJaPyl";i:2417;s:10:"DiapaUnder";i:2418;s:3:"Wra";i:2419;s:4:"Subp";i:2420;s:3:"Sal";i:2421;s:5:"Unmea";i:2422;s:12:"FittiQuinaSe";i:2423;s:8:"PseWicke";i:2424;s:12:"BlNondePlate";i:2425;s:8:"SigTyUnt";i:2426;s:3:"San";i:2427;s:8:"LocPerRx";i:2428;s:8:"SupUnsup";i:2429;s:6:"GraTri";i:2430;s:7:"BaldnLo";i:2431;s:9:"CruroUnej";i:2432;s:10:"LovesRehoo";i:2433;s:12:"HemeNomaOuth";i:2434;s:5:"Innox";i:2435;s:11:"KierRenTele";i:2436;s:9:"SolitStaw";i:2437;s:8:"CeMamUns";i:2438;s:11:"MysoiNeotQu";i:2439;s:10:"KatakTurns";i:2440;s:10:"DisprHymno";i:2441;s:4:"Regr";i:2442;s:9:"GlobaSynt";i:2443;s:3:"Lut";i:2444;s:3:"The";i:2445;s:3:"Rhi";i:2446;s:3:"Tym";i:2447;s:7:"MeUnman";i:2448;s:9:"ExiOmnTor";i:2449;s:12:"PerfeStiSton";i:2450;s:6:"HoPrUn";i:2451;s:10:"IrSeptaTam";i:2452;s:10:"FoParTechn";i:2453;s:4:"Spha";i:2454;s:12:"StupUnflaVie";i:2455;s:7:"LoneVan";i:2456;s:8:"MonRadio";i:2457;s:10:"HomImpSynd";i:2458;s:2:"Ch";i:2459;s:9:"EnOrPoeta";i:2460;s:10:"CoSeminSen";i:2461;s:7:"ProidVe";i:2462;s:6:"MeShor";i:2463;s:4:"Hois";i:2464;s:9:"EyeMePoli";i:2465;s:3:"Rei";i:2466;s:5:"Zoste";i:2467;s:8:"SithUnwa";i:2468;s:5:"Corre";i:2469;s:9:"SilTriUnv";i:2470;s:9:"CompMysta";i:2471;s:7:"HepMuSu";i:2472;s:6:"OpeQua";i:2473;s:4:"Rema";i:2474;s:11:"DeclMassiUn";i:2475;s:3:"Lep";i:2476;s:8:"NecrPhth";i:2477;s:4:"Trir";i:2478;s:8:"MesoWelc";i:2479;s:4:"Vulp";i:2480;s:7:"LindPhr";i:2481;s:7:"EntirMo";i:2482;s:9:"RiSlaTaun";i:2483;s:8:"PreToufi";i:2484;s:9:"PalaSovra";i:2485;s:5:"Penni";i:2486;s:12:"NonprProcRad";i:2487;s:8:"PantRogu";i:2488;s:8:"ForeScom";i:2489;s:2:"Yo";i:2490;s:9:"LiOutUnti";i:2491;s:6:"PostSt";i:2492;s:10:"SpiSyUntuc";i:2493;s:7:"AlquiRh";i:2494;s:12:"PaloSkeldTir";i:2495;s:8:"BiflaUnr";i:2496;s:9:"LacRhuWin";i:2497;s:5:"Unres";i:2498;s:7:"PoPsilo";i:2499;s:3:"Obe";i:2500;s:5:"Nonin";i:2501;s:7:"UncaUnh";i:2502;s:5:"Undiv";i:2503;s:9:"InIzMorin";i:2504;s:7:"NoPicPi";i:2505;s:13:"PediPoltThiof";i:2506;s:4:"Scap";i:2507;s:11:"CoMesorTact";i:2508;s:10:"DeepPikePr";i:2509;s:8:"OmbroQui";i:2510;s:5:"InePo";i:2511;s:10:"PaPolyzWea";i:2512;s:10:"GhiziPosne";i:2513;s:5:"Nonde";i:2514;s:10:"RachyStaph";i:2515;s:9:"NorProtRh";i:2516;s:11:"ConflElusSt";i:2517;s:9:"OliSaraTa";i:2518;s:5:"Spath";i:2519;s:9:"HetIreUnr";i:2520;s:9:"GigeHoUro";i:2521;s:6:"NonSex";i:2522;s:9:"DrawIreni";i:2523;s:7:"HacMars";i:2524;s:10:"DenRaSoros";i:2525;s:10:"HolHypSupe";i:2526;s:4:"Cred";i:2527;s:5:"StUns";i:2528;s:8:"FictWagw";i:2529;s:3:"Unc";i:2530;s:9:"ShoreWhit";i:2531;s:11:"ProRehaTras";i:2532;s:5:"UnUph";i:2533;s:10:"MoPneumPsi";i:2534;s:12:"RabbSomnUnti";i:2535;s:11:"SplenTheUnb";i:2536;s:3:"Par";i:2537;s:10:"DyaFisQuin";i:2538;s:6:"LoUncl";i:2539;s:11:"DicOvariVar";i:2540;s:8:"PreSciSc";i:2541;s:8:"HarHiMor";i:2542;s:11:"QuadrSawaSi";i:2543;s:6:"SuThim";i:2544;s:5:"Vulva";i:2545;s:3:"Hou";i:2546;s:3:"Mer";i:2547;s:8:"GiInPaga";i:2548;s:2:"Co";i:2549;s:10:"PseudRenun";i:2550;s:13:"MiskMortaTana";i:2551;s:7:"OmTerro";i:2552;s:7:"ExUnbex";i:2553;s:5:"Micro";i:2554;s:11:"DenuObsSemi";i:2555;s:11:"BursDiRenea";i:2556;s:3:"Zym";i:2557;s:9:"BunNoWitt";i:2558;s:5:"Misqu";i:2559;s:4:"LaPl";i:2560;s:6:"IndKin";i:2561;s:10:"TriUnmVent";i:2562;s:10:"MoOvePlant";i:2563;s:5:"Verbe";i:2564;s:5:"CutSc";i:2565;s:9:"UnbeVigor";i:2566;s:9:"ListUnder";i:2567;s:10:"PtilSegrSt";i:2568;s:9:"GruelNaso";i:2569;s:5:"Nudis";i:2570;s:13:"LithPhrasRest";i:2571;s:10:"HyPonUngra";i:2572;s:8:"ParPrese";i:2573;s:2:"Pu";i:2574;s:7:"SeSweUn";i:2575;s:4:"Stop";i:2576;s:11:"EntSynTechn";i:2577;s:14:"OverpTrachUneq";i:2578;s:10:"SteenUtter";i:2579;s:12:"FlorMediUnco";i:2580;s:11:"CoCyancNigg";i:2581;s:4:"Sexu";i:2582;s:11:"BignoErStom";i:2583;s:12:"BiggeMyrSupe";i:2584;s:5:"Tinti";i:2585;s:9:"PhilaSubc";i:2586;s:8:"CouPoeti";i:2587;s:13:"DockeIriMisti";i:2588;s:11:"GramOlPityp";i:2589;s:13:"DisEvoluMycod";i:2590;s:5:"Morph";i:2591;s:2:"Ou";i:2592;s:5:"ProUn";i:2593;s:4:"Dulc";i:2594;s:7:"LongaSp";i:2595;s:8:"PolPulUn";i:2596;s:8:"BuDiInco";i:2597;s:6:"ChaiDe";i:2598;s:11:"PacabUnUnsh";i:2599;s:10:"ArthrUninv";i:2600;s:6:"PyroRo";i:2601;s:7:"VulgaYi";i:2602;s:5:"EcLio";i:2603;s:11:"EranIncouOr";i:2604;s:12:"LunuModulOrd";i:2605;s:5:"Patri";i:2606;s:2:"Dr";i:2607;s:7:"RaTrans";i:2608;s:12:"CompoLipScot";i:2609;s:10:"SardoSensa";i:2610;s:12:"OverlSupWrin";i:2611;s:9:"PhotSperm";i:2612;s:7:"TimekUn";i:2613;s:5:"Pusey";i:2614;s:7:"PoQuSou";i:2615;s:6:"FreeGr";i:2616;s:4:"Nona";i:2617;s:6:"PsilUn";i:2618;s:7:"DiseSta";i:2619;s:5:"Eluso";i:2620;s:7:"EndabFr";i:2621;s:7:"EntMaTe";i:2622;s:5:"UndUn";i:2623;s:8:"FrStavUn";i:2624;s:6:"DisSyn";i:2625;s:3:"Tub";i:2626;s:11:"BescDeRenas";i:2627;s:12:"RabbiReSuper";i:2628;s:4:"Bolo";i:2629;s:10:"NonaPectPh";i:2630;s:12:"PolymRoomtUn";i:2631;s:11:"KolTypWirem";i:2632;s:7:"CroPrae";i:2633;s:7:"SubWarr";i:2634;s:6:"SteTro";i:2635;s:7:"BaForel";i:2636;s:6:"OppiWh";i:2637;s:9:"GleanSacc";i:2638;s:10:"MisliQueru";i:2639;s:8:"ScaWoodb";i:2640;s:3:"Equ";i:2641;s:10:"NeocyUnwov";i:2642;s:13:"CoacElectUnsp";i:2643;s:4:"Unch";i:2644;s:7:"BrUncom";i:2645;s:5:"Tanni";i:2646;s:10:"CuLeiOcclu";i:2647;s:3:"Soa";i:2648;s:11:"MyxoPoUnbeg";i:2649;s:12:"PhysiTrUnexi";i:2650;s:6:"InSice";i:2651;s:7:"NonPauc";i:2652;s:6:"SecuUl";i:2653;s:10:"HornOutOut";i:2654;s:6:"OpprOv";i:2655;s:10:"BaHypStich";i:2656;s:9:"HemoaIcTi";i:2657;s:4:"Zeug";i:2658;s:4:"Sphy";i:2659;s:5:"Shaka";i:2660;s:6:"PaPrSc";i:2661;s:5:"Unrel";i:2662;s:9:"ReSmintTw";i:2663;s:2:"Ku";i:2664;s:4:"Botu";i:2665;s:9:"TrulUnext";i:2666;s:9:"CoglWhite";i:2667;s:5:"Curle";i:2668;s:10:"InMinSince";i:2669;s:11:"NoRachTrans";i:2670;s:11:"RolexUnflZo";i:2671;s:7:"KoSwiUm";i:2672;s:9:"SemiUncUn";i:2673;s:7:"AdBosVe";i:2674;s:12:"CamphPePrein";i:2675;s:7:"PygmoSe";i:2676;s:7:"InMorco";i:2677;s:7:"OcOffen";i:2678;s:5:"Speck";i:2679;s:4:"Till";i:2680;s:11:"BestrUnWorc";i:2681;s:8:"OlOveSyn";i:2682;s:10:"UnsocWound";i:2683;s:6:"ReprSu";i:2684;s:7:"RemigUn";i:2685;s:12:"GeorOutmServ";i:2686;s:3:"Pac";i:2687;s:12:"LePerseSerop";i:2688;s:11:"FeverNonWav";i:2689;s:5:"BaBac";i:2690;s:9:"DragGaSan";i:2691;s:8:"UnforYar";i:2692;s:4:"Vent";i:2693;s:7:"BluSlug";i:2694;s:6:"OverSp";i:2695;s:12:"DiuHypoProto";i:2696;s:7:"ReUndes";i:2697;s:8:"PolUnimp";i:2698;s:5:"Unsmo";i:2699;s:9:"CuriaSulf";i:2700;s:11:"SyntToTreas";i:2701;s:13:"SaproSyceeTen";i:2702;s:5:"Thuoc";i:2703;s:6:"BroHol";i:2704;s:13:"MetaMothwVili";i:2705;s:10:"LeStuUnioc";i:2706;s:12:"PitheTeTrigo";i:2707;s:5:"LowMe";i:2708;s:12:"SoStahlTomme";i:2709;s:8:"PothSpTe";i:2710;s:11:"MultPhytoUn";i:2711;s:4:"Note";i:2712;s:13:"SuluSynanViol";i:2713;s:9:"SeapUnsta";i:2714;s:3:"Nin";i:2715;s:3:"Lat";i:2716;s:11:"BiopDespeEt";i:2717;s:9:"FiSuperVo";i:2718;s:5:"Cereb";i:2719;s:10:"BoNonbUnde";i:2720;s:2:"Lu";i:2721;s:10:"PrerPsyWhe";i:2722;s:11:"MeReferShee";i:2723;s:12:"GrosInvitUna";i:2724;s:8:"SarrSeri";i:2725;s:4:"Rags";i:2726;s:2:"Sm";i:2727;s:5:"Unint";i:2728;s:3:"Gut";i:2729;s:9:"InteMyPyr";i:2730;s:7:"IsoloUn";i:2731;s:5:"SiUnb";i:2732;s:10:"BlCanniEqu";i:2733;s:5:"Konar";i:2734;s:4:"Step";i:2735;s:6:"InteNa";i:2736;s:7:"KnaSubc";i:2737;s:9:"ChaUnaUnc";i:2738;s:6:"LaNeUp";i:2739;s:3:"Reb";i:2740;s:9:"DiscPePot";i:2741;s:8:"NonNonVi";i:2742;s:4:"Tata";i:2743;s:4:"Cent";i:2744;s:7:"HandPho";i:2745;s:11:"GazetMenWoa";i:2746;s:9:"PolyPredi";i:2747;s:12:"LaboPreScase";i:2748;s:7:"PaTheTh";i:2749;s:14:"DeafDiaclSides";i:2750;s:4:"Dixi";i:2751;s:8:"MiscUnpo";i:2752;s:9:"RepShaUpt";i:2753;s:13:"GaberJewdPont";i:2754;s:6:"DyoInc";i:2755;s:11:"FanaOeQuinq";i:2756;s:6:"ChThre";i:2757;s:4:"Orth";i:2758;s:9:"LaborMiSp";i:2759;s:6:"DeNavi";i:2760;s:10:"RioShSwain";i:2761;s:3:"Men";i:2762;s:4:"Ghib";i:2763;s:9:"SunpTrust";i:2764;s:5:"Feloi";i:2765;s:9:"NotorOiko";i:2766;s:5:"Laryn";i:2767;s:5:"Wishf";i:2768;s:10:"DoMultiUne";i:2769;s:4:"Unow";i:2770;s:10:"DumpiEvePy";i:2771;s:14:"OctonTwelUnman";i:2772;s:11:"NasSterXylo";i:2773;s:8:"CompOppu";i:2774;s:9:"CoalEnure";i:2775;s:4:"Suba";i:2776;s:5:"Histo";i:2777;s:12:"PaPsidiTooth";i:2778;s:5:"Tubul";i:2779;s:4:"Outg";i:2780;s:5:"HolNi";i:2781;s:2:"El";i:2782;s:11:"SpurnSuUnst";i:2783;s:3:"Soc";i:2784;s:7:"MachSyn";i:2785;s:12:"LoriPraePren";i:2786;s:7:"ProTric";i:2787;s:5:"Disre";i:2788;s:11:"IntePseuXen";i:2789;s:3:"Sei";i:2790;s:4:"Refl";i:2791;s:11:"MisomSperUn";i:2792;s:9:"RetraVaca";i:2793;s:6:"PolyRe";i:2794;s:12:"GrizzImpSnow";i:2795;s:8:"NumUroxa";i:2796;s:4:"LePa";i:2797;s:3:"Qua";i:2798;s:14:"InvolSearSemio";i:2799;s:6:"LeStan";i:2800;s:10:"SantaStaph";i:2801;s:4:"Imag";i:2802;s:9:"PreRakiSu";i:2803;s:13:"DegeTrigoUnch";i:2804;s:8:"FracUngr";i:2805;s:12:"EzrFlukShast";i:2806;s:10:"PlaneSteth";i:2807;s:8:"PresiUnd";i:2808;s:9:"BroDrivIn";i:2809;s:4:"Vice";i:2810;s:3:"Mah";i:2811;s:4:"AcXy";i:2812;s:10:"IsrMegaUst";i:2813;s:9:"PeriReaTr";i:2814;s:10:"CoOswaZami";i:2815;s:5:"JacUn";i:2816;s:9:"FaLeanPar";i:2817;s:14:"EngauInerrSpha";i:2818;s:5:"Sager";i:2819;s:13:"SteeSubwUnpry";i:2820;s:9:"RhagoStag";i:2821;s:4:"Plag";i:2822;s:4:"Sens";i:2823;s:11:"MonTylosWoo";i:2824;s:5:"Toxop";i:2825;s:4:"MiMn";i:2826;s:6:"PhlePy";i:2827;s:14:"LatoPeramUpval";i:2828;s:10:"PerStTarox";i:2829;s:5:"Thero";i:2830;s:9:"ForHaPras";i:2831;s:6:"PeTubi";i:2832;s:13:"DiffeIneVesic";i:2833;s:10:"NonveTeneb";i:2834;s:6:"TauUna";i:2835;s:14:"JeronUncuUntha";i:2836;s:5:"Telem";i:2837;s:9:"BargHaSul";i:2838;s:11:"InartMelaSt";i:2839;s:5:"Confe";i:2840;s:9:"MobPseTen";i:2841;s:4:"Obst";i:2842;s:9:"IvanPedan";i:2843;s:5:"Noctu";i:2844;s:8:"MythTiUn";i:2845;s:13:"ParlaQuadRash";i:2846;s:10:"CyOpiloPre";i:2847;s:13:"OsteUngyvUnsp";i:2848;s:5:"StZoo";i:2849;s:4:"Slid";i:2850;s:9:"ParRuVibr";i:2851;s:14:"EndoImposSubmi";i:2852;s:7:"SpiUnUn";i:2853;s:7:"PreRais";i:2854;s:9:"EpigMyxVo";i:2855;s:5:"ReUnd";i:2856;s:9:"RusSheUnm";i:2857;s:6:"PretWr";i:2858;s:3:"Hom";i:2859;s:6:"GrSini";i:2860;s:11:"ColeNonvPos";i:2861;s:3:"Tor";i:2862;s:8:"CraMonoc";i:2863;s:15:"MisfoPrestSaliv";i:2864;s:7:"NonWaho";i:2865;s:12:"GlanReliSeax";i:2866;s:10:"BrNonpUnse";i:2867;s:7:"TarocTh";i:2868;s:3:"Ora";i:2869;s:8:"GrudgPra";i:2870;s:6:"PeSnUn";i:2871;s:13:"LigniMicroUnc";i:2872;s:7:"ClogHor";i:2873;s:10:"MiserRebUn";i:2874;s:10:"EntSpeSple";i:2875;s:10:"ColleGuard";i:2876;s:5:"Totte";i:2877;s:8:"IsataTax";i:2878;s:8:"NonWorra";i:2879;s:5:"Unsha";i:2880;s:10:"EuStanxUne";i:2881;s:3:"Pin";i:2882;s:4:"MoSe";i:2883;s:5:"WeaWh";i:2884;s:7:"ProviRa";i:2885;s:11:"PreTibiUnin";i:2886;s:11:"MyeloUnsUnt";i:2887;s:9:"InspiStee";i:2888;s:5:"Sensu";i:2889;s:9:"StufSwerv";i:2890;s:2:"Sy";i:2891;s:11:"GunnHaiTrun";i:2892;s:7:"NeopPre";i:2893;s:11:"BitMooncTip";i:2894;s:4:"Unso";i:2895;s:12:"MajPhlebSpin";i:2896;s:5:"Furil";i:2897;s:5:"Sledf";i:2898;s:10:"LadylMaSac";i:2899;s:10:"ChMylioSen";i:2900;s:9:"IxParaUre";i:2901;s:9:"DoMedalUn";i:2902;s:6:"OuUnsa";i:2903;s:8:"DouNonax";i:2904;s:4:"Univ";i:2905;s:4:"Moly";i:2906;s:9:"MonitPeda";i:2907;s:9:"CelaLunul";i:2908;s:8:"AnDenaMe";i:2909;s:6:"IrTerr";i:2910;s:3:"Usu";i:2911;s:7:"UnfoUnv";i:2912;s:7:"JudMono";i:2913;s:14:"PentRagsoUnimb";i:2914;s:9:"ReTransWh";i:2915;s:14:"EquisHornsInco";i:2916;s:12:"HukxOligiSup";i:2917;s:9:"PhSpSuper";i:2918;s:11:"SemUndaUnpr";i:2919;s:4:"Self";i:2920;s:11:"FleeMatePro";i:2921;s:6:"SupWor";i:2922;s:5:"Unben";i:2923;s:9:"MemorTach";i:2924;s:6:"SegUns";i:2925;s:10:"CoDisplSod";i:2926;s:7:"EleSple";i:2927;s:8:"PolyUror";i:2928;s:3:"Yal";i:2929;s:5:"JiTri";i:2930;s:10:"TaiThyroUn";i:2931;s:4:"Wath";i:2932;s:7:"EquiRep";i:2933;s:10:"ShimSubUnd";i:2934;s:6:"HasSan";i:2935;s:5:"HemUt";i:2936;s:4:"Spiv";i:2937;s:10:"MunycPhary";i:2938;s:8:"LargOver";i:2939;s:6:"OveSab";i:2940;s:14:"AulosLarxTraga";i:2941;s:9:"DePubesZa";i:2942;s:9:"LutSeYaki";i:2943;s:8:"PoSphaTh";i:2944;s:3:"Igl";i:2945;s:5:"Contr";i:2946;s:10:"PolysRamSu";i:2947;s:5:"Unack";i:2948;s:10:"AnCapsHyda";i:2949;s:6:"PoeSli";i:2950;s:6:"SausUn";i:2951;s:12:"PostgStaghWi";i:2952;s:8:"TetrTita";i:2953;s:14:"MendaPostScran";i:2954;s:9:"CharaSeag";i:2955;s:8:"HexSpodi";i:2956;s:5:"Quadr";i:2957;s:8:"SubpSwea";i:2958;s:9:"DoNicoTer";i:2959;s:7:"MilliSl";i:2960;s:13:"HelpPneumSlim";i:2961;s:6:"ToeUrb";i:2962;s:10:"DiMaionVen";i:2963;s:7:"PestePo";i:2964;s:7:"LiyPura";i:2965;s:10:"SparlTrian";i:2966;s:5:"BraGa";i:2967;s:7:"FrontRe";i:2968;s:5:"Unall";i:2969;s:5:"Diape";i:2970;s:8:"EscIdiot";i:2971;s:5:"Salam";i:2972;s:13:"CommHypeInest";i:2973;s:4:"Wors";i:2974;s:7:"ReinfSa";i:2975;s:8:"ResciTre";i:2976;s:10:"PiStaurSup";i:2977;s:8:"PorraUpl";i:2978;s:8:"StocUnki";i:2979;s:4:"IsSe";i:2980;s:11:"SattTaTrans";i:2981;s:3:"Mel";i:2982;s:11:"UnanUnUnexp";i:2983;s:5:"Green";i:2984;s:13:"SokemStulWape";i:2985;s:6:"TacoVe";i:2986;s:5:"Coral";i:2987;s:13:"ChoroDipicShr";i:2988;s:11:"PseSterTepe";i:2989;s:6:"OverRe";i:2990;s:10:"FreehMonPo";i:2991;s:12:"KreiLocaPlas";i:2992;s:9:"HurMorPty";i:2993;s:10:"DaSeqTirre";i:2994;s:13:"HypsiReproUre";i:2995;s:5:"Subno";i:2996;s:5:"Outpo";i:2997;s:10:"AndSevenVo";i:2998;s:5:"Vehmi";i:2999;s:3:"Vet";i:3000;s:5:"Proto";i:3001;s:5:"Unbri";i:3002;s:4:"Spie";i:3003;s:11:"ClaColobWhe";i:3004;s:4:"MeSy";i:3005;s:7:"ForSuXy";i:3006;s:11:"ChaKukPrior";i:3007;s:5:"Draco";i:3008;s:5:"Stair";i:3009;s:3:"Len";i:3010;s:4:"Saga";i:3011;s:9:"PyrroReTy";i:3012;s:4:"Nigg";i:3013;s:6:"ProSki";i:3014;s:8:"AutoPeri";i:3015;s:12:"NitroPieTric";i:3016;s:5:"StaTu";i:3017;s:3:"Rec";i:3018;s:4:"Pall";i:3019;s:12:"PreceShamUpl";i:3020;s:7:"CoviWag";i:3021;s:6:"ImTran";i:3022;s:13:"MandaStageUnf";i:3023;s:9:"SemisXero";i:3024;s:3:"Eso";i:3025;s:7:"FumatTr";i:3026;s:4:"Weev";i:3027;s:8:"IntraOst";i:3028;s:11:"GaNeurUngel";i:3029;s:7:"UnUnUnp";i:3030;s:5:"SiTan";i:3031;s:5:"FlPhy";i:3032;s:10:"GmeRubWarl";i:3033;s:9:"CayapTerm";i:3034;s:10:"MoRefinVau";i:3035;s:5:"Phosp";i:3036;s:12:"EligiHexUnkn";i:3037;s:6:"OvUnor";i:3038;s:5:"Verbi";i:3039;s:4:"Wind";i:3040;s:6:"BoroPh";i:3041;s:12:"MesoRudSinec";i:3042;s:10:"DisHemaJam";i:3043;s:8:"ReSiphSt";i:3044;s:4:"Pret";i:3045;s:4:"Pros";i:3046;s:8:"GlaObstu";i:3047;s:8:"JouPaPip";i:3048;s:8:"PunUndef";i:3049;s:5:"Ultra";i:3050;s:3:"Pur";i:3051;s:12:"MisMockxNomo";i:3052;s:12:"TactThacUnhe";i:3053;s:8:"PostSund";i:3054;s:9:"QuadrSavi";i:3055;s:7:"RecTepi";i:3056;s:12:"RutteTeUnplu";i:3057;s:6:"EncPar";i:3058;s:6:"ElSexu";i:3059;s:5:"MonSe";i:3060;s:4:"Velo";i:3061;s:10:"IthaTropUn";i:3062;s:9:"DuffTaute";i:3063;s:9:"ThesaTrUn";i:3064;s:5:"Unson";i:3065;s:14:"HumisKensiPanp";i:3066;s:9:"NectShTil";i:3067;s:4:"Pais";i:3068;s:8:"FeatSemi";i:3069;s:3:"Scu";i:3070;s:11:"DispePhWavy";i:3071;s:9:"ReSexivSp";i:3072;s:12:"NonfeNonlaPh";i:3073;s:9:"CuForYipx";i:3074;s:5:"DiHap";i:3075;s:11:"OversPhytSp";i:3076;s:8:"OrthScor";i:3077;s:3:"Ofo";i:3078;s:6:"MyoPic";i:3079;s:9:"CarLeNaos";i:3080;s:8:"PalmSati";i:3081;s:9:"SpokUnimo";i:3082;s:5:"Gorin";i:3083;s:9:"DyOligUnc";i:3084;s:9:"PhorSpaeb";i:3085;s:9:"ParaSumpt";i:3086;s:11:"CloConDidym";i:3087;s:11:"MenUnaWanto";i:3088;s:5:"Tetra";i:3089;s:7:"PreTurn";i:3090;s:8:"SliTitiv";i:3091;s:8:"HowisUns";i:3092;s:12:"MemPlantUnce";i:3093;s:11:"OstePindSuc";i:3094;s:12:"NonPhycSpect";i:3095;s:4:"Pogo";i:3096;s:2:"Ex";i:3097;s:4:"SaSt";i:3098;s:14:"SeedxSheexSupe";i:3099;s:5:"Fring";i:3100;s:13:"ExcoUndefUnev";i:3101;s:6:"IrRamx";i:3102;s:3:"Rab";i:3103;s:2:"Oe";i:3104;s:8:"UnevZhmu";i:3105;s:5:"OutPr";i:3106;s:3:"Ger";i:3107;s:6:"MesVer";i:3108;s:9:"FuchTidel";i:3109;s:10:"ChRetUnter";i:3110;s:4:"Mall";i:3111;s:10:"ReddySanda";i:3112;s:3:"Ido";i:3113;s:12:"FonSylviUnha";i:3114;s:10:"SamsoTrans";i:3115;s:6:"OrchTe";i:3116;s:10:"NotSemUnse";i:3117;s:4:"Ultr";i:3118;s:4:"PoUn";i:3119;s:8:"TransVal";i:3120;s:8:"FuciGlob";i:3121;s:7:"ScotTet";i:3122;s:15:"RicheRonsdWindo";i:3123;s:5:"Steel";i:3124;s:5:"Semif";i:3125;s:6:"SupUpw";i:3126;s:4:"Bedp";i:3127;s:13:"DagDispiUnsen";i:3128;s:3:"Coa";i:3129;s:12:"JetPedalSego";i:3130;s:4:"Lion";i:3131;s:11:"CymSnipjTeu";i:3132;s:4:"Prop";i:3133;s:7:"NeuroYi";i:3134;s:9:"MasMeTrom";i:3135;s:10:"EthylLoRef";i:3136;s:10:"CombDogSpi";i:3137;s:8:"FrLoMidm";i:3138;s:4:"Myog";i:3139;s:9:"PrScleSub";i:3140;s:13:"RefTuboaUnsor";i:3141;s:9:"EvasiStei";i:3142;s:9:"SpUnValix";i:3143;s:5:"Spira";i:3144;s:4:"EuPs";i:3145;s:9:"HardPenna";i:3146;s:5:"Wonde";i:3147;s:12:"NeonVocXipho";i:3148;s:10:"ChalJuckRe";i:3149;s:8:"DeopMadd";i:3150;s:4:"Grea";i:3151;s:3:"Tik";i:3152;s:5:"SuTar";i:3153;s:9:"LimacScTo";i:3154;s:13:"OverpPenxUnci";i:3155;s:5:"Rimpi";i:3156;s:4:"PySa";i:3157;s:10:"HydLamaiSq";i:3158;s:12:"HyReserSpiro";i:3159;s:11:"BeaBothDrif";i:3160;s:10:"PlRhiSikar";i:3161;s:4:"Olid";i:3162;s:7:"DiSperm";i:3163;s:4:"Prel";i:3164;s:5:"Mioce";i:3165;s:5:"Nonim";i:3166;s:5:"Prize";i:3167;s:7:"StasVul";i:3168;s:10:"PreliWoods";i:3169;s:3:"Rif";i:3170;s:3:"Bur";i:3171;s:13:"PleurUncluVes";i:3172;s:6:"RefSam";i:3173;s:8:"TickeXer";i:3174;s:4:"Unla";i:3175;s:7:"HyPelec";i:3176;s:12:"ForHumbuMalm";i:3177;s:9:"FuOrbitPh";i:3178;s:7:"FerFoRe";i:3179;s:4:"CoVe";i:3180;s:6:"NuncSu";i:3181;s:7:"CuUnfor";i:3182;s:11:"DemoPerStru";i:3183;s:6:"ReevUn";i:3184;s:9:"UndeWhelk";i:3185;s:4:"FuTu";i:3186;s:5:"Ithom";i:3187;s:5:"Sopex";i:3188;s:7:"InSpeci";i:3189;s:5:"Nontu";i:3190;s:6:"HomPre";i:3191;s:10:"InteMyceSu";i:3192;s:9:"CelLoSlac";i:3193;s:4:"Piny";i:3194;s:13:"HeterSpionTur";i:3195;s:4:"Slot";i:3196;s:8:"MetPresh";i:3197;s:13:"DisiNonnuVert";i:3198;s:9:"ArOdonWhi";i:3199;s:8:"InInteMe";i:3200;s:4:"Weat";i:3201;s:9:"GaUnsupUn";i:3202;s:7:"PrisoSh";i:3203;s:10:"IndiLycMol";i:3204;s:7:"PaRehUn";i:3205;s:7:"PollPro";i:3206;s:3:"Gro";i:3207;s:4:"Past";i:3208;s:10:"LambsPanty";i:3209;s:9:"ImpliProc";i:3210;s:5:"Cried";i:3211;s:6:"CycaUn";i:3212;s:14:"KeaxScreeUnlet";i:3213;s:8:"NundRevo";i:3214;s:12:"MadSherWeebl";i:3215;s:2:"Hi";i:3216;s:11:"RajaUnWokex";i:3217;s:12:"PseudRiviSte";i:3218;s:7:"FlaPaUn";i:3219;s:3:"Tax";i:3220;s:4:"PhPu";i:3221;s:11:"GraPsammStr";i:3222;s:14:"CelluEquiRailb";i:3223;s:7:"CongKer";i:3224;s:7:"MagnePs";i:3225;s:13:"NovelSeqSignl";i:3226;s:10:"DauncFoute";i:3227;s:11:"ArianPucRig";i:3228;s:10:"AlaCapThir";i:3229;s:7:"ConteDe";i:3230;s:12:"RupRuskySpri";i:3231;s:8:"FreUncon";i:3232;s:8:"HxLonYvo";i:3233;s:9:"PeriaStag";i:3234;s:2:"Op";i:3235;s:6:"LibiVa";i:3236;s:7:"PouTumu";i:3237;s:5:"Unerr";i:3238;s:7:"OblOrch";i:3239;s:8:"CountUng";i:3240;s:10:"NordiUneas";i:3241;s:9:"CtOrtSail";i:3242;s:5:"Roomm";i:3243;s:4:"Fant";i:3244;s:3:"Gan";i:3245;s:6:"PeUnle";i:3246;s:8:"MuPolRok";i:3247;s:9:"DrawHyper";i:3248;s:13:"EpiMaywiNotho";i:3249;s:7:"LySaxTe";i:3250;s:6:"NoncRh";i:3251;s:6:"FoUnsh";i:3252;s:3:"Sym";i:3253;s:11:"DiaOveThraw";i:3254;s:8:"CycloSem";i:3255;s:8:"PanjaThe";i:3256;s:2:"Ri";i:3257;s:5:"SuUnm";i:3258;s:9:"ArCoPirop";i:3259;s:9:"GoffLibMa";i:3260;s:10:"InsepTalpa";i:3261;s:4:"Steg";i:3262;s:5:"Renai";i:3263;s:9:"SubUnUnwi";i:3264;s:9:"PerPersQu";i:3265;s:5:"DisHo";i:3266;s:4:"Test";i:3267;s:8:"OpenhShi";i:3268;s:4:"SaTe";i:3269;s:5:"SarVe";i:3270;s:8:"PrSpVisi";i:3271;s:9:"KeLoloxRe";i:3272;s:3:"Exe";i:3273;s:4:"Plet";i:3274;s:3:"Uno";i:3275;s:5:"Overr";i:3276;s:7:"MyQuadr";i:3277;s:8:"MacSemSo";i:3278;s:11:"EgghoFeThur";i:3279;s:9:"GaNeTickl";i:3280;s:10:"CafEnlUnpr";i:3281;s:7:"CroScap";i:3282;s:12:"EspaNonmValv";i:3283;s:7:"ParaUnb";i:3284;s:3:"Tal";i:3285;s:9:"CysDitNon";i:3286;s:5:"Unsan";i:3287;s:5:"GlyRe";i:3288;s:4:"Tent";i:3289;s:3:"Bio";i:3290;s:9:"MeSpielUn";i:3291;s:12:"GrufRewoUnch";i:3292;s:5:"EumSe";i:3293;s:3:"Mog";i:3294;s:7:"SoUnswa";i:3295;s:9:"RidSpXero";i:3296;s:4:"Pock";i:3297;s:5:"Steno";i:3298;s:9:"EndMiliVi";i:3299;s:6:"EvocRh";i:3300;s:8:"JoQuisUn";i:3301;s:4:"Gemm";i:3302;s:7:"PinSoma";i:3303;s:6:"JubMul";i:3304;s:10:"BecivBrent";i:3305;s:9:"JaunSynco";i:3306;s:10:"KenMalSure";i:3307;s:8:"SeTriaWo";i:3308;s:9:"ScTriUpal";i:3309;s:10:"FeMycoZucc";i:3310;s:7:"FeUntru";i:3311;s:10:"ScathWeath";i:3312;s:5:"Knubb";i:3313;s:5:"OvUnu";i:3314;s:11:"DoNonliUnre";i:3315;s:5:"Relie";i:3316;s:4:"Must";i:3317;s:4:"PaRe";i:3318;s:2:"Il";i:3319;s:9:"DartPaTat";i:3320;s:5:"KilPr";i:3321;s:4:"DoMi";i:3322;s:7:"ScoSuff";i:3323;s:5:"PirSi";i:3324;s:4:"Rota";i:3325;s:3:"Wad";i:3326;s:14:"InfaMesolMonoh";i:3327;s:7:"RigidUn";i:3328;s:10:"RosSimilUn";i:3329;s:3:"Thr";i:3330;s:9:"DecolDupl";i:3331;s:11:"DialGlazePr";i:3332;s:6:"FunUnt";i:3333;s:6:"ScSemi";i:3334;s:4:"Elec";i:3335;s:14:"HeauLargeUpsta";i:3336;s:7:"SpUptra";i:3337;s:3:"Sno";i:3338;s:7:"NoNontr";i:3339;s:7:"DubTurb";i:3340;s:9:"MilkSupra";i:3341;s:7:"PhiPySu";i:3342;s:14:"NightPerioRect";i:3343;s:4:"EuMe";i:3344;s:12:"FuliLutePros";i:3345;s:12:"AutDeroEyoty";i:3346;s:10:"HulPrusSpu";i:3347;s:7:"PatWeez";i:3348;s:5:"Fragm";i:3349;s:6:"NuTigx";i:3350;s:6:"InfUlt";i:3351;s:6:"CoSubj";i:3352;s:8:"GreHyLea";i:3353;s:6:"CypsEm";i:3354;s:4:"Unsa";i:3355;s:5:"GuPro";i:3356;s:4:"Rotu";i:3357;s:5:"DoEmb";i:3358;s:3:"Pia";i:3359;s:7:"DrMoroc";i:3360;s:7:"LeOpsim";i:3361;s:5:"Sextu";i:3362;s:13:"EpipRecaShoop";i:3363;s:9:"ExaraSiVe";i:3364;s:12:"PreasSooUndi";i:3365;s:6:"FroInv";i:3366;s:7:"MorthPa";i:3367;s:3:"Wri";i:3368;s:7:"OverTet";i:3369;s:3:"Yea";i:3370;s:4:"Sand";i:3371;s:11:"DemonDiManq";i:3372;s:5:"MoiTi";i:3373;s:8:"ForsHype";i:3374;s:8:"HaloMont";i:3375;s:11:"InvPacUnexh";i:3376;s:12:"SilvUnaggWir";i:3377;s:8:"MuSemSen";i:3378;s:4:"Disp";i:3379;s:6:"ProUnq";i:3380;s:10:"ImploVenet";i:3381;s:4:"Hors";i:3382;s:5:"LucTh";i:3383;s:9:"ObliTibio";i:3384;s:4:"Homo";i:3385;s:12:"MonoSabSelen";i:3386;s:7:"NonSubm";i:3387;s:4:"Seri";i:3388;s:8:"CypriTeh";i:3389;s:8:"LocSewer";i:3390;s:12:"PiliTubUnmor";i:3391;s:5:"Beami";i:3392;s:8:"HyRegRep";i:3393;s:7:"SmUnrou";i:3394;s:7:"HeNoUnd";i:3395;s:8:"CyResSob";i:3396;s:9:"ElecaMocm";i:3397;s:5:"Quini";i:3398;s:10:"TegeaUnsca";i:3399;s:10:"ScorSeTrip";i:3400;s:8:"OligSeTh";i:3401;s:9:"HuRooUnde";i:3402;s:5:"Razor";i:3403;s:4:"Uncl";i:3404;s:6:"ScriSu";i:3405;s:4:"Unpu";i:3406;s:8:"RomneSin";i:3407;s:4:"Supe";i:3408;s:5:"Subor";i:3409;s:10:"PhotoProSt";i:3410;s:5:"MaVer";i:3411;s:8:"GrImToba";i:3412;s:10:"PaRetUnfor";i:3413;s:9:"PanPriaRe";i:3414;s:8:"FlokiPht";i:3415;s:6:"PreSol";i:3416;s:8:"QuiniVar";i:3417;s:12:"BurnRetSphae";i:3418;s:7:"GondSpe";i:3419;s:5:"Pseud";i:3420;s:7:"MeRespe";i:3421;s:5:"OtTur";i:3422;s:7:"OuPedRe";i:3423;s:10:"CeraNoSara";i:3424;s:7:"EpiFiRe";i:3425;s:14:"SulphThallTwee";i:3426;s:7:"OuRhema";i:3427;s:4:"Fanc";i:3428;s:8:"OoscoWay";i:3429;s:14:"ComfoHomodHypo";i:3430;s:12:"ImTapisUnfit";i:3431;s:4:"Laix";i:3432;s:5:"Unfes";i:3433;s:2:"Bu";i:3434;s:7:"EtReoTo";i:3435;s:9:"SnippYard";i:3436;s:5:"SaUnd";i:3437;s:5:"Osteo";i:3438;s:8:"PageaUnd";i:3439;s:8:"InhNonUn";i:3440;s:14:"MiscPilasSilic";i:3441;s:9:"OvePnStyc";i:3442;s:8:"BaNoncTo";i:3443;s:5:"Super";i:3444;s:6:"IrSeUn";i:3445;s:9:"CountEchi";i:3446;s:5:"BliRo";i:3447;s:11:"KeMoveaUnha";i:3448;s:15:"CratcSeamrSuper";i:3449;s:4:"Mist";i:3450;s:4:"Wrec";i:3451;s:7:"CenNons";i:3452;s:5:"Confr";i:3453;s:9:"IndecUnal";i:3454;s:5:"Groov";i:3455;s:5:"Nonre";i:3456;s:12:"OuthiSoSuper";i:3457;s:6:"HeSubc";i:3458;s:15:"MetriScuftTable";i:3459;s:5:"Satie";i:3460;s:12:"IncoOrphaSqu";i:3461;s:4:"HyTh";i:3462;s:10:"DirepGiMoi";i:3463;s:3:"Mor";i:3464;s:5:"PaPho";i:3465;s:6:"TavTol";i:3466;s:9:"IsthmNapp";i:3467;s:6:"CopSpl";i:3468;s:6:"BuTren";i:3469;s:11:"PrepUncrUnp";i:3470;s:3:"Mis";i:3471;s:5:"Twitt";i:3472;s:5:"Koiar";i:3473;s:10:"BuHirTetra";i:3474;s:7:"DipiWis";i:3475;s:5:"BasGh";i:3476;s:11:"DeziPreRune";i:3477;s:5:"PerPr";i:3478;s:5:"Scrol";i:3479;s:9:"CenSapSna";i:3480;s:12:"ObraOchleTyk";i:3481;s:6:"AreLan";i:3482;s:10:"MeniNoProa";i:3483;s:4:"Wadm";i:3484;s:10:"LaMotleUnd";i:3485;s:7:"CoSurfe";i:3486;s:5:"KaUnm";i:3487;s:5:"IlSim";i:3488;s:5:"Plero";i:3489;s:9:"LanceSele";i:3490;s:2:"Ro";i:3491;s:11:"DrawsSecuVe";i:3492;s:4:"Vine";i:3493;s:5:"Mythm";i:3494;s:8:"DiJesuKo";i:3495;s:9:"CorLiScut";i:3496;s:5:"ObeOr";i:3497;s:9:"PreStouTe";i:3498;s:8:"SubUnfor";i:3499;s:11:"DiEtonThala";i:3500;s:13:"FamOrallSuper";i:3501;s:5:"Unrib";i:3502;s:7:"PrReUno";i:3503;s:11:"MichTricWai";i:3504;s:8:"OveUtric";i:3505;s:5:"Propa";i:3506;s:10:"PolycVikin";i:3507;s:9:"DivMeduTh";i:3508;s:13:"LuxuTranViper";i:3509;s:8:"DiFrHypo";i:3510;s:13:"MagniMontWres";i:3511;s:10:"CantDeUnci";i:3512;s:10:"PeScripUnf";i:3513;s:10:"ClaitSpUnc";i:3514;s:10:"DecoPhoSup";i:3515;s:5:"InSys";i:3516;s:13:"RhetoUdoUnder";i:3517;s:4:"Marr";i:3518;s:5:"LaRed";i:3519;s:10:"KilolPiUns";i:3520;s:5:"Monoc";i:3521;s:13:"GalliUreViato";i:3522;s:3:"Mac";i:3523;s:9:"SadhScamm";i:3524;s:11:"PhoenSinUns";i:3525;s:8:"EpiUnjud";i:3526;s:8:"JasMonTu";i:3527;s:8:"ParWangx";i:3528;s:9:"AverCaste";i:3529;s:11:"BindMonoUnm";i:3530;s:3:"Sph";i:3531;s:5:"Postf";i:3532;s:5:"Oligo";i:3533;s:10:"ComexRodin";i:3534;s:4:"ThWe";i:3535;s:5:"Unfra";i:3536;s:10:"PleurUnaug";i:3537;s:8:"DepeNeSe";i:3538;s:10:"DiProRaddl";i:3539;s:7:"FeThere";i:3540;s:10:"HenOverpSi";i:3541;s:5:"Epido";i:3542;s:5:"GarTr";i:3543;s:7:"HypThZi";i:3544;s:4:"ReTw";i:3545;s:5:"Trans";i:3546;s:8:"AfReSchi";i:3547;s:5:"Dacry";i:3548;s:12:"QuartTruncWi";i:3549;s:10:"DishoFooti";i:3550;s:12:"ChasInvLutec";i:3551;s:4:"Whip";i:3552;s:5:"MaWin";i:3553;s:5:"HyUns";i:3554;s:11:"EmEvangPoly";i:3555;s:5:"Submo";i:3556;s:4:"PaSw";i:3557;s:6:"DipLac";i:3558;s:7:"MegRemo";i:3559;s:12:"ExtraHymeSec";i:3560;s:7:"OutlePl";i:3561;s:12:"KhasRanjUnle";i:3562;s:10:"RhRounxSan";i:3563;s:4:"Thea";i:3564;s:12:"GraRelatUnri";i:3565;s:4:"Snar";i:3566;s:4:"SoTh";i:3567;s:5:"Undel";i:3568;s:9:"GazHydSem";i:3569;s:11:"CatCopiUnho";i:3570;s:5:"Cerol";i:3571;s:6:"MisrTa";i:3572;s:11:"IntJouLiqui";i:3573;s:3:"Sec";i:3574;s:8:"UnrowVil";i:3575;s:8:"LocoThTh";i:3576;s:10:"HandmUngue";i:3577;s:2:"Is";i:3578;s:11:"RefUterXant";i:3579;s:5:"Canid";i:3580;s:7:"InIsote";i:3581;s:7:"PeUncor";i:3582;s:5:"HoWin";i:3583;s:9:"DisadNoti";i:3584;s:13:"GuttMaeaRamma";i:3585;s:4:"UnWa";i:3586;s:13:"EpiMerrySexua";i:3587;s:7:"FlunkSu";i:3588;s:4:"Solv";i:3589;s:8:"MiNeOnag";i:3590;s:4:"Nibo";i:3591;s:11:"FlocHydMuri";i:3592;s:9:"CaJozyOle";i:3593;s:9:"FemeResti";i:3594;s:7:"PhRocce";i:3595;s:4:"Urun";i:3596;s:9:"DrakeFrum";i:3597;s:11:"InsaMaOctin";i:3598;s:8:"PertuTea";i:3599;s:9:"MisOuriPh";i:3600;s:12:"HeSeaweWretc";i:3601;s:4:"MaPo";i:3602;s:9:"PancQuidd";i:3603;s:2:"Wh";i:3604;s:7:"SmugSol";i:3605;s:4:"Tris";i:3606;s:5:"CeInf";i:3607;s:7:"OphiTeg";i:3608;s:6:"FoFrem";i:3609;s:10:"HabilStore";i:3610;s:3:"Ste";i:3611;s:3:"Vei";i:3612;s:6:"HetmSy";i:3613;s:13:"RivoSpinoZoop";i:3614;s:7:"GyrMiSo";i:3615;s:11:"MeNoncPathi";i:3616;s:10:"DerOrPobsx";i:3617;s:5:"Volti";i:3618;s:9:"PoacUnrep";i:3619;s:9:"ExacSnaUn";i:3620;s:3:"Sce";i:3621;s:7:"DisSple";i:3622;s:11:"KiMonoUnaro";i:3623;s:4:"Tall";i:3624;s:12:"MyelOldSheet";i:3625;s:3:"Con";i:3626;s:14:"CollExpedPreci";i:3627;s:11:"InfecNevYol";i:3628;s:8:"ExHyTomf";i:3629;s:3:"Shi";i:3630;s:2:"Na";i:3631;s:11:"ErGownsMous";i:3632;s:7:"ScUtsuk";i:3633;s:9:"IsoQuRecr";i:3634;s:6:"ChaHon";i:3635;s:11:"EquIconoNep";i:3636;s:10:"TuitiUnsin";i:3637;s:11:"FoxMooUnpor";i:3638;s:4:"Synt";i:3639;s:7:"HemNerv";i:3640;s:5:"Wroth";i:3641;s:7:"UnZirba";i:3642;s:10:"HeterThUnm";i:3643;s:8:"ReUnraVo";i:3644;s:7:"PissaRo";i:3645;s:9:"DiFretSix";i:3646;s:8:"RabUnpro";i:3647;s:12:"InteIrksLipo";i:3648;s:10:"ByrSumpUnp";i:3649;s:2:"Oo";i:3650;s:13:"AntimDaggPseu";i:3651;s:12:"SuVaginVerbe";i:3652;s:5:"InLar";i:3653;s:5:"Fibro";i:3654;s:10:"FoldMeUnab";i:3655;s:12:"BiPrediProar";i:3656;s:11:"SyphThrasTr";i:3657;s:11:"ExNitroPala";i:3658;s:7:"DegrPre";i:3659;s:13:"ExormFeudxTer";i:3660;s:7:"SuTraUn";i:3661;s:7:"NyaSche";i:3662;s:7:"EntoGer";i:3663;s:9:"MacrTrans";i:3664;s:6:"DenoGn";i:3665;s:12:"MultiUnbarUn";i:3666;s:10:"InsLipPenc";i:3667;s:10:"JanTyroWhi";i:3668;s:10:"HalHydruMo";i:3669;s:3:"Irr";i:3670;s:13:"SkaffSubTriad";i:3671;s:5:"Windr";i:3672;s:5:"Unebb";i:3673;s:5:"RevWi";i:3674;s:7:"MajTend";i:3675;s:7:"PreUnbe";i:3676;s:12:"MyoOutscPala";i:3677;s:10:"DrainIlUnm";i:3678;s:13:"RepeSaxxUndis";i:3679;s:11:"ImidMuPsila";i:3680;s:7:"DopLuci";i:3681;s:7:"DecTezx";i:3682;s:9:"ToothViri";i:3683;s:14:"MagneRaspiTach";i:3684;s:10:"SluSwYalix";i:3685;s:10:"PhanSyUnel";i:3686;s:8:"HuReSpec";i:3687;s:4:"Pitc";i:3688;s:8:"HiHyShor";i:3689;s:12:"LieNonacPseu";i:3690;s:13:"KnaOinomSerri";i:3691;s:6:"EntUph";i:3692;s:9:"OdoSaraSu";i:3693;s:5:"Varis";i:3694;s:6:"CorOrp";i:3695;s:9:"BlNocSpli";i:3696;s:7:"SwomWis";i:3697;s:12:"PanscResWigw";i:3698;s:6:"RepTin";i:3699;s:5:"Chitc";i:3700;s:6:"InquMi";i:3701;s:6:"InWhin";i:3702;s:7:"HerTabo";i:3703;s:7:"RackfTu";i:3704;s:10:"MediRabbUn";i:3705;s:12:"CauloExospIn";i:3706;s:11:"HydroSiUnch";i:3707;s:8:"CyPrRect";i:3708;s:7:"AllBand";i:3709;s:8:"HystMail";i:3710;s:12:"HypLooseSole";i:3711;s:10:"CoFreckNon";i:3712;s:10:"AnEffeTali";i:3713;s:10:"DoctrPerit";i:3714;s:5:"SiUna";i:3715;s:4:"Rhiz";i:3716;s:4:"OuRe";i:3717;s:4:"Hobb";i:3718;s:3:"Cer";i:3719;s:9:"AnOrangRa";i:3720;s:9:"SoulfSpZo";i:3721;s:11:"UnreUnsVeto";i:3722;s:5:"HyTec";i:3723;s:9:"AtPhotUns";i:3724;s:4:"Lune";i:3725;s:3:"Tro";i:3726;s:8:"PhagoPim";i:3727;s:9:"NagNoUnde";i:3728;s:9:"FezzeMike";i:3729;s:10:"RiffxVersa";i:3730;s:8:"SigSusan";i:3731;s:7:"OverRei";i:3732;s:10:"NonOverPer";i:3733;s:11:"CoundSpriTc";i:3734;s:5:"Bismu";i:3735;s:9:"ClefThrea";i:3736;s:9:"PinkTetra";i:3737;s:5:"Therm";i:3738;s:5:"PeXyl";i:3739;s:5:"SaSma";i:3740;s:15:"PerinTransUnart";i:3741;s:5:"Pytha";i:3742;s:7:"SyUngen";i:3743;s:9:"BeMonotPa";i:3744;s:11:"DiDictyMoto";i:3745;s:11:"PolyTrWarbl";i:3746;s:9:"FaForeHor";i:3747;s:4:"Inkl";i:3748;s:5:"Perfr";i:3749;s:8:"FunMeRep";i:3750;s:10:"PrePrSkunk";i:3751;s:6:"DoHepa";i:3752;s:12:"NonParoPhilo";i:3753;s:7:"GroUnre";i:3754;s:9:"PillaUnwa";i:3755;s:10:"CixoxSocTh";i:3756;s:6:"HypPat";i:3757;s:15:"TautoTreacUnhar";i:3758;s:4:"Cant";i:3759;s:7:"DisUnsa";i:3760;s:15:"InconMilleWenon";i:3761;s:13:"EmbryNonacPre";i:3762;s:5:"Nonfl";i:3763;s:12:"RegaTripUngr";i:3764;s:9:"ScratWarn";i:3765;s:6:"BiGluc";i:3766;s:7:"BaPolyp";i:3767;s:11:"NovaReskSto";i:3768;s:4:"Utte";i:3769;s:6:"PlaPle";i:3770;s:9:"HematMona";i:3771;s:8:"OphZoono";i:3772;s:10:"StaidUnpre";i:3773;s:9:"DispoIntr";i:3774;s:7:"JeluJet";i:3775;s:4:"Four";i:3776;s:10:"IntMycoUpl";i:3777;s:5:"Skulk";i:3778;s:11:"LepPoodlSer";i:3779;s:13:"OxyrhSpiSquar";i:3780;s:7:"ExHulPa";i:3781;s:5:"SaSod";i:3782;s:15:"PretySweepSweet";i:3783;s:11:"GryMystSter";i:3784;s:5:"RuSmo";i:3785;s:7:"SuperUn";i:3786;s:8:"PolStorm";i:3787;s:8:"IncliPac";i:3788;s:5:"Prepr";i:3789;s:8:"MeckeMeg";i:3790;s:5:"Unobj";i:3791;s:3:"Gra";i:3792;s:9:"OwerlPrTe";i:3793;s:9:"BrShaVesi";i:3794;s:5:"Thaum";i:3795;s:2:"Ki";i:3796;s:7:"HeUngui";i:3797;s:4:"Sout";i:3798;s:4:"Ross";i:3799;s:8:"EyeWhips";i:3800;s:10:"HeIatroUnp";i:3801;s:5:"MePro";i:3802;s:5:"Fault";i:3803;s:9:"MutaSabba";i:3804;s:3:"Swa";i:3805;s:8:"ReSoWive";i:3806;s:5:"FlIsl";i:3807;s:4:"HiPh";i:3808;s:8:"ChelMerc";i:3809;s:6:"BlaReq";i:3810;s:4:"Codi";i:3811;s:9:"SpTriWote";i:3812;s:9:"LegiPyrSt";i:3813;s:6:"EnLida";i:3814;s:4:"Ungr";i:3815;s:11:"PuSchoSnudg";i:3816;s:8:"HyOsmUnh";i:3817;s:7:"UndeZos";i:3818;s:12:"PhilProvSnap";i:3819;s:11:"OoPeriSangu";i:3820;s:5:"Smutt";i:3821;s:5:"MePac";i:3822;s:5:"Thuri";i:3823;s:14:"ArsenHerseSuba";i:3824;s:4:"Macc";i:3825;s:5:"Untra";i:3826;s:8:"CrNonPil";i:3827;s:4:"CaMe";i:3828;s:8:"PanTherm";i:3829;s:12:"InMesolPatac";i:3830;s:8:"MatriNon";i:3831;s:6:"GeoSex";i:3832;s:9:"NurseShit";i:3833;s:4:"Stro";i:3834;s:11:"BavPreinTug";i:3835;s:11:"KarsMuUnwit";i:3836;s:4:"Meta";i:3837;s:7:"OvPrill";i:3838;s:7:"MiMucos";i:3839;s:10:"CouMobsSha";i:3840;s:4:"Visi";i:3841;s:7:"SmooSub";i:3842;s:6:"CyPaSi";i:3843;s:9:"MyPacUntu";i:3844;s:5:"Oricy";i:3845;s:3:"Sup";i:3846;s:6:"TerUnd";i:3847;s:8:"IsMonoSa";i:3848;s:9:"HidSunrWo";i:3849;s:12:"MolaxSanUnst";i:3850;s:5:"MoPha";i:3851;s:8:"PieSubri";i:3852;s:5:"Watch";i:3853;s:4:"Team";i:3854;s:10:"PrSesaVind";i:3855;s:10:"HexadNaVac";i:3856;s:9:"MelanNoRa";i:3857;s:7:"PrSepSt";i:3858;s:5:"Sridh";i:3859;s:5:"MasPo";i:3860;s:7:"LiStatu";i:3861;s:10:"SprawTrans";i:3862;s:8:"LooPhoto";i:3863;s:9:"CaTaZingi";i:3864;s:3:"Oro";i:3865;s:5:"Dermi";i:3866;s:7:"DrierRe";i:3867;s:10:"MiskPerSup";i:3868;s:3:"Wat";i:3869;s:6:"CoWelc";i:3870;s:11:"NonPediScho";i:3871;s:10:"JemmOuRubo";i:3872;s:5:"Urtic";i:3873;s:5:"FrGal";i:3874;s:6:"LaceVa";i:3875;s:8:"RecSubch";i:3876;s:5:"SaSph";i:3877;s:12:"MarylMnemoOn";i:3878;s:3:"Lie";i:3879;s:8:"FluPrSuf";i:3880;s:5:"Pangx";i:3881;s:13:"MediReoccUnda";i:3882;s:9:"CelFatGou";i:3883;s:4:"Xant";i:3884;s:4:"Moil";i:3885;s:11:"DeltForIned";i:3886;s:7:"RegiUnp";i:3887;s:7:"KabylTh";i:3888;s:8:"ShaTrade";i:3889;s:7:"VilWaWi";i:3890;s:9:"MappiOsph";i:3891;s:5:"DriSl";i:3892;s:5:"Tauto";i:3893;s:5:"Sinia";i:3894;s:5:"PrUnr";i:3895;s:7:"UnsVerb";i:3896;s:13:"DesExploTorsi";i:3897;s:5:"PaUna";i:3898;s:8:"PhotUpis";i:3899;s:12:"FemHalfSubur";i:3900;s:10:"BesoDolMem";i:3901;s:9:"WordZebra";i:3902;s:5:"Sweep";i:3903;s:7:"TrUnpro";i:3904;s:11:"ResSerorWid";i:3905;s:8:"SafrSouv";i:3906;s:4:"Ridg";i:3907;s:11:"CancMessUnd";i:3908;s:6:"OvUnUn";i:3909;s:9:"ShSqueUne";i:3910;s:7:"OratoRo";i:3911;s:5:"GaSpe";i:3912;s:7:"CoOolog";i:3913;s:5:"NonPr";i:3914;s:9:"VanguWaho";i:3915;s:7:"ConseLa";i:3916;s:4:"Zimb";i:3917;s:2:"Im";i:3918;s:11:"AzDiasFonsx";i:3919;s:11:"CrEmbleGale";i:3920;s:7:"DiHonUn";i:3921;s:8:"PerjSphy";i:3922;s:12:"PomonStUnvol";i:3923;s:9:"MiPokoTik";i:3924;s:10:"BikhxConic";i:3925;s:4:"BiFi";i:3926;s:6:"CruFig";i:3927;s:4:"Requ";i:3928;s:5:"Amoeb";i:3929;s:9:"FeathHusb";i:3930;s:8:"ExcrLave";i:3931;s:3:"Obl";i:3932;s:10:"HyperMonSh";i:3933;s:7:"UnerrXe";i:3934;s:8:"HypJibNe";i:3935;s:11:"HousSbUnent";i:3936;s:4:"Pyra";i:3937;s:10:"BacParaSub";i:3938;s:5:"Occam";i:3939;s:12:"MichiRumblTu";i:3940;s:4:"Supr";i:3941;s:7:"FeUnsuf";i:3942;s:7:"DowieSp";i:3943;s:10:"FaceOppoUn";i:3944;s:12:"MetamTopoUbi";i:3945;s:7:"PeriPre";i:3946;s:7:"PsycSep";i:3947;s:8:"MeloUncl";i:3948;s:8:"UndanWau";i:3949;s:11:"AutoChoResy";i:3950;s:7:"OvPegUn";i:3951;s:10:"ArseSoyUns";i:3952;s:5:"SpeUn";i:3953;s:4:"Pinn";i:3954;s:5:"Unspr";i:3955;s:3:"Glo";i:3956;s:5:"BraLe";i:3957;s:10:"IdylNonUnm";i:3958;s:9:"ChilChimp";i:3959;s:2:"Hy";i:3960;s:11:"KharNicomPi";i:3961;s:14:"HemipPariUnpre";i:3962;s:8:"TexUnciv";i:3963;s:12:"MitNaphtRevi";i:3964;s:12:"IraqiKoreTat";i:3965;s:12:"HephInfuThix";i:3966;s:13:"ButtFratcPrer";i:3967;s:11:"BanExtraPro";i:3968;s:10:"PsPulpVomi";i:3969;s:9:"DraffTutw";i:3970;s:10:"NoSoliTheo";i:3971;s:6:"NonvSo";i:3972;s:10:"BeSpurSuba";i:3973;s:3:"Rey";i:3974;s:8:"FadGuTel";i:3975;s:12:"FluGeocPascu";i:3976;s:5:"Punty";i:3977;s:5:"Alcid";i:3978;s:5:"Soggy";i:3979;s:7:"PurpRes";i:3980;s:3:"Por";i:3981;s:7:"MultThw";i:3982;s:2:"Of";i:3983;s:7:"GalicOd";i:3984;s:13:"HyperMilUndis";i:3985;s:7:"ElegiUn";i:3986;s:4:"Inst";i:3987;s:8:"MonkcPan";i:3988;s:4:"Jawx";i:3989;s:12:"CoDonneRewar";i:3990;s:11:"MerkSemidUn";i:3991;s:14:"ErectRomerSero";i:3992;s:4:"Jett";i:3993;s:10:"DeJohRahul";i:3994;s:2:"Ar";i:3995;s:5:"Silic";i:3996;s:10:"PrearPrSem";i:3997;s:4:"Meso";i:3998;s:10:"CysLicheRe";i:3999;s:4:"Snag";i:4000;s:9:"GenecPres";i:4001;s:7:"PropSha";i:4002;s:9:"HamaTraUn";i:4003;s:9:"GyroMatUn";i:4004;s:12:"MisuNonParas";i:4005;s:13:"KindeOvePrein";i:4006;s:13:"IconUnentUnpl";i:4007;s:10:"MyrmeNotel";i:4008;s:10:"KrobPoUnsa";i:4009;s:10:"IndusPalau";i:4010;s:7:"HostPar";i:4011;s:9:"HelPreiSu";i:4012;s:5:"Kacha";i:4013;s:3:"Nac";i:4014;s:7:"SafeWan";i:4015;s:8:"ScrViost";i:4016;s:9:"LoSinnSub";i:4017;s:5:"Vedai";i:4018;s:12:"IdeoPanVaunt";i:4019;s:9:"DeErgxOcy";i:4020;s:8:"HanMePal";i:4021;s:7:"ShValky";i:4022;s:11:"DingIncuTel";i:4023;s:8:"PePillSp";i:4024;s:6:"FocPos";i:4025;s:5:"KnaTr";i:4026;s:4:"Wint";i:4027;s:12:"InsiTheUnmis";i:4028;s:7:"AtmogLe";i:4029;s:8:"MePalRec";i:4030;s:8:"ComKwann";i:4031;s:4:"Over";i:4032;s:12:"NiobiNoneqTo";i:4033;s:11:"KiScytStrop";i:4034;s:4:"Wast";i:4035;s:10:"CasuInteMo";i:4036;s:5:"ShWhi";i:4037;s:3:"Wro";i:4038;s:9:"SwasThTot";i:4039;s:5:"AndSc";i:4040;s:6:"GrapSc";i:4041;s:7:"FirepUn";i:4042;s:13:"BoerBuffaPass";i:4043;s:7:"CoSpTin";i:4044;s:3:"Gor";i:4045;s:7:"DisbeHi";i:4046;s:5:"EleSi";i:4047;s:12:"LympUninkYer";i:4048;s:5:"ShaSl";i:4049;s:9:"IlLiPlagi";i:4050;s:7:"CeOrtho";i:4051;s:7:"AntShel";i:4052;s:7:"DiPeris";i:4053;s:3:"Flo";i:4054;s:10:"GladiNonfa";i:4055;s:4:"Unen";i:4056;s:5:"Myosa";i:4057;s:8:"ReqUnder";i:4058;s:8:"IdyTortr";i:4059;s:10:"CanPrTheri";i:4060;s:2:"Ur";i:4061;s:10:"AtollCorti";i:4062;s:8:"DexGestx";i:4063;s:6:"MerQua";i:4064;s:4:"Usat";i:4065;s:14:"BromLicenNaseb";i:4066;s:11:"GoOrthoSull";i:4067;s:11:"QuinSuperUn";i:4068;s:12:"ConfKoftPass";i:4069;s:8:"OrgaSkTr";i:4070;s:6:"CheaOu";i:4071;s:11:"BafGynRamul";i:4072;s:9:"OpPerZepp";i:4073;s:9:"HypMerTom";i:4074;s:7:"RarelUn";i:4075;s:13:"EpidoPostcSta";i:4076;s:7:"VandyWh";i:4077;s:10:"OutwaRapSo";i:4078;s:11:"OverSouWall";i:4079;s:11:"BeclaChorCo";i:4080;s:3:"Sin";i:4081;s:6:"RaSnak";i:4082;s:10:"FiHomLumbr";i:4083;s:12:"PapaProaThyr";i:4084;s:7:"IntTric";i:4085;s:10:"DisInfrOrl";i:4086;s:10:"GoldeIntVo";i:4087;s:8:"DainOoUl";i:4088;s:9:"FullMulti";i:4089;s:9:"MolRizWoo";i:4090;s:5:"Mimos";i:4091;s:5:"Unfun";i:4092;s:7:"HiSuber";i:4093;s:9:"OpistTrac";i:4094;s:9:"MourPreho";i:4095;s:10:"ProtoRebra";i:4096;s:7:"DiManif";i:4097;s:9:"RepuSubin";i:4098;s:5:"Khald";i:4099;s:5:"SkiSu";i:4100;s:8:"BecloNon";i:4101;s:5:"Mesol";i:4102;s:12:"InnaSmeltTur";i:4103;s:9:"ExtooSidt";i:4104;s:12:"InduTitTrywo";i:4105;s:8:"CoMarsMa";i:4106;s:3:"Wei";i:4107;s:6:"GrSiph";i:4108;s:9:"MelOvPyro";i:4109;s:9:"JovinUnin";i:4110;s:6:"OlPsyc";i:4111;s:10:"GiggeWinly";i:4112;s:5:"Shoem";i:4113;s:8:"CoSyncUn";i:4114;s:10:"NomOverhSp";i:4115;s:10:"TrembUncon";i:4116;s:8:"ReinScSm";i:4117;s:5:"Gager";i:4118;s:11:"TiUnprWorke";i:4119;s:9:"MaParanRe";i:4120;s:9:"StruUnche";i:4121;s:3:"Div";i:4122;s:13:"IndiNeomWoodb";i:4123;s:7:"FiFlaUn";i:4124;s:6:"BryCru";i:4125;s:9:"SubdoSubs";i:4126;s:9:"ReveUnWin";i:4127;s:2:"Ir";i:4128;s:7:"GlSkill";i:4129;s:8:"MagySent";i:4130;s:6:"SarsUn";i:4131;s:14:"SquirVaricVola";i:4132;s:6:"GallWa";i:4133;s:7:"PlumbPr";i:4134;s:10:"EmersHaStr";i:4135;s:10:"EffForSupe";i:4136;s:7:"ReiSpin";i:4137;s:12:"PeacPleTinny";i:4138;s:4:"Tara";i:4139;s:11:"DoghInterSi";i:4140;s:9:"KetPoSpik";i:4141;s:7:"LiMalle";i:4142;s:4:"Unbo";i:4143;s:12:"SharUnfVersa";i:4144;s:7:"UndeWea";i:4145;s:3:"Myx";i:4146;s:7:"DetPlut";i:4147;s:7:"IroqRec";i:4148;s:7:"IntPsha";i:4149;s:5:"PosSe";i:4150;s:10:"SubTimarUn";i:4151;s:5:"MetPa";i:4152;s:9:"HoSnarStr";i:4153;s:5:"Phall";i:4154;s:10:"NecrThUnte";i:4155;s:12:"GemaHortSwam";i:4156;s:4:"Nong";i:4157;s:7:"CerSole";i:4158;s:10:"LatinPenma";i:4159;s:11:"PieriRejSem";i:4160;s:5:"PrPro";i:4161;s:8:"MultiPri";i:4162;s:4:"Twol";i:4163;s:7:"TraTurb";i:4164;s:7:"NaNodRe";i:4165;s:5:"Ouaba";i:4166;s:6:"KerPlu";i:4167;s:4:"Pont";i:4168;s:3:"Gur";i:4169;s:11:"PersSpoliUn";i:4170;s:7:"PlantSl";i:4171;s:2:"Fl";i:4172;s:8:"DiSuUnbo";i:4173;s:8:"IseReque";i:4174;s:11:"GenePtVentr";i:4175;s:4:"Rain";i:4176;s:7:"TrUnUns";i:4177;s:7:"DisUnmi";i:4178;s:10:"OcuReVorti";i:4179;s:8:"AzComSiz";i:4180;s:11:"InterStaVes";i:4181;s:6:"FoPanc";i:4182;s:12:"DeutLifNocta";i:4183;s:4:"HyVo";i:4184;s:7:"OvePres";i:4185;s:6:"SaUnpr";i:4186;s:13:"IzduPostePros";i:4187;s:11:"PePiscoUnri";i:4188;s:9:"FlakePist";i:4189;s:10:"GrappOratr";i:4190;s:9:"OilmaPoly";i:4191;s:4:"Soro";i:4192;s:11:"BellUncloUn";i:4193;s:4:"Ribb";i:4194;s:5:"Encri";i:4195;s:11:"NocuShopSia";i:4196;s:8:"GloTraum";i:4197;s:10:"NondParaPh";i:4198;s:12:"GooxPanWeapo";i:4199;s:8:"ScleWhor";i:4200;s:6:"LokaRa";i:4201;s:9:"SaWeaseWi";i:4202;s:8:"ConUnglo";i:4203;s:6:"TafiWo";i:4204;s:13:"EmpPrayfSubal";i:4205;s:11:"CallCamComp";i:4206;s:8:"CorPaleo";i:4207;s:9:"FrOpiuPal";i:4208;s:7:"CardoHe";i:4209;s:10:"LapMaQuebr";i:4210;s:11:"EncycKupUnh";i:4211;s:3:"Opi";i:4212;s:12:"HydroMeRecti";i:4213;s:4:"Nomo";i:4214;s:5:"Unsub";i:4215;s:7:"HomesRa";i:4216;s:12:"ForksMoStepg";i:4217;s:8:"OctoPopp";i:4218;s:12:"FrazQuernSna";i:4219;s:5:"Serol";i:4220;s:7:"NonelSc";i:4221;s:11:"PepPlaUncom";i:4222;s:11:"NonRetrUnha";i:4223;s:5:"Moril";i:4224;s:4:"CoTo";i:4225;s:7:"EnduLin";i:4226;s:8:"PhoebUng";i:4227;s:11:"HetTalkWrec";i:4228;s:12:"JadisMuUnfor";i:4229;s:5:"BitPu";i:4230;s:5:"EuOle";i:4231;s:10:"UnpUnrarUp";i:4232;s:3:"Vis";i:4233;s:7:"OilskPe";i:4234;s:9:"MaSuVicel";i:4235;s:12:"HypoiIlocaWa";i:4236;s:5:"Figle";i:4237;s:5:"Level";i:4238;s:7:"EntalIn";i:4239;s:7:"PrePrun";i:4240;s:7:"ScolUnf";i:4241;s:3:"Gna";i:4242;s:8:"SepSymph";i:4243;s:5:"LeiSp";i:4244;s:5:"Onrus";i:4245;s:12:"NonOxidoStee";i:4246;s:9:"AsymReiRe";i:4247;s:11:"OvePluRelin";i:4248;s:9:"MePedVege";i:4249;s:12:"HemMarkMasse";i:4250;s:10:"OrogeUnpea";i:4251;s:9:"ForPilThy";i:4252;s:7:"MaPtery";i:4253;s:10:"OraShrUndi";i:4254;s:5:"Bacte";i:4255;s:6:"RefoUn";i:4256;s:13:"ParabPhysaTwa";i:4257;s:13:"ClaDandlItera";i:4258;s:9:"LeucMicOp";i:4259;s:3:"Vip";i:4260;s:7:"TrunkWr";i:4261;s:5:"Vermi";i:4262;s:10:"DupHirUnde";i:4263;s:14:"HoardSalteTaur";i:4264;s:3:"Vio";i:4265;s:5:"Goatb";i:4266;s:12:"DressInMaryx";i:4267;s:4:"Neph";i:4268;s:12:"TanstTrivaWe";i:4269;s:6:"LupeSu";i:4270;s:2:"Us";i:4271;s:6:"PerTab";i:4272;s:9:"FiMerYeas";i:4273;s:4:"OnOt";i:4274;s:10:"AntihSmich";i:4275;s:7:"ObiRoSa";i:4276;s:9:"UnscWelsh";i:4277;s:7:"RhombUn";i:4278;s:5:"Think";i:4279;s:6:"SinWes";i:4280;s:5:"LesSu";i:4281;s:5:"NoObs";i:4282;s:8:"ScarWusp";i:4283;s:8:"HoNaPhot";i:4284;s:5:"Vulca";i:4285;s:12:"MarrOwsYeast";i:4286;s:4:"Togu";i:4287;s:13:"FiloPilgrRuth";i:4288;s:8:"CouPyrTe";i:4289;s:7:"ConUnpa";i:4290;s:4:"Unag";i:4291;s:9:"SaUncUroc";i:4292;s:6:"UnrWhe";i:4293;s:9:"PolyShatt";i:4294;s:5:"ReaSi";i:4295;s:7:"NonOuac";i:4296;s:10:"OlidPsUnex";i:4297;s:6:"SwunUn";i:4298;s:7:"TorvVac";i:4299;s:9:"SoariVall";i:4300;s:9:"EreGaShea";i:4301;s:10:"PospoSemip";i:4302;s:8:"NoSuscUn";i:4303;s:3:"Ost";i:4304;s:9:"ErytMolSa";i:4305;s:5:"SaTre";i:4306;s:7:"PaShaZe";i:4307;s:11:"EnlTaarxTra";i:4308;s:5:"Puppi";i:4309;s:3:"War";i:4310;s:4:"Fera";i:4311;s:13:"CircHandkStal";i:4312;s:6:"PiSubc";i:4313;s:5:"Morrh";i:4314;s:10:"JoiPhytoTe";i:4315;s:8:"DevExcur";i:4316;s:7:"NunsPig";i:4317;s:10:"MarsPiePri";i:4318;s:6:"DuraSa";i:4319;s:5:"FluGe";i:4320;s:4:"RoWi";i:4321;s:5:"Ovine";i:4322;s:10:"PepsiPhVir";i:4323;s:8:"MiSpSupe";i:4324;s:3:"Sel";i:4325;s:5:"Sympt";i:4326;s:11:"IsUndrWhatt";i:4327;s:6:"AnOstr";i:4328;s:4:"IrSa";i:4329;s:4:"Spal";i:4330;s:4:"Sear";i:4331;s:3:"Fiq";i:4332;s:7:"FaOutro";i:4333;s:11:"DamoMePolyd";i:4334;s:13:"HarmfOreTushx";i:4335;s:5:"MaUnt";i:4336;s:7:"DesmoTa";i:4337;s:9:"CossaPhPh";i:4338;s:5:"Notho";i:4339;s:5:"Sowli";i:4340;s:7:"MisesPo";i:4341;s:4:"Uplo";i:4342;s:5:"Recti";i:4343;s:7:"GloHolo";i:4344;s:9:"TyppUnnob";i:4345;s:3:"Euc";i:4346;s:8:"MonNotPr";i:4347;s:7:"LogiSoo";i:4348;s:6:"NoUnde";i:4349;s:4:"Mosa";i:4350;s:3:"Cor";i:4351;s:9:"CinquSupr";i:4352;s:7:"PunkVra";i:4353;s:10:"CarSlWebwo";i:4354;s:5:"CarDe";i:4355;s:7:"TetraUl";i:4356;s:2:"Jo";i:4357;s:4:"OvPo";i:4358;s:4:"Spoi";i:4359;s:8:"IrresTyp";i:4360;s:6:"OvRySo";i:4361;s:6:"MicrPa";i:4362;s:3:"Ral";i:4363;s:5:"PrSem";i:4364;s:8:"OuttrSol";i:4365;s:12:"DisInduSmoke";i:4366;s:7:"PneuSag";i:4367;s:4:"Pstx";i:4368;s:8:"MeOveUnr";i:4369;s:5:"Diaer";i:4370;s:8:"FrorGiUn";i:4371;s:6:"SciSea";i:4372;s:6:"ImOver";i:4373;s:7:"CuLoath";i:4374;s:13:"MatriSorTawgi";i:4375;s:5:"SulWa";i:4376;s:11:"DemiHoodxMu";i:4377;s:9:"GladGuiMu";i:4378;s:10:"MaiMazPeri";i:4379;s:9:"ReedTetra";i:4380;s:8:"MeUnUnpe";i:4381;s:11:"HistoHouTro";i:4382;s:7:"DumGoUn";i:4383;s:3:"Ses";i:4384;s:12:"ReceScincUpg";i:4385;s:9:"ChaDiNonm";i:4386;s:9:"HyraxUnsc";i:4387;s:7:"MiNephr";i:4388;s:5:"PlePr";i:4389;s:5:"OpPre";i:4390;s:8:"AwnlBitt";i:4391;s:12:"JuraMucUnrue";i:4392;s:4:"NoRu";i:4393;s:4:"Rita";i:4394;s:10:"DyStenTetr";i:4395;s:6:"MoPoUn";i:4396;s:11:"ConKokoPapi";i:4397;s:8:"HoUnUnde";i:4398;s:13:"RhiUnsweWapac";i:4399;s:10:"BecLadRema";i:4400;s:13:"JordaSeleWitn";i:4401;s:8:"TramUnwh";i:4402;s:13:"GladePanzoZym";i:4403;s:7:"EvilOli";i:4404;s:11:"LeoUnconWea";i:4405;s:5:"ThToo";i:4406;s:8:"SubvVisi";i:4407;s:11:"IloInaTrogo";i:4408;s:5:"Troch";i:4409;s:5:"TriWo";i:4410;s:10:"IsoJuvVanq";i:4411;s:7:"NaTalon";i:4412;s:11:"CauRenSemin";i:4413;s:11:"HectoMancPa";i:4414;s:5:"HySwa";i:4415;s:6:"CuprLe";i:4416;s:4:"Tain";i:4417;s:4:"Watc";i:4418;s:13:"NegriPosWollo";i:4419;s:10:"DetInterLy";i:4420;s:11:"HamiManSpoa";i:4421;s:6:"FuUran";i:4422;s:11:"EpitLegitSi";i:4423;s:4:"Resi";i:4424;s:9:"FlobIctPh";i:4425;s:13:"QopSpaniTroch";i:4426;s:12:"ReediThirUna";i:4427;s:14:"ItaliNondeRuck";i:4428;s:8:"UntUpsho";i:4429;s:11:"InquStanTri";i:4430;s:10:"PeSebiTarg";i:4431;s:5:"ReUnp";i:4432;s:9:"FlocHecRe";i:4433;s:7:"MogrSum";i:4434;s:5:"ForHi";i:4435;s:6:"NicePe";i:4436;s:4:"Unob";i:4437;s:8:"MnemSupe";i:4438;s:5:"StUnc";i:4439;s:5:"Basop";i:4440;s:4:"Rust";i:4441;s:10:"ChGonVerme";i:4442;s:6:"SigYet";i:4443;s:4:"Trap";i:4444;s:5:"Uproo";i:4445;s:7:"BrSpecu";i:4446;s:5:"DePro";i:4447;s:11:"PrPterUness";i:4448;s:7:"LipPeTi";i:4449;s:8:"RoostTru";i:4450;s:12:"EngarIndeSub";i:4451;s:9:"SipeTaint";i:4452;s:13:"HutxLibelUnem";i:4453;s:10:"DuEtheUnor";i:4454;s:9:"SmStockSw";i:4455;s:7:"TarTeYu";i:4456;s:5:"LoUnr";i:4457;s:4:"Yesx";i:4458;s:9:"SleeStone";i:4459;s:10:"FraGemmPer";i:4460;s:9:"IsocThesm";i:4461;s:10:"DerogEritr";i:4462;s:13:"ConHarasLippy";i:4463;s:5:"UnUnr";i:4464;s:6:"GastSt";i:4465;s:4:"Unsh";i:4466;s:9:"GalHypUnt";i:4467;s:2:"Fe";i:4468;s:13:"HypotIntReali";i:4469;s:4:"Uner";i:4470;s:6:"MoUnid";i:4471;s:11:"HomodMymVil";i:4472;s:9:"FibPrepUn";i:4473;s:9:"SpracWart";i:4474;s:5:"EdxSo";i:4475;s:8:"AxeOverf";i:4476;s:12:"EunucFanhoMo";i:4477;s:4:"DeFl";i:4478;s:8:"ErGnSulp";i:4479;s:9:"MoOvePeri";i:4480;s:11:"ScUnanUnpow";i:4481;s:11:"NautiTaYawn";i:4482;s:6:"QuScSu";i:4483;s:3:"Urf";i:4484;s:4:"Phyt";i:4485;s:7:"GhMyelo";i:4486;s:5:"ClEle";i:4487;s:12:"MetePyrotSne";i:4488;s:7:"MonSpoi";i:4489;s:5:"Operc";i:4490;s:5:"HaHex";i:4491;s:10:"BivFirefPh";i:4492;s:7:"TrabUpt";i:4493;s:5:"Maund";i:4494;s:10:"MalebPrere";i:4495;s:4:"Quar";i:4496;s:9:"MaStarbUn";i:4497;s:11:"PythUnVello";i:4498;s:4:"Domi";i:4499;s:8:"PoiSprew";i:4500;s:9:"MeagrUnwi";i:4501;s:8:"NapRevea";i:4502;s:5:"Unact";i:4503;s:15:"CorniJumboSubur";i:4504;s:6:"JudUnd";i:4505;s:10:"InteMisRow";i:4506;s:9:"OleanSyba";i:4507;s:10:"ElaRedlSup";i:4508;s:4:"Farm";i:4509;s:4:"Hipp";i:4510;s:9:"PangPedPh";i:4511;s:5:"RhiTi";i:4512;s:9:"HomisPaTs";i:4513;s:4:"Reas";i:4514;s:7:"AnaboDi";i:4515;s:9:"JarlxRaTh";i:4516;s:11:"GravSunsVer";i:4517;s:4:"Real";i:4518;s:7:"EfPulta";i:4519;s:9:"GunlxMaRe";i:4520;s:7:"PhaPhre";i:4521;s:5:"PlUnr";i:4522;s:5:"Skipp";i:4523;s:4:"Mura";i:4524;s:14:"SerosUnentWork";i:4525;s:3:"Ske";i:4526;s:11:"AttrFourLac";i:4527;s:8:"PreShrin";i:4528;s:7:"InsProf";i:4529;s:11:"ClSwottWebm";i:4530;s:11:"EndPhylaPle";i:4531;s:8:"MenyaThe";i:4532;s:8:"FissVaul";i:4533;s:7:"HastiOv";i:4534;s:3:"Met";i:4535;s:2:"Ta";i:4536;s:8:"HatbaPan";i:4537;s:6:"SyndTe";i:4538;s:5:"Hoppl";i:4539;s:8:"FerNecro";i:4540;s:4:"Rese";i:4541;s:8:"EnoHiUna";i:4542;s:7:"MaOverh";i:4543;s:11:"ItzNonsPecu";i:4544;s:5:"PhiTh";i:4545;s:3:"Jus";i:4546;s:9:"CoLevoTra";i:4547;s:4:"Sent";i:4548;s:7:"PeUrami";i:4549;s:7:"MuWoman";i:4550;s:4:"Ooph";i:4551;s:10:"InterRocUn";i:4552;s:7:"SaShadd";i:4553;s:14:"PaniTwiteUnirh";i:4554;s:7:"PaPhaRe";i:4555;s:4:"SaSy";i:4556;s:11:"ExarcTestTr";i:4557;s:3:"Vex";i:4558;s:3:"Yuz";i:4559;s:5:"UnWea";i:4560;s:11:"HylPanPapil";i:4561;s:9:"WineWulfe";i:4562;s:7:"PenSpar";i:4563;s:8:"WaveWove";i:4564;s:10:"DisFlStenc";i:4565;s:14:"DelegImpreMult";i:4566;s:7:"DiOpina";i:4567;s:5:"PenPr";i:4568;s:5:"Pinna";i:4569;s:14:"HieroIdylYoick";i:4570;s:6:"CougSt";i:4571;s:5:"RevSt";i:4572;s:10:"IndiMusPro";i:4573;s:4:"SkSu";i:4574;s:4:"Becl";i:4575;s:7:"IndiIns";i:4576;s:9:"NickeOuri";i:4577;s:9:"DeDiHundr";i:4578;s:9:"PerfeSeUn";i:4579;s:5:"MerTh";i:4580;s:7:"LanMann";i:4581;s:13:"FileOtheUndev";i:4582;s:9:"JupaSymme";i:4583;s:5:"Overb";i:4584;s:9:"RelocSnoo";i:4585;s:13:"MckayPetiQuee";i:4586;s:12:"SantaSophUnt";i:4587;s:10:"ConInvinSa";i:4588;s:4:"SyZo";i:4589;s:7:"DessSal";i:4590;s:5:"Septu";i:4591;s:10:"EchenOuTel";i:4592;s:8:"AsPeWith";i:4593;s:5:"Rubli";i:4594;s:11:"DonInspiMor";i:4595;s:12:"CypriDeHorto";i:4596;s:14:"ShravSoureSqua";i:4597;s:11:"IsothUncoYu";i:4598;s:5:"Softe";i:4599;s:8:"FiliTegx";i:4600;s:11:"PreRotteTan";i:4601;s:12:"FulgPitcVulc";i:4602;s:5:"Raphi";i:4603;s:4:"Silk";i:4604;s:7:"IntMilk";i:4605;s:7:"EthExor";i:4606;s:4:"InTe";i:4607;s:12:"HousePetrPre";i:4608;s:5:"Techn";i:4609;s:4:"Spir";i:4610;s:6:"MaThul";i:4611;s:4:"Phae";i:4612;s:9:"MythPipRe";i:4613;s:7:"PottlUn";i:4614;s:10:"FarOverStu";i:4615;s:12:"CranDhawxMis";i:4616;s:14:"ImprOverlTrach";i:4617;s:14:"DoohiGrimiNond";i:4618;s:7:"RedfiSe";i:4619;s:8:"EnteUpra";i:4620;s:13:"IrreKoalPicud";i:4621;s:9:"SaSenSiva";i:4622;s:4:"Quad";i:4623;s:5:"Japon";i:4624;s:9:"SeveSodSt";i:4625;s:13:"CyanhExtMicro";i:4626;s:6:"PavRig";i:4627;s:9:"NaphtProv";i:4628;s:12:"HypovIrreTet";i:4629;s:10:"GiloxLapsi";i:4630;s:5:"ToTri";i:4631;s:8:"GroggPre";i:4632;s:5:"Perco";i:4633;s:5:"Upclo";i:4634;s:8:"GenitLab";i:4635;s:6:"UnXylo";i:4636;s:5:"Devir";i:4637;s:8:"FleNassa";i:4638;s:4:"Salt";i:4639;s:13:"DeliManipUnen";i:4640;s:7:"ShinSho";i:4641;s:6:"IliaOu";i:4642;s:10:"EleImponPu";i:4643;s:9:"ProSuWild";i:4644;s:8:"AnNymRum";i:4645;s:14:"HexamInteUnrou";i:4646;s:11:"CopiHealTem";i:4647;s:14:"PieceSometUnre";i:4648;s:5:"HadRa";i:4649;s:10:"CamacJohan";i:4650;s:9:"OverZeugo";i:4651;s:3:"Sha";i:4652;s:6:"PrReZo";i:4653;s:10:"CoeffPhoUn";i:4654;s:9:"MycoObjec";i:4655;s:4:"MiTa";i:4656;s:9:"BiQuadrSe";i:4657;s:13:"OverPhantSanc";i:4658;s:4:"Poni";i:4659;s:7:"MyeTerr";i:4660;s:4:"Inri";i:4661;s:5:"SemZy";i:4662;s:8:"AuFruKer";i:4663;s:8:"GrPeVasu";i:4664;s:8:"NooQuant";i:4665;s:10:"PlodPtarTa";i:4666;s:8:"SubWistx";i:4667;s:14:"CoccSuperTundx";i:4668;s:5:"Spass";i:4669;s:8:"PuzzSapl";i:4670;s:4:"Snon";i:4671;s:8:"InstPega";i:4672;s:13:"HomoOrthRadia";i:4673;s:5:"Phyll";i:4674;s:8:"ChondRev";i:4675;s:9:"MoroPessi";i:4676;s:5:"Fores";i:4677;s:11:"NeOligPtole";i:4678;s:4:"Serf";i:4679;s:10:"InSymUnacu";i:4680;s:8:"BigemMis";i:4681;s:8:"OvicRaSc";i:4682;s:9:"IsmxUnder";i:4683;s:13:"HaeShoweTesta";i:4684;s:4:"Gram";i:4685;s:7:"TipsUnc";i:4686;s:6:"EuPerm";i:4687;s:9:"EyLoPhone";i:4688;s:7:"RepScat";i:4689;s:4:"MiSu";i:4690;s:11:"ChaucRoriUn";i:4691;s:5:"Slowh";i:4692;s:3:"Osp";i:4693;s:5:"Windo";i:4694;s:3:"Izt";i:4695;s:4:"Hoga";i:4696;s:7:"DrUndis";i:4697;s:9:"NucPyraTh";i:4698;s:5:"Unrum";i:4699;s:10:"BotchGrGue";i:4700;s:9:"LaPreaSem";i:4701;s:7:"OdaxSho";i:4702;s:4:"Outb";i:4703;s:9:"CreeUniun";i:4704;s:12:"JeaniMandSun";i:4705;s:6:"DeDigr";i:4706;s:10:"RotSciurSe";i:4707;s:7:"ConfiUn";i:4708;s:4:"Regi";i:4709;s:11:"DoSterThimb";i:4710;s:4:"Vamp";i:4711;s:3:"Hae";i:4712;s:6:"HyRete";i:4713;s:2:"Up";i:4714;s:9:"FumarRece";i:4715;s:3:"Loc";i:4716;s:5:"Unatt";i:4717;s:7:"OstrVea";i:4718;s:11:"InNettlPres";i:4719;s:7:"BuMilks";i:4720;s:4:"Bone";i:4721;s:10:"MyopPorpSy";i:4722;s:12:"EmmenMeUndis";i:4723;s:9:"DaggeDiGi";i:4724;s:7:"UnVarie";i:4725;s:5:"Perfe";i:4726;s:9:"AzoDaffTa";i:4727;s:10:"IneffWheal";i:4728;s:10:"DeconPinSe";i:4729;s:10:"TeUnmVolut";i:4730;s:10:"SaproTrach";i:4731;s:10:"PlaRhiUrba";i:4732;s:9:"StropSubv";i:4733;s:7:"IcNatur";i:4734;s:8:"FluxaRei";i:4735;s:6:"ChEcTo";i:4736;s:12:"QuadrUnovZoo";i:4737;s:12:"NecroThakuWa";i:4738;s:5:"SeSer";i:4739;s:2:"Go";i:4740;s:12:"FrogxPredSar";i:4741;s:12:"CaFrumpTortr";i:4742;s:7:"LePrakr";i:4743;s:6:"KurtWa";i:4744;s:13:"NymphOthVowma";i:4745;s:14:"InduMurgeTrabe";i:4746;s:10:"CytogUrodi";i:4747;s:12:"CarMarkwPrep";i:4748;s:9:"DyPalatRi";i:4749;s:15:"LazarMimusRemov";i:4750;s:9:"OfPterRef";i:4751;s:10:"PapawTherm";i:4752;s:9:"MoProTrea";i:4753;s:9:"PushfScut";i:4754;s:6:"NonSub";i:4755;s:9:"OcSubWido";i:4756;s:8:"UnchaUng";i:4757;s:9:"IcIntePer";i:4758;s:12:"PurgaReiRepl";i:4759;s:13:"CryLoxiaPutri";i:4760;s:12:"HomolMeaslTa";i:4761;s:4:"Pent";i:4762;s:7:"MisMoRu";i:4763;s:9:"ColorCyMa";i:4764;s:7:"HaemoTr";i:4765;s:5:"ParTa";i:4766;s:8:"MesOrxUn";i:4767;s:5:"Kavix";i:4768;s:3:"Pha";i:4769;s:13:"CaliCatsPenba";i:4770;s:10:"OverRaUnsm";i:4771;s:5:"Psych";i:4772;s:9:"SphaeUnfa";i:4773;s:4:"Wyli";i:4774;s:6:"SuSwan";i:4775;s:10:"DichoElkho";i:4776;s:6:"OsteSt";i:4777;s:5:"Overa";i:4778;s:10:"ReprTeUnpa";i:4779;s:9:"PaTonnZei";i:4780;s:4:"Ostr";i:4781;s:5:"Octob";i:4782;s:3:"Exo";i:4783;s:3:"Res";i:4784;s:3:"But";i:4785;s:8:"RhoTreVi";i:4786;s:6:"PrReda";i:4787;s:4:"IlUn";i:4788;s:7:"MeMonNo";i:4789;s:12:"FluMyrmePaho";i:4790;s:6:"ImprRe";i:4791;s:8:"EpichGla";i:4792;s:12:"RepaRolliUnr";i:4793;s:8:"MininOve";i:4794;s:7:"ProsaUn";i:4795;s:12:"HypNonapYoke";i:4796;s:7:"LalopSc";i:4797;s:8:"HoUneVis";i:4798;s:12:"DorKenmMicro";i:4799;s:15:"ProclResubZoops";i:4800;s:15:"SemirSerraSuper";i:4801;s:7:"SaceUna";i:4802;s:5:"Monoa";i:4803;s:5:"NoUns";i:4804;s:9:"DiEmTauto";i:4805;s:10:"BoehmSupUn";i:4806;s:10:"SuperUnres";i:4807;s:14:"BattDemonVitam";i:4808;s:9:"FilInxRap";i:4809;s:10:"PremiStStr";i:4810;s:10:"DesyGooPer";i:4811;s:4:"CoSo";i:4812;s:8:"HypeTric";i:4813;s:4:"Writ";i:4814;s:8:"PolSangu";i:4815;s:7:"PreUnig";i:4816;s:10:"RubServuTo";i:4817;s:5:"Sider";i:4818;s:9:"UnhauUnpe";i:4819;s:9:"HerniVine";i:4820;s:5:"MaMud";i:4821;s:5:"Nonan";i:4822;s:9:"CurraEnte";i:4823;s:8:"RaTenuTe";i:4824;s:2:"Ba";i:4825;s:5:"CuNeu";i:4826;s:8:"SartShat";i:4827;s:5:"Polyp";i:4828;s:7:"UnreUnv";i:4829;s:12:"InteKarakRhy";i:4830;s:3:"Ven";i:4831;s:3:"Tan";i:4832;s:8:"MeNecrNo";i:4833;s:12:"DioResoUnpre";i:4834;s:4:"Fell";i:4835;s:9:"ExGrafMai";i:4836;s:14:"SanifSuffrUnpr";i:4837;s:12:"GrimmKerUnma";i:4838;s:5:"Subsc";i:4839;s:6:"GilLaw";i:4840;s:10:"PerinPsilo";i:4841;s:8:"RenilRiv";i:4842;s:12:"FuThymoXenop";i:4843;s:7:"CassMen";i:4844;s:6:"LuMoro";i:4845;s:10:"HobPlasmSk";i:4846;s:5:"Rehum";i:4847;s:4:"Piaz";i:4848;s:5:"Defen";i:4849;s:4:"Prea";i:4850;s:9:"RedSaccSu";i:4851;s:9:"StrouUrox";i:4852;s:9:"MakObelSe";i:4853;s:5:"ReoTa";i:4854;s:7:"MiNonvi";i:4855;s:7:"AutZieg";i:4856;s:5:"Oscil";i:4857;s:9:"SuSuperUn";i:4858;s:4:"Slee";i:4859;s:5:"CaaJa";i:4860;s:8:"OvePosit";i:4861;s:7:"LianWel";i:4862;s:11:"FibOvoelTub";i:4863;s:4:"Crou";i:4864;s:5:"Wheed";i:4865;s:5:"Heter";i:4866;s:5:"MaPol";i:4867;s:8:"HundMaUr";i:4868;s:5:"ChGyr";i:4869;s:7:"CrFrPhr";i:4870;s:7:"KiPrima";i:4871;s:13:"DemibSinUnint";i:4872;s:5:"PuTyp";i:4873;s:8:"HooPhysi";i:4874;s:7:"CubitSt";i:4875;s:12:"PaletProWhos";i:4876;s:7:"RaTorme";i:4877;s:15:"LeiotSnortWally";i:4878;s:10:"LymPlaniSc";i:4879;s:5:"SuUni";i:4880;s:10:"BefrGlobRh";i:4881;s:11:"LibeNowaTro";i:4882;s:8:"ArEnerRh";i:4883;s:4:"Scar";i:4884;s:10:"CorpLumSha";i:4885;s:14:"HereaTraiTruan";i:4886;s:5:"Syrin";i:4887;s:6:"ChaInn";i:4888;s:3:"Use";i:4889;s:12:"EkahLusiUnfo";i:4890;s:10:"HelpiPhala";i:4891;s:10:"InterUnpro";i:4892;s:5:"Unred";i:4893;s:9:"ConfPePle";i:4894;s:9:"HoPreReam";i:4895;s:11:"PotTedTimer";i:4896;s:5:"Shiel";i:4897;s:10:"GoOverResc";i:4898;s:6:"NaTyle";i:4899;s:10:"FibGommeLa";i:4900;s:7:"PremoSt";i:4901;s:9:"HydroTale";i:4902;s:7:"PerThig";i:4903;s:8:"AcoelHal";i:4904;s:7:"BocNonr";i:4905;s:8:"PsySnaTe";i:4906;s:5:"Stint";i:4907;s:9:"SectSeeUn";i:4908;s:4:"FlMu";i:4909;s:4:"Reve";i:4910;s:7:"QuestUn";i:4911;s:11:"LibUnsVictu";i:4912;s:8:"SellaTra";i:4913;s:10:"CoMultParu";i:4914;s:3:"See";i:4915;s:7:"MaReTea";i:4916;s:10:"DucTowaTri";i:4917;s:7:"KeeProb";i:4918;s:10:"CitywPenta";i:4919;s:5:"Sprad";i:4920;s:5:"Fugit";i:4921;s:5:"TrVas";i:4922;s:12:"FoozlKlysSta";i:4923;s:10:"HomaMisTur";i:4924;s:8:"MensuVar";i:4925;s:13:"ForLinteNidic";i:4926;s:10:"SteToUpspl";i:4927;s:11:"JoshSuVisco";i:4928;s:5:"Semis";i:4929;s:7:"NovoxTr";i:4930;s:9:"GushxSchi";i:4931;s:10:"CitrPhthSu";i:4932;s:6:"ProUnc";i:4933;s:5:"PaVol";i:4934;s:3:"Lam";i:4935;s:7:"MatPlSo";i:4936;s:2:"Ru";i:4937;s:9:"PerRereTh";i:4938;s:11:"DokPetrSube";i:4939;s:6:"ProtSt";i:4940;s:15:"EidetParroScyph";i:4941;s:9:"OverRechi";i:4942;s:5:"Elsew";i:4943;s:12:"HomoSpecZizz";i:4944;s:5:"PrUnw";i:4945;s:8:"RattlRig";i:4946;s:5:"Helic";i:4947;s:9:"MicrSimSq";i:4948;s:8:"PreUnjil";i:4949;s:7:"InfrRep";i:4950;s:10:"GuasPalRub";i:4951;s:8:"OrgSubWa";i:4952;s:4:"Sept";i:4953;s:8:"BrEyeTri";i:4954;s:7:"NagOver";i:4955;s:12:"ButNonadSill";i:4956;s:11:"LavStympVea";i:4957;s:11:"DispoExRaph";i:4958;s:11:"FlexuMaleSa";i:4959;s:10:"IncraVihar";i:4960;s:6:"ScSkew";i:4961;s:4:"Irre";i:4962;s:8:"PeRecUna";i:4963;s:11:"CoPulviSchi";i:4964;s:10:"InconLyasx";i:4965;s:10:"MonodSparg";i:4966;s:6:"KickPi";i:4967;s:11:"TrabUnrWary";i:4968;s:14:"MuleResorUnsna";i:4969;s:13:"MetapNiggSupe";i:4970;s:9:"GraSterUv";i:4971;s:7:"LasquQu";i:4972;s:6:"UnaUnp";i:4973;s:10:"BipheConWr";i:4974;s:6:"CirUni";i:4975;s:3:"Pon";i:4976;s:5:"ExtRe";i:4977;s:5:"Inact";i:4978;s:7:"TrVendi";i:4979;s:5:"Santa";i:4980;s:9:"OsPhlPoro";i:4981;s:14:"HolidHydroTome";i:4982;s:4:"Incr";i:4983;s:3:"Hyp";i:4984;s:10:"CoOverrSug";i:4985;s:4:"Soap";i:4986;s:4:"Sole";i:4987;s:11:"PompProteSq";i:4988;s:6:"OutySu";i:4989;s:8:"FraFrPha";i:4990;s:7:"ParaPat";i:4991;s:12:"MarcOrobScot";i:4992;s:9:"PhysiTuto";i:4993;s:7:"AzygCit";i:4994;s:11:"UnharUnpUns";i:4995;s:3:"Neg";i:4996;s:6:"PrSupe";i:4997;s:9:"RosehUnso";i:4998;s:8:"NonRodom";i:4999;s:9:"IsothOpto";i:5000;s:8:"ForeFrac";i:5001;s:11:"ShoneTeZymo";i:5002;s:10:"BrKoloUnif";i:5003;s:12:"ImprPremiSup";i:5004;s:9:"EpeisGiIn";i:5005;s:8:"SnatcTes";i:5006;s:7:"GerTrav";i:5007;s:11:"ElSubsuUnsu";i:5008;s:4:"ApPr";i:5009;s:5:"Seapo";i:5010;s:7:"PoTetra";i:5011;s:11:"DeceFraOver";i:5012;s:4:"NoPa";i:5013;s:10:"BaraBiMoul";i:5014;s:8:"SchooZam";i:5015;s:3:"Omp";i:5016;s:12:"NuttiStVotar";i:5017;s:9:"DiSoarSto";i:5018;s:3:"Ges";i:5019;s:12:"OeninSubTher";i:5020;s:5:"DumJi";i:5021;s:9:"ClareDias";i:5022;s:9:"OutglPuUn";i:5023;s:5:"Salel";i:5024;s:10:"HyperOePro";i:5025;s:6:"BegNon";i:5026;s:6:"DemeLa";i:5027;s:11:"AuletCoNonr";i:5028;s:10:"JaNonRegul";i:5029;s:3:"Coc";i:5030;s:3:"Fun";i:5031;s:3:"Sti";i:5032;s:9:"MaineMile";i:5033;s:10:"CahokLikab";i:5034;s:6:"SponSt";i:5035;s:3:"Tai";i:5036;s:11:"MicrOverRef";i:5037;s:9:"SlumSphin";i:5038;s:8:"RivalSho";i:5039;s:13:"ContPredeTurb";i:5040;s:7:"TopUndi";i:5041;s:7:"PluStar";i:5042;s:2:"Gy";i:5043;s:13:"PantPiccSemid";i:5044;s:11:"NoTetraTumm";i:5045;s:7:"DaTheot";i:5046;s:13:"PiaTheorUnawa";i:5047;s:5:"Zilla";i:5048;s:12:"LikenProWhat";i:5049;s:5:"Demod";i:5050;s:2:"Ul";i:5051;s:5:"Sexlo";i:5052;s:4:"Unab";i:5053;s:5:"Tobac";i:5054;s:7:"UnVenti";i:5055;s:14:"HugeoSchuSucci";i:5056;s:4:"Octa";i:5057;s:12:"EcoFritxUncu";i:5058;s:7:"MadhvVa";i:5059;s:10:"UnbewUnive";i:5060;s:8:"PsTreUni";i:5061;s:5:"Overc";i:5062;s:7:"DarryLe";i:5063;s:8:"LateRuin";i:5064;s:8:"ParThYea";i:5065;s:5:"Reapp";i:5066;s:12:"NarProoeRhin";i:5067;s:10:"ThiUnconUn";i:5068;s:7:"OrtUnpe";i:5069;s:5:"Reliq";i:5070;s:4:"Uret";i:5071;s:6:"PsyTer";i:5072;s:3:"Gal";i:5073;s:5:"Rando";i:5074;s:6:"UnnWis";i:5075;s:12:"IndInteOutth";i:5076;s:3:"Fla";i:5077;s:7:"AntiSem";i:5078;s:7:"ConMeRh";i:5079;s:6:"SupTaf";i:5080;s:9:"MaPosScen";i:5081;s:3:"Kae";i:5082;s:8:"EquStrTi";i:5083;s:8:"GawmxOve";i:5084;s:10:"GinglUncap";i:5085;s:14:"DemodHakxSongb";i:5086;s:6:"IracSe";i:5087;s:4:"Squa";i:5088;s:7:"MicrUnp";i:5089;s:6:"LoSzla";i:5090;s:11:"JurorUnhaVe";i:5091;s:8:"ThioaZen";i:5092;s:12:"HerbaHeteIso";i:5093;s:8:"RhizUnve";i:5094;s:3:"Twe";i:5095;s:13:"InnSpraySunbe";i:5096;s:9:"KoLoRecop";i:5097;s:7:"LegaSta";i:5098;s:5:"Uncou";i:5099;s:9:"SemiSkiTh";i:5100;s:6:"BecoUn";i:5101;s:2:"Av";i:5102;s:8:"AssyCoRe";i:5103;s:7:"PrWauch";i:5104;s:8:"RhapStyl";i:5105;s:5:"Parap";i:5106;s:5:"Thrif";i:5107;s:10:"InteLophVi";i:5108;s:11:"GradIntLeas";i:5109;s:8:"AurRandy";i:5110;s:7:"HyOutdw";i:5111;s:2:"Sw";i:5112;s:4:"Pede";i:5113;s:12:"HomoeIllYame";i:5114;s:12:"NoninSclStro";i:5115;s:5:"Nonco";i:5116;s:7:"GeSpeck";i:5117;s:6:"StoUnp";i:5118;s:5:"ExpSk";i:5119;s:10:"DiEthnoPro";i:5120;s:11:"RhynUnconUn";i:5121;s:3:"Tha";i:5122;s:4:"Unst";i:5123;s:10:"MicroUncit";i:5124;s:7:"IrrTymp";i:5125;s:10:"SemUnimaWa";i:5126;s:10:"OverPontSn";i:5127;s:10:"AreLaugLau";i:5128;s:7:"DesTran";i:5129;s:7:"IrMinue";i:5130;s:11:"NoRenteVexx";i:5131;s:8:"GousPySc";i:5132;s:7:"NeOxylx";i:5133;s:5:"Unpen";i:5134;s:10:"KatexUnpat";i:5135;s:5:"PoTra";i:5136;s:4:"Nonf";i:5137;s:5:"Expos";i:5138;s:4:"Repe";i:5139;s:11:"DePisoUnide";i:5140;s:5:"EquZa";i:5141;s:8:"UnUnUnwi";i:5142;s:11:"OffcParaXyl";i:5143;s:10:"RaSemiSurv";i:5144;s:9:"PhacoVesi";i:5145;s:3:"Inv";i:5146;s:14:"EpitrOsteXanth";i:5147;s:5:"Wellb";i:5148;s:4:"CaSu";i:5149;s:11:"SincTrogUna";i:5150;s:7:"InParam";i:5151;s:13:"GabioOverbUnc";i:5152;s:6:"PlowRo";i:5153;s:13:"InsecJeremTel";i:5154;s:3:"Rat";i:5155;s:7:"IncPrYa";i:5156;s:8:"JarTwist";i:5157;s:6:"ElaPre";i:5158;s:9:"MiRopTurk";i:5159;s:11:"PrPremoSept";i:5160;s:13:"EssOverfWycli";i:5161;s:7:"TragiUn";i:5162;s:9:"InOuRatch";i:5163;s:5:"Bolar";i:5164;s:2:"Br";i:5165;s:12:"QuadrRentVol";i:5166;s:11:"OuphUnsleWh";i:5167;s:11:"GalvRachiSt";i:5168;s:12:"DeliFazPutri";i:5169;s:9:"HiHomeRet";i:5170;s:8:"SloSuper";i:5171;s:4:"Mime";i:5172;s:4:"Turb";i:5173;s:10:"MagiOrUnsu";i:5174;s:11:"BlaOrthOutg";i:5175;s:4:"Inal";i:5176;s:8:"SkeVolva";i:5177;s:12:"HexatMunicOn";i:5178;s:8:"OmnQuUnc";i:5179;s:3:"Qui";i:5180;s:8:"BiFacMan";i:5181;s:5:"Uncle";i:5182;s:10:"PalaSpUnco";i:5183;s:5:"Sabba";i:5184;s:5:"NonSe";i:5185;s:14:"JuggeTetryUnbo";i:5186;s:10:"KerniPalRe";i:5187;s:4:"Unma";i:5188;s:4:"Unmu";i:5189;s:5:"Incom";i:5190;s:7:"PinkSca";i:5191;s:11:"NaSletSperm";i:5192;s:3:"Mil";i:5193;s:10:"AnthrBuOve";i:5194;s:11:"NemaPerUnre";i:5195;s:10:"RevisSanto";i:5196;s:7:"GaGunRa";i:5197;s:13:"PhotoPreUnrec";i:5198;s:6:"ReZoop";i:5199;s:12:"DermaMaThorn";i:5200;s:4:"Scor";i:5201;s:3:"Tet";i:5202;s:10:"MesocSeric";i:5203;s:12:"OmenSomnaSub";i:5204;s:10:"FiordJochx";i:5205;s:9:"DissGaQua";i:5206;s:7:"TeTorme";i:5207;s:7:"LarrUnf";i:5208;s:6:"DiesTa";i:5209;s:7:"NonfStr";i:5210;s:7:"DisEpil";i:5211;s:5:"Smoke";i:5212;s:9:"JimSaShut";i:5213;s:11:"BandOxamPsy";i:5214;s:3:"Pul";i:5215;s:4:"Hype";i:5216;s:4:"Scon";i:5217;s:11:"FourpMilUnd";i:5218;s:12:"ChuThundTopo";i:5219;s:9:"ExMuRelev";i:5220;s:4:"Resp";i:5221;s:8:"CorMilks";i:5222;s:11:"HalfInPyrul";i:5223;s:12:"PhotoProsaUn";i:5224;s:4:"Maie";i:5225;s:7:"MaOchUb";i:5226;s:5:"SpWar";i:5227;s:8:"CalPhaTe";i:5228;s:7:"HePrYam";i:5229;s:4:"Yama";i:5230;s:4:"Trib";i:5231;s:6:"InPoUn";i:5232;s:5:"Sciss";i:5233;s:3:"Teg";i:5234;s:12:"CinctJesModi";i:5235;s:14:"FoghToxigWolfh";i:5236;s:10:"LowSuWould";i:5237;s:7:"SpittTe";i:5238;s:8:"KathUnde";i:5239;s:11:"EnanReRight";i:5240;s:4:"Unpe";i:5241;s:8:"ToppVaci";i:5242;s:11:"LymphSwirWh";i:5243;s:9:"MarkPseud";i:5244;s:10:"ElegaTlasc";i:5245;s:10:"NonmTheoWa";i:5246;s:8:"MenSynar";i:5247;s:3:"Pil";i:5248;s:5:"Thala";i:5249;s:11:"OlePreUnado";i:5250;s:2:"Ni";i:5251;s:4:"Unge";i:5252;s:8:"ProSolTo";i:5253;s:4:"Rece";i:5254;s:8:"IsoLitho";i:5255;s:3:"Col";i:5256;s:5:"Intra";i:5257;s:5:"RoUnl";i:5258;s:7:"HeronIn";i:5259;s:4:"Undu";i:5260;s:7:"LimSaVo";i:5261;s:10:"CenPentSto";i:5262;s:8:"IsraTher";i:5263;s:9:"DeperUpge";i:5264;s:9:"DispLeWoo";i:5265;s:4:"Puru";i:5266;s:3:"Rig";i:5267;s:9:"CoGolPred";i:5268;s:3:"Lyr";i:5269;s:9:"PoRediUnl";i:5270;s:12:"SnotTibiaUnt";i:5271;s:5:"Mirac";i:5272;s:4:"HyHy";i:5273;s:5:"Senti";i:5274;s:6:"RadeSe";i:5275;s:12:"LattSpheUnne";i:5276;s:5:"Sperm";i:5277;s:7:"PrStabl";i:5278;s:9:"KieyUnpam";i:5279;s:9:"NeThiocVi";i:5280;s:6:"MicrRe";i:5281;s:4:"Hint";i:5282;s:6:"OxycVi";i:5283;s:12:"TrimUndeUnex";i:5284;s:7:"EncTcUr";i:5285;s:4:"Tomb";i:5286;s:12:"RoritSxTartw";i:5287;s:9:"GametPret";i:5288;s:8:"CoNodPri";i:5289;s:13:"PhosPogrPseud";i:5290;s:6:"PalTab";i:5291;s:7:"DiscGla";i:5292;s:8:"GrHibVen";i:5293;s:14:"LeaveMassUncor";i:5294;s:9:"ReSmiUnin";i:5295;s:5:"Sacch";i:5296;s:7:"MisceSt";i:5297;s:14:"PrissRepoTauch";i:5298;s:9:"CornPeScr";i:5299;s:8:"HepSuper";i:5300;s:11:"HiroSeriUnd";i:5301;s:12:"SuTripaUnpin";i:5302;s:10:"IrPuerViti";i:5303;s:7:"SubVint";i:5304;s:12:"ScenaThWinna";i:5305;s:8:"FlorSupr";i:5306;s:8:"ProUndes";i:5307;s:12:"TranVagiVier";i:5308;s:7:"KerriQu";i:5309;s:12:"PrunQuinSter";i:5310;s:8:"HoUnWeit";i:5311;s:6:"UnUnte";i:5312;s:9:"OthinUnWe";i:5313;s:9:"PlanRouUn";i:5314;s:3:"Sex";i:5315;s:7:"KangPia";i:5316;s:5:"Uncau";i:5317;s:5:"GeJox";i:5318;s:7:"GamalPs";i:5319;s:4:"Mono";i:5320;s:7:"PaleoPa";i:5321;s:6:"KeysMa";i:5322;s:6:"SeUngo";i:5323;s:5:"StWre";i:5324;s:12:"CamelDatiFre";i:5325;s:5:"ParUn";i:5326;s:5:"Phoby";i:5327;s:12:"BrocNotorUnt";i:5328;s:5:"Repan";i:5329;s:7:"EczWeed";i:5330;s:9:"FatKornOv";i:5331;s:8:"PaReSyno";i:5332;s:10:"LeoraPolyc";i:5333;s:8:"PolSalmo";i:5334;s:11:"LeaLillSpea";i:5335;s:7:"PasPrim";i:5336;s:10:"GyPalTachy";i:5337;s:6:"ProTra";i:5338;s:10:"OdontReamy";i:5339;s:9:"PasixZoog";i:5340;s:5:"SleTo";i:5341;s:10:"IcedxPaSom";i:5342;s:3:"Nep";i:5343;s:7:"OofyShe";i:5344;s:3:"Bic";i:5345;s:11:"ExHoovRodne";i:5346;s:5:"IsQui";i:5347;s:10:"MolSkiesUn";i:5348;s:4:"Trop";i:5349;s:8:"EnterTch";i:5350;s:5:"OveSu";i:5351;s:5:"Uncre";i:5352;s:9:"InquPhren";i:5353;s:10:"StaSwUnrun";i:5354;s:9:"MoNontaSt";i:5355;s:12:"LaryUninUnme";i:5356;s:5:"Spiro";i:5357;s:8:"PhylTett";i:5358;s:4:"SaSe";i:5359;s:11:"MultiPerPsy";i:5360;s:8:"DaRemSub";i:5361;s:4:"Trip";i:5362;s:11:"HemLummoUna";i:5363;s:13:"DepigGinIntre";i:5364;s:5:"MiOog";i:5365;s:3:"Mic";i:5366;s:6:"ConExt";i:5367;s:10:"SaSturdSur";i:5368;s:8:"KinsmUle";i:5369;s:9:"ItenMilde";i:5370;s:5:"Urome";i:5371;s:10:"SolUnexVar";i:5372;s:9:"SulphTask";i:5373;s:9:"CasMetaVa";i:5374;s:10:"KyuxTriUnm";i:5375;s:13:"MyocePectuPor";i:5376;s:14:"LardiMiscuToda";i:5377;s:7:"UnfXant";i:5378;s:12:"PhaRackUnind";i:5379;s:3:"Mes";i:5380;s:8:"HepatMed";i:5381;s:8:"EarSomet";i:5382;s:6:"ExemUn";i:5383;s:8:"KeelPuQu";i:5384;s:5:"Raung";i:5385;s:13:"HyogQuadrSili";i:5386;s:8:"AmpLiPat";i:5387;s:8:"ImpTradi";i:5388;s:8:"SergeSph";i:5389;s:8:"CirUnblo";i:5390;s:9:"EvMortZin";i:5391;s:10:"MammaThioz";i:5392;s:5:"PeiPr";i:5393;s:13:"MicrNotoRadic";i:5394;s:10:"PhtPicumPs";i:5395;s:11:"GeocNonbOst";i:5396;s:8:"AvaJagVy";i:5397;s:5:"MaVen";i:5398;s:8:"ParaUnbl";i:5399;s:7:"OdonRen";i:5400;s:5:"Locus";i:5401;s:12:"DecoOrthRach";i:5402;s:9:"BlFolSyna";i:5403;s:6:"TeUnde";i:5404;s:8:"MouUntVe";i:5405;s:8:"CedarIso";i:5406;s:9:"StonTenct";i:5407;s:11:"ReinsRepSil";i:5408;s:8:"QuaSneak";i:5409;s:8:"CakeCont";i:5410;s:5:"Eucon";i:5411;s:4:"Tele";i:5412;s:4:"Nonc";i:5413;s:8:"AeMapSub";i:5414;s:10:"GigmaRebri";i:5415;s:4:"Wany";i:5416;s:5:"UnpUn";i:5417;s:5:"PorRe";i:5418;s:13:"HomotMoonPost";i:5419;s:14:"CommDecorImmed";i:5420;s:4:"LiLu";i:5421;s:11:"ConcHibZygo";i:5422;s:12:"InteSubeUnch";i:5423;s:5:"Bulim";i:5424;s:9:"RecoUnhoi";i:5425;s:11:"ProteSaloVe";i:5426;s:10:"MaMegPrair";i:5427;s:12:"OvereSeralTo";i:5428;s:4:"Saca";i:5429;s:11:"ChipLactaSl";i:5430;s:3:"Nos";i:5431;s:11:"SickSponWhi";i:5432;s:7:"MouVisi";i:5433;s:8:"OvQxUnsh";i:5434;s:12:"GlabeLePromy";i:5435;s:4:"SpUs";i:5436;s:5:"Uncan";i:5437;s:10:"SculpSuTru";i:5438;s:9:"OuStronWa";i:5439;s:9:"ChioMaste";i:5440;s:11:"DownlPrUnfe";i:5441;s:5:"Tinca";i:5442;s:10:"HidPrRoare";i:5443;s:6:"ChrEri";i:5444;s:9:"OlfaSalpi";i:5445;s:6:"PleoPr";i:5446;s:10:"UnmenWalle";i:5447;s:4:"Osop";i:5448;s:10:"SpaSubtUnt";i:5449;s:7:"EmitRou";i:5450;s:8:"GriPatSa";i:5451;s:5:"FoPhe";i:5452;s:12:"RectoSteUnre";i:5453;s:7:"NoUnequ";i:5454;s:7:"EntitRa";i:5455;s:4:"GaMa";i:5456;s:7:"PrStere";i:5457;s:14:"PalisRevieSeac";i:5458;s:10:"KeratScUnc";i:5459;s:5:"Gorra";i:5460;s:4:"Oste";i:5461;s:5:"Snipx";i:5462;s:5:"SwaTe";i:5463;s:13:"SikeUnutVolit";i:5464;s:8:"SeptTaff";i:5465;s:8:"MartPrRu";i:5466;s:12:"CeChopsRever";i:5467;s:12:"InProveVagin";i:5468;s:5:"InTri";i:5469;s:13:"OligaPalVesse";i:5470;s:6:"PhysPr";i:5471;s:14:"GlycoNeurVulva";i:5472;s:7:"BeDrScl";i:5473;s:7:"BraUnsa";i:5474;s:3:"Ory";i:5475;s:5:"Phary";i:5476;s:3:"Ini";i:5477;s:10:"MaResuWitt";i:5478;s:8:"DeMaddMa";i:5479;s:12:"HereSteZygom";i:5480;s:9:"FluHashUn";i:5481;s:5:"ChIni";i:5482;s:5:"SoUng";i:5483;s:6:"MaSpir";i:5484;s:10:"GawnxRaUns";i:5485;s:7:"ScToxic";i:5486;s:6:"LabPla";i:5487;s:4:"RoVo";i:5488;s:12:"CircuDicProt";i:5489;s:14:"MariPhiloThank";i:5490;s:3:"Wid";i:5491;s:12:"PlatTheokTou";i:5492;s:6:"ScTiVi";i:5493;s:5:"Clubm";i:5494;s:9:"PondUnvul";i:5495;s:5:"Salte";i:5496;s:11:"RotTubicUnh";i:5497;s:10:"TheoTitlUn";i:5498;s:8:"NatifRen";i:5499;s:4:"Zygn";i:5500;s:3:"Eut";i:5501;s:4:"Gene";i:5502;s:12:"ProReactSemi";i:5503;s:11:"CartPoPyrex";i:5504;s:5:"Shock";i:5505;s:4:"Zygo";i:5506;s:5:"MiTen";i:5507;s:7:"PhrTorr";i:5508;s:8:"GruSeTau";i:5509;s:7:"SmaSoUr";i:5510;s:12:"HoweMegOverw";i:5511;s:7:"NumPrUn";i:5512;s:7:"CyQuebr";i:5513;s:8:"IzzarSup";i:5514;s:8:"CoDihDor";i:5515;s:9:"MowRosSee";i:5516;s:14:"PerseSabeSalpi";i:5517;s:4:"Hear";i:5518;s:7:"StrTrep";i:5519;s:10:"NumerUnrWe";i:5520;s:8:"SleiUnch";i:5521;s:4:"NoPr";i:5522;s:4:"Buzz";i:5523;s:5:"ChMae";i:5524;s:5:"DiMer";i:5525;s:7:"RebetVe";i:5526;s:8:"RevTonsi";i:5527;s:11:"PilSquirSui";i:5528;s:8:"PaVoluYo";i:5529;s:10:"CalaCalcCr";i:5530;s:12:"HoverImpPerf";i:5531;s:7:"EmbHeZa";i:5532;s:5:"Paran";i:5533;s:8:"ElianWat";i:5534;s:13:"LumbMugfuUnsu";i:5535;s:9:"CitOvUnso";i:5536;s:11:"PreSunZerma";i:5537;s:4:"Plan";i:5538;s:10:"LaugMoPopu";i:5539;s:8:"PerSuppl";i:5540;s:11:"CapMeroThre";i:5541;s:7:"DerUndi";i:5542;s:9:"MuddiPuUn";i:5543;s:6:"PoSnai";i:5544;s:4:"Trun";i:5545;s:13:"ConfiExhauSou";i:5546;s:5:"Idiom";i:5547;s:5:"ChSpa";i:5548;s:12:"PheRectTousc";i:5549;s:11:"HemJokSelen";i:5550;s:10:"MilliPeatm";i:5551;s:8:"InacPseu";i:5552;s:7:"VegeXen";i:5553;s:6:"BarGre";i:5554;s:5:"InWhi";i:5555;s:9:"CounReove";i:5556;s:13:"PalmePlaSwang";i:5557;s:9:"DiPreiRee";i:5558;s:10:"CappaHyper";i:5559;s:9:"PatsTymUn";i:5560;s:4:"PrUn";i:5561;s:8:"BongHeat";i:5562;s:4:"Derm";i:5563;s:7:"HomoeSt";i:5564;s:13:"SignaSpeSquif";i:5565;s:9:"OlofPlaco";i:5566;s:7:"CaStoUn";i:5567;s:7:"GlMossi";i:5568;s:7:"TrUnclo";i:5569;s:14:"GlycPolycTapet";i:5570;s:15:"OchroParasVilit";i:5571;s:6:"BairDi";i:5572;s:9:"AssorJuSp";i:5573;s:10:"LooPhPluto";i:5574;s:8:"CorCroni";i:5575;s:7:"LaTerml";i:5576;s:11:"SponsTraTub";i:5577;s:6:"ConOst";i:5578;s:8:"SambuUnl";i:5579;s:8:"HormOrga";i:5580;s:7:"CytRecl";i:5581;s:6:"PySupe";i:5582;s:3:"Foo";i:5583;s:9:"GeoMatPho";i:5584;s:7:"CattPar";i:5585;s:11:"CacheHomUng";i:5586;s:13:"RefTrucuUnrip";i:5587;s:5:"HusWx";i:5588;s:13:"DumbSollyUnla";i:5589;s:7:"CoIroni";i:5590;s:11:"FiberLeniTc";i:5591;s:11:"ReanaUrduZe";i:5592;s:8:"TwiddVir";i:5593;s:5:"Possi";i:5594;s:5:"ArEus";i:5595;s:8:"NeogZono";i:5596;s:8:"ProdPsTe";i:5597;s:6:"AttSte";i:5598;s:4:"Epiz";i:5599;s:5:"Impot";i:5600;s:8:"PotWerec";i:5601;s:13:"DavidPsePseud";i:5602;s:5:"Semic";i:5603;s:4:"Hell";i:5604;s:8:"EncyRepe";i:5605;s:6:"TorUng";i:5606;s:12:"GlSauroUnlea";i:5607;s:7:"RecoThy";i:5608;s:7:"GiQuiSu";i:5609;s:5:"CrVel";i:5610;s:9:"PeSeUnthr";i:5611;s:12:"SchUnleaWitc";i:5612;s:14:"BiannGenitLevi";i:5613;s:11:"EndoMisNong";i:5614;s:3:"Slu";i:5615;s:11:"FruOordxSyn";i:5616;s:7:"RondVes";i:5617;s:4:"Upmo";i:5618;s:9:"MemorShou";i:5619;s:5:"Preli";i:5620;s:4:"Midd";i:5621;s:6:"PilTom";i:5622;s:5:"Wager";i:5623;s:12:"CollaCosSpor";i:5624;s:12:"SleuTaffyTet";i:5625;s:8:"NaceVagi";i:5626;s:5:"Ondag";i:5627;s:3:"Anc";i:5628;s:5:"Unafi";i:5629;s:5:"Parda";i:5630;s:10:"CrossIndig";i:5631;s:4:"ChCo";i:5632;s:6:"JaSeba";i:5633;s:6:"ForPen";i:5634;s:6:"PlunTr";i:5635;s:9:"OverTrUni";i:5636;s:7:"SupUnmo";i:5637;s:13:"SeeaSemilThos";i:5638;s:4:"Clad";i:5639;s:5:"MiSal";i:5640;s:5:"IntRe";i:5641;s:10:"CopGaSpeec";i:5642;s:7:"MaOaRiz";i:5643;s:9:"ElePoseUn";i:5644;s:5:"Trapf";i:5645;s:4:"Syll";i:5646;s:4:"Unof";i:5647;s:7:"SpSupTr";i:5648;s:7:"CrassOv";i:5649;s:9:"SupeTinke";i:5650;s:8:"NoneYamx";i:5651;s:4:"Jour";i:5652;s:7:"SummeSy";i:5653;s:7:"SwaTwin";i:5654;s:5:"Outse";i:5655;s:5:"Unlic";i:5656;s:12:"HospPlastQua";i:5657;s:7:"UnaYabb";i:5658;s:5:"Holly";i:5659;s:7:"PlaUnre";i:5660;s:11:"DisEngMonum";i:5661;s:3:"Opu";i:5662;s:10:"LogyxPrefe";i:5663;s:5:"OccPr";i:5664;s:7:"DrafPro";i:5665;s:8:"SeleTypi";i:5666;s:4:"SuUr";i:5667;s:4:"Sper";i:5668;s:7:"IrUnret";i:5669;s:7:"GoloSem";i:5670;s:12:"QuadrRevToly";i:5671;s:3:"Ura";i:5672;s:6:"ShopVa";i:5673;s:11:"ChucGentlVi";i:5674;s:8:"HaulmHic";i:5675;s:8:"OversOxe";i:5676;s:15:"IllumTritoUnwoo";i:5677;s:10:"ReThunUnpe";i:5678;s:3:"Phe";i:5679;s:5:"OdySe";i:5680;s:10:"DisIrrMans";i:5681;s:9:"DoEpYttri";i:5682;s:12:"OveOxazSpoof";i:5683;s:5:"Recom";i:5684;s:3:"Spy";i:5685;s:9:"ChiFooUns";i:5686;s:8:"NoSuUnov";i:5687;s:7:"LeTiUnr";i:5688;s:14:"ChromIncusSulp";i:5689;s:12:"EschaGastrRe";i:5690;s:12:"OpaquTechnUn";i:5691;s:9:"CuGasSnee";i:5692;s:9:"BrillShoo";i:5693;s:8:"NotUrete";i:5694;s:13:"SpiroSteYaupo";i:5695;s:8:"IntScyph";i:5696;s:5:"HoUnr";i:5697;s:5:"Silkg";i:5698;s:3:"Khw";i:5699;s:8:"MalSemip";i:5700;s:9:"ChrisNonp";i:5701;s:8:"PosPrefl";i:5702;s:6:"RuffSu";i:5703;s:3:"Sax";i:5704;s:8:"MulTermi";i:5705;s:10:"CooMonsUns";i:5706;s:7:"JiPaUnb";i:5707;s:5:"Thymo";i:5708;s:12:"CurstMimePos";i:5709;s:7:"UnrWood";i:5710;s:5:"Favon";i:5711;s:6:"LeSupp";i:5712;s:2:"Sq";i:5713;s:8:"FireInse";i:5714;s:10:"MonopRaffa";i:5715;s:3:"Pho";i:5716;s:14:"ConvNotwiSuper";i:5717;s:5:"OchSt";i:5718;s:10:"DeDialEogh";i:5719;s:9:"MisoUnwra";i:5720;s:8:"ProvUnad";i:5721;s:2:"Cy";i:5722;s:7:"MisenWo";i:5723;s:7:"FiPansc";i:5724;s:13:"AnomaHianPear";i:5725;s:5:"HySat";i:5726;s:4:"PrVa";i:5727;s:12:"QuicUnwWoneg";i:5728;s:9:"DefraHygr";i:5729;s:8:"CherHoMy";i:5730;s:9:"ChanVined";i:5731;s:7:"RaRavis";i:5732;s:15:"LiquiOverpSuper";i:5733;s:11:"CubPhiloUns";i:5734;s:7:"SleiSpa";i:5735;s:6:"SwaUnp";i:5736;s:10:"CoRefUnrig";i:5737;s:15:"SarciThundWantl";i:5738;s:10:"BubonIncOn";i:5739;s:7:"PlScrup";i:5740;s:7:"DeDiObv";i:5741;s:11:"RaisUnderVe";i:5742;s:12:"ConteHaIndec";i:5743;s:11:"MusPolycSul";i:5744;s:7:"SagaSub";i:5745;s:5:"OcUnr";i:5746;s:4:"Reca";i:5747;s:10:"ImmenWitti";i:5748;s:7:"PhraUnc";i:5749;s:5:"Prein";i:5750;s:10:"LievMetPre";i:5751;s:8:"OggSigil";i:5752;s:12:"ThTripyUnbro";i:5753;s:10:"KraTerebTr";i:5754;s:6:"InteSe";i:5755;s:10:"FoSheThora";i:5756;s:9:"ExiNoniUn";i:5757;s:12:"QuRoentSamsa";i:5758;s:6:"PhTyro";i:5759;s:10:"LazybParap";i:5760;s:8:"CaFissMo";i:5761;s:5:"Ataxi";i:5762;s:14:"JaileMolePrear";i:5763;s:10:"BarytSixha";i:5764;s:4:"SiSo";i:5765;s:12:"DiscoInterRa";i:5766;s:11:"HyposSyVici";i:5767;s:4:"InLu";i:5768;s:10:"FlPolysTot";i:5769;s:10:"PrSlaugThe";i:5770;s:9:"MegThTort";i:5771;s:7:"NodReSy";i:5772;s:9:"ConPoliVi";i:5773;s:5:"Snipp";i:5774;s:13:"CuticGunbSulf";i:5775;s:5:"Parag";i:5776;s:3:"Ili";i:5777;s:4:"Call";i:5778;s:14:"MarmSairvSuper";i:5779;s:8:"MollSeam";i:5780;s:10:"InvalSesba";i:5781;s:5:"EqInd";i:5782;s:6:"CoPodo";i:5783;s:7:"AntiWis";i:5784;s:14:"GestiHomePetra";i:5785;s:3:"Phy";i:5786;s:7:"CaimiIs";i:5787;s:8:"OstUrson";i:5788;s:6:"ReSpha";i:5789;s:10:"DiDisParas";i:5790;s:10:"OtalOvSpha";i:5791;s:7:"SaSpavi";i:5792;s:8:"UndwiUns";i:5793;s:5:"Linse";i:5794;s:7:"StUnadu";i:5795;s:4:"SaWi";i:5796;s:13:"InterPsyTempt";i:5797;s:10:"CrosDiSecl";i:5798;s:6:"CyLeMu";i:5799;s:5:"CryNu";i:5800;s:5:"Yiddi";i:5801;s:5:"StoUn";i:5802;s:9:"EggeOutwa";i:5803;s:10:"MoravVisuo";i:5804;s:5:"Ghurr";i:5805;s:14:"CompHolidToxic";i:5806;s:7:"MyoSoci";i:5807;s:9:"LiPosthUn";i:5808;s:6:"SpUnto";i:5809;s:3:"Sep";i:5810;s:11:"FumatIndiTr";i:5811;s:4:"Nege";i:5812;s:10:"PeResuScra";i:5813;s:12:"ConvLinParas";i:5814;s:6:"HooPal";i:5815;s:6:"SporSt";i:5816;s:11:"DyeForsUnem";i:5817;s:10:"PredrPrStu";i:5818;s:11:"DivHegLutem";i:5819;s:10:"ScornUnhig";i:5820;s:9:"BeneTubWa";i:5821;s:5:"SanSt";i:5822;s:7:"LaSaUnc";i:5823;s:7:"MisaPol";i:5824;s:14:"MungPenetRudol";i:5825;s:8:"NonclSur";i:5826;s:10:"HydLigPres";i:5827;s:6:"QuirSa";i:5828;s:7:"SquirTa";i:5829;s:8:"EnExpPro";i:5830;s:4:"HeUn";i:5831;s:9:"UnstUnthr";i:5832;s:5:"LiZoo";i:5833;s:5:"Somet";i:5834;s:7:"UnZucch";i:5835;s:6:"InSpUn";i:5836;s:12:"GawNeptOutbo";i:5837;s:4:"Weed";i:5838;s:9:"BoPolySig";i:5839;s:8:"HatcNucl";i:5840;s:6:"HypInn";i:5841;s:6:"UlUngr";i:5842;s:10:"MesScUnatt";i:5843;s:5:"UncYo";i:5844;s:12:"BimilFasciMe";i:5845;s:8:"PubiTass";i:5846;s:15:"GaragLouchNonlo";i:5847;s:8:"PhilhTah";i:5848;s:6:"SornWa";i:5849;s:10:"TamilUnhor";i:5850;s:3:"Bra";i:5851;s:12:"HeteParPreco";i:5852;s:15:"DazemTotanUncri";i:5853;s:9:"FahlMilly";i:5854;s:5:"ImOpp";i:5855;s:5:"Sahar";i:5856;s:14:"MelopReceTrira";i:5857;s:5:"Mimly";i:5858;s:13:"LunaSupraUnil";i:5859;s:12:"ResiVerWhipp";i:5860;s:10:"NucTolUnou";i:5861;s:6:"SuTele";i:5862;s:6:"PinShu";i:5863;s:8:"HyphYuzl";i:5864;s:7:"SplinUn";i:5865;s:5:"Stack";i:5866;s:5:"Odont";i:5867;s:11:"MoParamPreo";i:5868;s:7:"LiMonst";i:5869;s:6:"StroUn";i:5870;s:4:"Tica";i:5871;s:7:"CouElec";i:5872;s:8:"EnrKusim";i:5873;s:3:"Sic";i:5874;s:9:"IncoUnali";i:5875;s:11:"RedeSelexUp";i:5876;s:5:"Parat";i:5877;s:9:"NonacWhif";i:5878;s:12:"RoSangaWistl";i:5879;s:8:"RatTranq";i:5880;s:9:"JentLogUp";i:5881;s:3:"Mus";i:5882;s:14:"ServaSparkSper";i:5883;s:5:"Sylvi";i:5884;s:10:"SnackSurge";i:5885;s:11:"AztecHomPou";i:5886;s:4:"PrPs";i:5887;s:4:"Unex";i:5888;s:7:"CeorGon";i:5889;s:8:"DisesWin";i:5890;s:8:"EglesIsl";i:5891;s:5:"Remix";i:5892;s:5:"BuRes";i:5893;s:11:"HemogOvSupe";i:5894;s:14:"MeasMenacSpora";i:5895;s:8:"OsphyTal";i:5896;s:4:"Blan";i:5897;s:6:"EfflHo";i:5898;s:9:"PrSpeceUn";i:5899;s:6:"IscUnt";i:5900;s:4:"InTa";i:5901;s:9:"PrehuTene";i:5902;s:9:"MatRuShak";i:5903;s:11:"NervePetRed";i:5904;s:8:"EpiMoOut";i:5905;s:7:"CuOsUnp";i:5906;s:6:"InfiSu";i:5907;s:8:"DeMnSkid";i:5908;s:5:"Secti";i:5909;s:5:"CurEc";i:5910;s:11:"FlankMiShri";i:5911;s:12:"ReeliRuRusty";i:5912;s:7:"PluviTu";i:5913;s:12:"MonoNonasStr";i:5914;s:5:"PreTo";i:5915;s:7:"StuUnif";i:5916;s:5:"Unord";i:5917;s:7:"DichrNa";i:5918;s:9:"NimmProlo";i:5919;s:3:"Jol";i:5920;s:9:"HadScoSub";i:5921;s:5:"Stoma";i:5922;s:8:"PrSignTe";i:5923;s:10:"RebTubeVul";i:5924;s:8:"DiNoWood";i:5925;s:5:"OrSwa";i:5926;s:9:"GaIntelOr";i:5927;s:5:"Scuti";i:5928;s:4:"Gent";i:5929;s:7:"NoniPte";i:5930;s:6:"CaHeat";i:5931;s:10:"PhalaWarve";i:5932;s:8:"PrPygUra";i:5933;s:5:"Nutcr";i:5934;s:11:"LepidMonUnr";i:5935;s:7:"PalPatr";i:5936;s:9:"ReiSixVes";i:5937;s:6:"MesUnf";i:5938;s:3:"Tsu";i:5939;s:5:"Ununi";i:5940;s:6:"ReSpie";i:5941;s:4:"SuUn";i:5942;s:9:"OrdinRabb";i:5943;s:11:"MiNapuPlebx";i:5944;s:8:"PodogSta";i:5945;s:9:"KuskuNigg";i:5946;s:4:"Capr";i:5947;s:5:"Chlam";i:5948;s:5:"CoSch";i:5949;s:13:"IndisMyseTubx";i:5950;s:10:"MazRhiTran";i:5951;s:4:"Prer";i:5952;s:7:"DoMecRu";i:5953;s:11:"RamaTopeZen";i:5954;s:13:"OverSpicUnmel";i:5955;s:5:"Prata";i:5956;s:6:"NebaSt";i:5957;s:12:"BudmaCritDio";i:5958;s:11:"GyroPaleoUn";i:5959;s:8:"EsMaRede";i:5960;s:8:"SizTetra";i:5961;s:7:"AsStaTa";i:5962;s:12:"NurseSmyrUna";i:5963;s:9:"CoUnmWrea";i:5964;s:10:"SisUnaltUn";i:5965;s:11:"GigarGranPr";i:5966;s:9:"MacabMyop";i:5967;s:7:"CrediGu";i:5968;s:10:"OncRinSlug";i:5969;s:4:"Rose";i:5970;s:5:"MisMo";i:5971;s:11:"ShoeSlUnmem";i:5972;s:10:"PasquPreci";i:5973;s:9:"DedicInIr";i:5974;s:12:"GlovePyjamSa";i:5975;s:9:"CockThroa";i:5976;s:3:"Jau";i:5977;s:7:"LibTrip";i:5978;s:13:"MegaRaceTobac";i:5979;s:5:"Atwix";i:5980;s:12:"PlatyReUnben";i:5981;s:11:"MoPropSulph";i:5982;s:5:"Twere";i:5983;s:4:"Timi";i:5984;s:8:"CoOophUn";i:5985;s:7:"KentiRe";i:5986;s:11:"AgrolKaPter";i:5987;s:5:"Xeres";i:5988;s:14:"ChoriLochiMaga";i:5989;s:12:"FucoxGaoNutt";i:5990;s:9:"PrStrTerp";i:5991;s:7:"ItSciss";i:5992;s:11:"GassiNatUnb";i:5993;s:4:"Unri";i:5994;s:8:"PrSarSir";i:5995;s:4:"HeUs";i:5996;s:11:"CyMatamStan";i:5997;s:9:"MarcSuTub";i:5998;s:7:"UnrVall";i:5999;s:7:"PentaPh";i:6000;s:5:"Knobs";i:6001;s:5:"Poste";i:6002;s:4:"InPr";i:6003;s:12:"DoldrRecUros";i:6004;s:12:"OctodParkeTh";i:6005;s:6:"PlaThe";i:6006;s:2:"En";i:6007;s:13:"PseudReheSpri";i:6008;s:8:"NitPraTo";i:6009;s:6:"PrTetr";i:6010;s:8:"DiscKary";i:6011;s:5:"Rattl";i:6012;s:6:"SpicSu";i:6013;s:7:"NonfTet";i:6014;s:7:"UnUteri";i:6015;s:14:"LesbiNawxPrere";i:6016;s:4:"Thra";i:6017;s:7:"MercThu";i:6018;s:6:"PiSini";i:6019;s:3:"Ten";i:6020;s:10:"ExecuXylos";i:6021;s:6:"IaKoUn";i:6022;s:4:"Coin";i:6023;s:6:"HagsLo";i:6024;s:8:"ProsScha";i:6025;s:5:"Weepx";i:6026;s:7:"MirdaPo";i:6027;s:4:"Pegm";i:6028;s:8:"DemForgo";i:6029;s:5:"Meado";i:6030;s:10:"ArchLimbSk";i:6031;s:8:"NegOrSem";i:6032;s:10:"KonRusseUn";i:6033;s:4:"PrSm";i:6034;s:7:"MuRenUn";i:6035;s:3:"Ras";i:6036;s:10:"InterKaTep";i:6037;s:13:"RedoToseTropi";i:6038;s:6:"LibeTa";i:6039;s:8:"ChQuinRe";i:6040;s:4:"Zoop";i:6041;s:10:"MorSpanUne";i:6042;s:10:"IndicRoUnr";i:6043;s:5:"SarTi";i:6044;s:3:"Mou";i:6045;s:7:"CaSatra";i:6046;s:12:"ForamMoaMura";i:6047;s:8:"MaMoSulp";i:6048;s:13:"PiscaTonnVisi";i:6049;s:14:"IndusMuskPodic";i:6050;s:9:"ResprSeni";i:6051;s:9:"AftHypeOa";i:6052;s:9:"DeDigEuco";i:6053;s:8:"TriUndes";i:6054;s:7:"MoskSik";i:6055;s:9:"FifthLuSu";i:6056;s:5:"Proli";i:6057;s:9:"PolyeQuin";i:6058;s:9:"InIsUnmat";i:6059;s:7:"CrPondo";i:6060;s:9:"LantStrid";i:6061;s:13:"HaveSubpTechn";i:6062;s:8:"MaMusaSc";i:6063;s:7:"MoSeSup";i:6064;s:5:"Timef";i:6065;s:9:"LoPyvuSyn";i:6066;s:3:"Spl";i:6067;s:9:"MouReseUm";i:6068;s:4:"ToWa";i:6069;s:9:"MonocSept";i:6070;s:13:"KanepTransTri";i:6071;s:4:"Enhy";i:6072;s:13:"SpareSubTaran";i:6073;s:8:"PagaSupe";i:6074;s:13:"MaesNaturSero";i:6075;s:6:"ClDere";i:6076;s:6:"BuruSn";i:6077;s:5:"PerPs";i:6078;s:7:"PanmUpw";i:6079;s:6:"GreeRe";i:6080;s:7:"ErUnpro";i:6081;s:7:"CycliHe";i:6082;s:2:"Eu";i:6083;s:7:"HoPandr";i:6084;s:11:"MesNonOscil";i:6085;s:10:"ExtPoTetra";i:6086;s:13:"MastoMonoPyro";i:6087;s:6:"HiNoSp";i:6088;s:10:"GlPraUnhed";i:6089;s:6:"RewTho";i:6090;s:7:"UnaWoor";i:6091;s:14:"ElfhPicknUntes";i:6092;s:4:"Tinw";i:6093;s:6:"CrucEx";i:6094;s:8:"FloInoge";i:6095;s:11:"HadexPremTh";i:6096;s:3:"Mys";i:6097;s:6:"TiUnau";i:6098;s:12:"EurNonsuNonv";i:6099;s:5:"Woonx";i:6100;s:5:"DiUnr";i:6101;s:7:"PoRaUlt";i:6102;s:5:"Ethno";i:6103;s:4:"Heat";i:6104;s:6:"ShacSm";i:6105;s:11:"NonPhantSyn";i:6106;s:4:"Quin";i:6107;s:14:"FewnKieseOptio";i:6108;s:11:"ClypImpaUns";i:6109;s:9:"ParalUnbe";i:6110;s:7:"ParSing";i:6111;s:10:"CleSexuaUn";i:6112;s:10:"SacchSolid";i:6113;s:9:"HorsIncor";i:6114;s:13:"FoodlHusNight";i:6115;s:8:"MonSubUn";i:6116;s:5:"Inkho";i:6117;s:4:"Panz";i:6118;s:4:"NoPe";i:6119;s:7:"PonTatu";i:6120;s:8:"CotVangl";i:6121;s:4:"Xero";i:6122;s:5:"CurPi";i:6123;s:3:"Str";i:6124;s:4:"Sacr";i:6125;s:4:"Quai";i:6126;s:5:"Uncoa";i:6127;s:9:"ChitProsu";i:6128;s:6:"PenuUn";i:6129;s:4:"BrLa";i:6130;s:7:"EnterJu";i:6131;s:11:"CoOutsTroph";i:6132;s:11:"ComNondRele";i:6133;s:12:"PreleScenUnp";i:6134;s:12:"ExHogwaPerip";i:6135;s:10:"UndWaZibet";i:6136;s:12:"GinkxQuinqRa";i:6137;s:6:"EmmVer";i:6138;s:10:"PalReTeasa";i:6139;s:12:"MePrepoSurge";i:6140;s:12:"HomSommUncti";i:6141;s:9:"TheoTinin";i:6142;s:12:"IrrMonoPetio";i:6143;s:13:"MultOxysuThes";i:6144;s:4:"Natc";i:6145;s:11:"DisiEnStoma";i:6146;s:11:"HegarPolTan";i:6147;s:5:"StTra";i:6148;s:5:"Unexc";i:6149;s:12:"AweSyllaUnpr";i:6150;s:6:"CaReci";i:6151;s:12:"SatirUnmiVac";i:6152;s:5:"SeUnc";i:6153;s:8:"ProtTint";i:6154;s:5:"Waikl";i:6155;s:10:"MaMegasSpr";i:6156;s:3:"Gam";i:6157;s:7:"CoVagar";i:6158;s:7:"ToppUre";i:6159;s:7:"ArcImpa";i:6160;s:15:"MokoxOcherSubar";i:6161;s:10:"DeeEtiolTh";i:6162;s:10:"TrottUnUnf";i:6163;s:5:"Bronz";i:6164;s:4:"Tutr";i:6165;s:6:"PisaRi";i:6166;s:6:"PuSuTe";i:6167;s:5:"PoSpi";i:6168;s:7:"LiReefe";i:6169;s:9:"QuadWimex";i:6170;s:11:"GoniSizalYa";i:6171;s:3:"Ins";i:6172;s:15:"GinglSemigSerpe";i:6173;s:13:"GadinSemUnfei";i:6174;s:7:"TinUnne";i:6175;s:5:"Grapt";i:6176;s:7:"PhScaTh";i:6177;s:5:"Preaf";i:6178;s:3:"Kap";i:6179;s:10:"MaywMohaWe";i:6180;s:4:"TaTe";i:6181;s:5:"FiTig";i:6182;s:3:"Jer";i:6183;s:11:"OdobPawneSp";i:6184;s:15:"NonbaTorchUnext";i:6185;s:4:"SlSt";i:6186;s:5:"Weelx";i:6187;s:5:"ExTem";i:6188;s:4:"EnUn";i:6189;s:12:"HurlyPythoTr";i:6190;s:4:"Glos";i:6191;s:13:"SpisuTriceUne";i:6192;s:11:"EpacImpoPro";i:6193;s:12:"MetOakeOuttr";i:6194;s:9:"OrScWoodn";i:6195;s:7:"HoMazum";i:6196;s:9:"MesenSemi";i:6197;s:9:"RegulSand";i:6198;s:5:"Stasi";i:6199;s:6:"UnZoog";i:6200;s:4:"Paro";i:6201;s:11:"SinapStaSup";i:6202;s:9:"TaeTeTheo";i:6203;s:10:"KrapiProrh";i:6204;s:9:"GuarMarxi";i:6205;s:13:"SybarTanUnwed";i:6206;s:4:"JaUn";i:6207;s:6:"SemiSo";i:6208;s:8:"UniUnind";i:6209;s:12:"DulTriuTubul";i:6210;s:5:"Engra";i:6211;s:12:"UnpreZygonZy";i:6212;s:5:"Recko";i:6213;s:9:"GrinnInUn";i:6214;s:7:"EleGeLi";i:6215;s:12:"MolSyconUnst";i:6216;s:4:"Scum";i:6217;s:10:"FlaFostUnd";i:6218;s:5:"Peris";i:6219;s:8:"CophLava";i:6220;s:12:"SmetSpriTran";i:6221;s:8:"NephUnwa";i:6222;s:7:"QueetSc";i:6223;s:10:"FeifGueTab";i:6224;s:10:"OliveUnciv";i:6225;s:10:"BuilCompTa";i:6226;s:7:"RecliRe";i:6227;s:4:"OrUn";i:6228;s:7:"ExRadSa";i:6229;s:3:"Jux";i:6230;s:12:"ShikiSupeUns";i:6231;s:4:"DeUn";i:6232;s:9:"MiNuReine";i:6233;s:8:"TheTrime";i:6234;s:6:"SemWot";i:6235;s:3:"Pyr";i:6236;s:9:"NerthOuts";i:6237;s:6:"DyscGl";i:6238;s:3:"Mag";i:6239;s:8:"LucraPer";i:6240;s:7:"ParUlVi";i:6241;s:3:"Ken";i:6242;s:8:"ScrUnsca";i:6243;s:10:"PewmaToUnp";i:6244;s:13:"MangNeglPolys";i:6245;s:9:"FoInhiSor";i:6246;s:11:"DrunSporTir";i:6247;s:4:"HaLo";i:6248;s:11:"PharyQuUnch";i:6249;s:12:"MiamMilPhary";i:6250;s:4:"Stuc";i:6251;s:10:"DomElectPs";i:6252;s:4:"Kumn";i:6253;s:9:"PreteVaga";i:6254;s:4:"MiPr";i:6255;s:9:"GimGrooPr";i:6256;s:5:"ForSc";i:6257;s:3:"Opa";i:6258;s:5:"Hirud";i:6259;s:6:"OverPo";i:6260;s:9:"MarNomaSt";i:6261;s:4:"Saxo";i:6262;s:6:"HyLept";i:6263;s:7:"MyxaOcy";i:6264;s:3:"Iso";i:6265;s:9:"NonchPeri";i:6266;s:9:"ElytrTric";i:6267;s:6:"MePurb";i:6268;s:6:"ChaOut";i:6269;s:8:"NonTrave";i:6270;s:8:"InsiPent";i:6271;s:6:"PrTern";i:6272;s:4:"Subg";i:6273;s:7:"ImprUnv";i:6274;s:4:"Unha";i:6275;s:8:"ProgUngl";i:6276;s:12:"LaMammiToros";i:6277;s:5:"SeaSu";i:6278;s:5:"Vigon";i:6279;s:9:"AriIdiLog";i:6280;s:8:"NovPsyTh";i:6281;s:8:"KrausTro";i:6282;s:5:"Undup";i:6283;s:10:"ContrMysTe";i:6284;s:4:"Harl";i:6285;s:3:"Upk";i:6286;s:6:"InfThi";i:6287;s:7:"GriMyct";i:6288;s:9:"BleaCoccy";i:6289;s:12:"NeuroSoldiSw";i:6290;s:8:"HeKathRa";i:6291;s:10:"NackeNitro";i:6292;s:14:"CapacPestPsych";i:6293;s:8:"OutlaUnl";i:6294;s:7:"PhrenVe";i:6295;s:10:"PluQuicYum";i:6296;s:5:"Tesse";i:6297;s:5:"Poiki";i:6298;s:6:"AppaWr";i:6299;s:8:"CattChPo";i:6300;s:5:"Unsuc";i:6301;s:10:"JotnOtoUns";i:6302;s:10:"ArgNubiPre";i:6303;s:9:"GastLagga";i:6304;s:3:"Yom";i:6305;s:8:"SpSteVet";i:6306;s:12:"ColPleoSheri";i:6307;s:10:"FrankNeSes";i:6308;s:3:"Kon";i:6309;s:14:"CraigMayorOpul";i:6310;s:8:"UnwUnVer";i:6311;s:7:"NonpeRe";i:6312;s:3:"Ort";i:6313;s:7:"HirsQui";i:6314;s:7:"HurOver";i:6315;s:5:"PrSlo";i:6316;s:5:"Cerem";i:6317;s:10:"PleaToXero";i:6318;s:9:"ProUnemUn";i:6319;s:4:"UnWi";i:6320;s:5:"PrePr";i:6321;s:11:"MigSerUnref";i:6322;s:5:"Progu";i:6323;s:8:"HaMultSe";i:6324;s:6:"RiRoll";i:6325;s:5:"Unthi";i:6326;s:14:"ExtolMisnSperm";i:6327;s:7:"PreTimb";i:6328;s:7:"RickeSh";i:6329;s:10:"PericPyrrh";i:6330;s:7:"TiUnder";i:6331;s:13:"HeaVocalWhewt";i:6332;s:4:"Misw";i:6333;s:3:"Bol";i:6334;s:14:"PlatiPunnSphac";i:6335;s:3:"Vag";i:6336;s:14:"SainxSlowSteep";i:6337;s:10:"MacrPaguTr";i:6338;s:4:"Scut";i:6339;s:8:"ShooUnin";i:6340;s:10:"OenanSayax";i:6341;s:14:"EpigLegioTiara";i:6342;s:11:"MenogStopVa";i:6343;s:6:"PlUniq";i:6344;s:10:"EmOverZepp";i:6345;s:11:"SeTelenWork";i:6346;s:6:"MoReco";i:6347;s:4:"Reda";i:6348;s:8:"PelRucTh";i:6349;s:10:"UngriWitho";i:6350;s:4:"MuOr";i:6351;s:14:"PrincThiopVivi";i:6352;s:6:"NonaPh";i:6353;s:5:"NeZoo";i:6354;s:8:"PuffySup";i:6355;s:4:"Bair";i:6356;s:14:"ManwaSinaiUroc";i:6357;s:6:"AntLux";i:6358;s:9:"FungKokTo";i:6359;s:10:"AquipPhyto";i:6360;s:8:"OrtTuVie";i:6361;s:11:"CaloGudgPie";i:6362;s:4:"Palt";i:6363;s:7:"HubXeno";i:6364;s:9:"KnobSyTra";i:6365;s:10:"MirzaObiUn";i:6366;s:10:"CongeStali";i:6367;s:5:"Pomox";i:6368;s:3:"Vol";i:6369;s:7:"InepMel";i:6370;s:10:"ResiRhynSe";i:6371;s:8:"PrTychUn";i:6372;s:9:"DactyTrut";i:6373;s:13:"BradaCarpHera";i:6374;s:9:"OctRebiSu";i:6375;s:10:"ShTruerVer";i:6376;s:9:"HeMeTellu";i:6377;s:12:"FairyGraveSt";i:6378;s:8:"PergTalp";i:6379;s:3:"Sty";i:6380;s:6:"IntSol";i:6381;s:9:"SpeciToUn";i:6382;s:8:"NutaSkir";i:6383;s:5:"Rhomb";i:6384;s:9:"SharUnsni";i:6385;s:5:"DiInt";i:6386;s:5:"Splen";i:6387;s:6:"OraTer";i:6388;s:5:"Dahli";i:6389;s:8:"ClaOliSo";i:6390;s:8:"PeSangSa";i:6391;s:4:"Tors";i:6392;s:9:"HypopPari";i:6393;s:14:"ThiosTonoUnise";i:6394;s:7:"CatPara";i:6395;s:4:"Sumb";i:6396;s:7:"OsPreSw";i:6397;s:8:"HattUrsi";i:6398;s:6:"DiNotc";i:6399;s:5:"Disso";i:6400;s:8:"ReRenaWh";i:6401;s:12:"MonocSeroWha";i:6402;s:5:"Toade";i:6403;s:4:"CnMo";i:6404;s:8:"CardiSca";i:6405;s:4:"Corn";i:6406;s:14:"ExtrPotorPreve";i:6407;s:13:"HomalRepreSub";i:6408;s:4:"Vers";i:6409;s:5:"Unbla";i:6410;s:12:"PalaPneStodg";i:6411;s:5:"PyTri";i:6412;s:10:"DeyshPlPul";i:6413;s:9:"IvorUndis";i:6414;s:7:"PellTid";i:6415;s:5:"Thatx";i:6416;s:8:"DecaEleu";i:6417;s:4:"Maea";i:6418;s:9:"AuObUnadj";i:6419;s:11:"EntInoJumps";i:6420;s:12:"TreUnnotUnsw";i:6421;s:9:"AtechGeOv";i:6422;s:9:"LecUndeUn";i:6423;s:9:"DimnStrin";i:6424;s:13:"MonopNonsuRep";i:6425;s:3:"Ver";i:6426;s:6:"PesTho";i:6427;s:11:"InPredSemis";i:6428;s:7:"PoPrint";i:6429;s:4:"Coch";i:6430;s:4:"Mopp";i:6431;s:3:"Dra";i:6432;s:7:"DrygoPr";i:6433;s:9:"SulciUnco";i:6434;s:8:"SaboUnUn";i:6435;s:10:"TanqTreUns";i:6436;s:7:"UnjVesp";i:6437;s:4:"Pror";i:6438;s:4:"Love";i:6439;s:10:"EmeHyPlaty";i:6440;s:6:"JucRec";i:6441;s:11:"ConfePhoTra";i:6442;s:9:"ConcoPoss";i:6443;s:12:"GalacIrSemif";i:6444;s:9:"ParaUnawa";i:6445;s:7:"UnUnZam";i:6446;s:7:"PinSabr";i:6447;s:9:"MicOverSu";i:6448;s:8:"PoilViny";i:6449;s:8:"SentiSup";i:6450;s:10:"ParlaProSu";i:6451;s:11:"BipBrancObu";i:6452;s:5:"Uncri";i:6453;s:5:"Suran";i:6454;s:12:"BloClaviTitu";i:6455;s:4:"TwUn";i:6456;s:6:"PiPost";i:6457;s:7:"ElaFeHy";i:6458;s:5:"Unnav";i:6459;s:13:"OphidPenthUni";i:6460;s:3:"Lar";i:6461;s:15:"LongiRisibTopog";i:6462;s:7:"ThamuUn";i:6463;s:11:"LotProUnbla";i:6464;s:7:"TaUnmer";i:6465;s:7:"MilPhyl";i:6466;s:5:"IncNi";i:6467;s:3:"Fix";i:6468;s:6:"ExSwip";i:6469;s:5:"OraUn";i:6470;s:4:"Scle";i:6471;s:9:"HoReparSu";i:6472;s:9:"OvangUnpr";i:6473;s:12:"DoorSnowbUni";i:6474;s:9:"OverPolUn";i:6475;s:3:"Mit";i:6476;s:5:"Unsop";i:6477;s:14:"HeavyHeterJump";i:6478;s:9:"PostpWall";i:6479;s:11:"RepRicTetra";i:6480;s:12:"LiqPalaUnipu";i:6481;s:7:"ClMesPe";i:6482;s:13:"HeterLirParda";i:6483;s:5:"Trieq";i:6484;s:9:"SamarTigr";i:6485;s:5:"Diver";i:6486;s:4:"LiUn";i:6487;s:4:"Beta";i:6488;s:6:"DivuOp";i:6489;s:9:"NoniScruf";i:6490;s:9:"LoafOnchi";i:6491;s:3:"Ele";i:6492;s:7:"TeraUnm";i:6493;s:5:"Palee";i:6494;s:5:"Solid";i:6495;s:4:"Mynp";i:6496;s:10:"FairyPulWo";i:6497;s:4:"Wate";i:6498;s:4:"Rodd";i:6499;s:10:"OffPlUngui";i:6500;s:9:"OvPlayUrd";i:6501;s:7:"SocioUp";i:6502;s:5:"EtPim";i:6503;s:10:"ParonPrVir";i:6504;s:2:"Ju";i:6505;s:10:"EchiSaThuy";i:6506;s:12:"NepNodulRefi";i:6507;s:11:"IncSinecUni";i:6508;s:9:"GarbStThe";i:6509;s:13:"GluttHeadsRun";i:6510;s:8:"EvaKafir";i:6511;s:9:"CoInNonen";i:6512;s:7:"InuOtho";i:6513;s:14:"DeutParenPrere";i:6514;s:10:"EnvMetamSh";i:6515;s:8:"ButadDis";i:6516;s:9:"MendPseud";i:6517;s:6:"PaProv";i:6518;s:3:"Ina";i:6519;s:10:"EcclInfPol";i:6520;s:6:"PrUnde";i:6521;s:14:"IneffPudgPursu";i:6522;s:9:"SchizToVo";i:6523;s:3:"Imb";i:6524;s:4:"ChSt";i:6525;s:9:"LarLecoSt";i:6526;s:5:"UnUnd";i:6527;s:7:"IntReoc";i:6528;s:7:"FixedIm";i:6529;s:9:"CogTabWoo";i:6530;s:8:"SpaciUnp";i:6531;s:6:"BaraBe";i:6532;s:5:"Unbar";i:6533;s:12:"CometDeIntra";i:6534;s:12:"ErePeracWhee";i:6535;s:7:"CounUnw";i:6536;s:8:"NonTormo";i:6537;s:10:"PreiSpeTri";i:6538;s:11:"CerLankWeve";i:6539;s:12:"TractUngelVe";i:6540;s:10:"DepDrUnsol";i:6541;s:4:"Mult";i:6542;s:6:"RewVol";i:6543;s:5:"MorSy";i:6544;s:8:"UndWilkx";i:6545;s:4:"BeUn";i:6546;s:7:"ImpasUn";i:6547;s:10:"IndiaMerca";i:6548;s:4:"Unfo";i:6549;s:8:"ImMiMuti";i:6550;s:13:"NaviPreaUnspa";i:6551;s:5:"Extra";i:6552;s:11:"RenuWhWicke";i:6553;s:7:"TergUnp";i:6554;s:8:"MydTabid";i:6555;s:3:"Lod";i:6556;s:4:"Cruc";i:6557;s:5:"DiaSo";i:6558;s:8:"HetInter";i:6559;s:7:"ClMisap";i:6560;s:3:"Spe";i:6561;s:9:"IllecPant";i:6562;s:5:"Rhabd";i:6563;s:13:"BrushWireYell";i:6564;s:7:"DilaOve";i:6565;s:12:"CyprDegPotma";i:6566;s:8:"TicUtero";i:6567;s:6:"InPtyc";i:6568;s:12:"DelaMaraScul";i:6569;s:7:"AlpCrSt";i:6570;s:12:"HeUnproVagra";i:6571;s:4:"Theo";i:6572;s:12:"RecSplanVenu";i:6573;s:4:"Groc";i:6574;s:14:"BasilNoncoZami";i:6575;s:10:"LifexPasto";i:6576;s:14:"PsychSubdWhipp";i:6577;s:5:"MosTe";i:6578;s:14:"CoplaObsiXyloq";i:6579;s:10:"FatbrStrat";i:6580;s:7:"ToUnpit";i:6581;s:5:"PitSh";i:6582;s:8:"JapRatch";i:6583;s:8:"VarniYod";i:6584;s:11:"HancoRemTen";i:6585;s:10:"KoreiOverf";i:6586;s:9:"TineVimfu";i:6587;s:7:"NoruUne";i:6588;s:3:"Ure";i:6589;s:11:"CultrPlaSun";i:6590;s:10:"ExogaTrowe";i:6591;s:5:"Upcom";i:6592;s:10:"CuneiFePre";i:6593;s:10:"MatPlSnaff";i:6594;s:8:"PhaTarTy";i:6595;s:9:"BetHenrUn";i:6596;s:6:"BruMet";i:6597;s:4:"Peck";i:6598;s:5:"Makes";i:6599;s:5:"Unpit";i:6600;s:8:"ScThewTo";i:6601;s:4:"Spec";i:6602;s:9:"UnplUnres";i:6603;s:11:"OxytoSaftUp";i:6604;s:5:"CiHoo";i:6605;s:9:"InverMoTi";i:6606;s:3:"Upt";i:6607;s:15:"OverfPolymPrein";i:6608;s:9:"CotePhoto";i:6609;s:4:"Crew";i:6610;s:8:"PredPrim";i:6611;s:10:"CephaShrub";i:6612;s:3:"Nuc";i:6613;s:3:"Ouz";i:6614;s:9:"ThereVena";i:6615;s:3:"Sug";i:6616;s:3:"Nam";i:6617;s:7:"QuinoTr";i:6618;s:5:"Reove";i:6619;s:10:"GliomIntSc";i:6620;s:9:"DuchReban";i:6621;s:6:"DisHot";i:6622;s:10:"PhoroRatio";i:6623;s:4:"BrTi";i:6624;s:10:"HyInterTae";i:6625;s:4:"Plic";i:6626;s:6:"IsoSar";i:6627;s:8:"CyaniPos";i:6628;s:7:"JossaTh";i:6629;s:7:"MoTease";i:6630;s:9:"PenciSynt";i:6631;s:12:"TobUnjelUrti";i:6632;s:7:"StorTri";i:6633;s:4:"Squi";i:6634;s:10:"IsoThUnsof";i:6635;s:9:"EpilNonbu";i:6636;s:4:"Recr";i:6637;s:12:"GlossMinTown";i:6638;s:3:"Sid";i:6639;s:10:"MoireNoTri";i:6640;s:9:"FungSkUna";i:6641;s:10:"LewPemmUne";i:6642;s:9:"MetaOveSc";i:6643;s:11:"FlaMiscaOwl";i:6644;s:6:"PrWhis";i:6645;s:7:"SpTesUn";i:6646;s:12:"BroSubjuUnmo";i:6647;s:6:"SupTch";i:6648;s:10:"HeromLodPo";i:6649;s:12:"BeniCheesSur";i:6650;s:9:"HypeOverl";i:6651;s:4:"Sela";i:6652;s:10:"StiTurtlVe";i:6653;s:11:"GreMacrMarr";i:6654;s:8:"AsHanImp";i:6655;s:6:"FaSher";i:6656;s:12:"MicPonerTran";i:6657;s:11:"GigarHyMudd";i:6658;s:11:"AmErytFemin";i:6659;s:5:"Sacer";i:6660;s:9:"OutmUneph";i:6661;s:7:"DissWar";i:6662;s:9:"ObPlScabr";i:6663;s:5:"Inten";i:6664;s:3:"Raj";i:6665;s:8:"LaPipeUn";i:6666;s:8:"PreReUne";i:6667;s:11:"InpMonobPle";i:6668;s:4:"Here";i:6669;s:8:"GasPicWe";i:6670;s:12:"GamoInfiMyos";i:6671;s:8:"MinoPlai";i:6672;s:4:"Unbe";i:6673;s:5:"Outla";i:6674;s:7:"PseuVer";i:6675;s:4:"Wing";i:6676;s:5:"Unrav";i:6677;s:6:"GreTur";i:6678;s:11:"OysStaTrith";i:6679;s:8:"SaSymTur";i:6680;s:13:"MisliOdontPha";i:6681;s:11:"HaysNanomRe";i:6682;s:8:"PasPhySe";i:6683;s:11:"ColoLutOpta";i:6684;s:7:"LyUrtic";i:6685;s:6:"TimbTo";i:6686;s:9:"ShotTeete";i:6687;s:13:"BrushCafCompu";i:6688;s:9:"UncrUnhum";i:6689;s:6:"FibrTa";i:6690;s:7:"GameInc";i:6691;s:5:"Weakh";i:6692;s:10:"ObfusQuave";i:6693;s:13:"FurlIrreUngen";i:6694;s:8:"FoulPant";i:6695;s:11:"DisEuryQuar";i:6696;s:4:"Fati";i:6697;s:5:"Shalt";i:6698;s:9:"GhosMiThe";i:6699;s:12:"BandlKowtoTe";i:6700;s:10:"ImponTabUn";i:6701;s:10:"HexOnsUnpe";i:6702;s:8:"ElegiTem";i:6703;s:7:"PenhRep";i:6704;s:9:"OdonPhosp";i:6705;s:6:"PlWenl";i:6706;s:7:"ReprUns";i:6707;s:4:"Smit";i:6708;s:5:"Terro";i:6709;s:7:"ResusTe";i:6710;s:10:"HoppiPrSop";i:6711;s:5:"Shini";i:6712;s:12:"HeterRoamSal";i:6713;s:6:"OverWo";i:6714;s:5:"ScUnp";i:6715;s:4:"Mead";i:6716;s:5:"Upstr";i:6717;s:5:"Unrep";i:6718;s:4:"Rumi";i:6719;s:7:"SuperTe";i:6720;s:5:"ThVer";i:6721;s:12:"SupeThrTomop";i:6722;s:12:"OugPedanStib";i:6723;s:6:"CiCoun";i:6724;s:9:"HeHobThug";i:6725;s:6:"CohGno";i:6726;s:4:"Styr";i:6727;s:9:"MePresRes";i:6728;s:9:"ExPhThumb";i:6729;s:8:"SheSkipp";i:6730;s:10:"CaFaunaUnq";i:6731;s:4:"Meri";i:6732;s:10:"FerReWaste";i:6733;s:5:"Ninet";i:6734;s:9:"KmetxUnar";i:6735;s:8:"PulsSaxt";i:6736;s:6:"PrepUp";i:6737;s:5:"Marxi";i:6738;s:10:"EpiscSubpr";i:6739;s:8:"KochProh";i:6740;s:5:"GemOr";i:6741;s:15:"HeroiSubcoUnbra";i:6742;s:4:"Scot";i:6743;s:8:"MisSnort";i:6744;s:6:"PhaPha";i:6745;s:8:"RevUnpre";i:6746;s:8:"AnoSupVe";i:6747;s:4:"Stam";i:6748;s:3:"Pru";i:6749;s:7:"BeCatap";i:6750;s:5:"DevSc";i:6751;s:11:"SexiSocUnil";i:6752;s:6:"InNons";i:6753;s:11:"RecaTecnTre";i:6754;s:5:"CiCoa";i:6755;s:11:"CoDiscPachy";i:6756;s:4:"Pleu";i:6757;s:10:"ExtrHesiMu";i:6758;s:6:"TilbTo";i:6759;s:5:"Vacuo";i:6760;s:13:"PlaTheopTrica";i:6761;s:12:"OsirProtRhoe";i:6762;s:8:"MulWeava";i:6763;s:4:"Mort";i:6764;s:10:"QuSchilTan";i:6765;s:7:"SqueTar";i:6766;s:12:"OrthPhaRoent";i:6767;s:9:"MoPifTrib";i:6768;s:15:"SubcaSuperUnbra";i:6769;s:7:"LilaNan";i:6770;s:10:"OrnisPresu";i:6771;s:7:"AndreSo";i:6772;s:5:"Sloug";i:6773;s:9:"HoOxyheSc";i:6774;s:13:"DermLawlMetac";i:6775;s:13:"DirTwelvVirgi";i:6776;s:11:"NuttRatapVi";i:6777;s:4:"Teet";i:6778;s:9:"GardeScWr";i:6779;s:6:"PaPlUn";i:6780;s:8:"PhrUrrad";i:6781;s:4:"OrTh";i:6782;s:10:"LenUnUtopi";i:6783;s:7:"FaOssZi";i:6784;s:9:"NonmTexas";i:6785;s:8:"SuTricUn";i:6786;s:11:"NaOrchiRive";i:6787;s:6:"KleUnp";i:6788;s:6:"PhilUn";i:6789;s:4:"Cade";i:6790;s:13:"MacanOvercUpc";i:6791;s:8:"PinnSpha";i:6792;s:11:"IcMegalPara";i:6793;s:5:"NonTi";i:6794;s:10:"PienxVaria";i:6795;s:9:"EncoNoYal";i:6796;s:4:"Uptr";i:6797;s:8:"AnconSyc";i:6798;s:9:"FeIntoSta";i:6799;s:7:"RelShif";i:6800;s:7:"ExonePr";i:6801;s:6:"PuThio";i:6802;s:6:"HesiKa";i:6803;s:5:"PanTy";i:6804;s:11:"MacuMiRomag";i:6805;s:12:"LearnNotaSti";i:6806;s:4:"Raki";i:6807;s:8:"OrthUnfl";i:6808;s:10:"KurvePhySp";i:6809;s:7:"HisMump";i:6810;s:9:"PanxTreen";i:6811;s:8:"IndifUnc";i:6812;s:5:"ByMus";i:6813;s:11:"OuSuspUnplu";i:6814;s:12:"IsoLoundOutp";i:6815;s:15:"HexaxSinuaUntro";i:6816;s:8:"SiccUnde";i:6817;s:7:"CaprSep";i:6818;s:8:"RetroTet";i:6819;s:9:"InteQuSem";i:6820;s:5:"Imper";i:6821;s:3:"Psy";i:6822;s:12:"GnarLogTogsx";i:6823;s:4:"Unpa";i:6824;s:7:"HorseRe";i:6825;s:12:"PaUndisUndre";i:6826;s:3:"Lus";i:6827;s:2:"Cu";i:6828;s:3:"Sip";i:6829;s:3:"Tom";i:6830;s:7:"MicRask";i:6831;s:4:"Pect";i:6832;s:12:"ThesTrichVir";i:6833;s:5:"Unwas";i:6834;s:5:"Naple";i:6835;s:9:"MegPrTumo";i:6836;s:11:"HastaPeteSu";i:6837;s:4:"Unrh";i:6838;s:7:"PolyTol";i:6839;s:13:"DisaOrnitPlat";i:6840;s:3:"Oes";i:6841;s:5:"Puboi";i:6842;s:5:"Polyt";i:6843;s:11:"NonmPsTetra";i:6844;s:4:"Uniu";i:6845;s:11:"FasPreRamis";i:6846;s:5:"Stylo";i:6847;s:13:"PonciScyUnloc";i:6848;s:9:"MiaoRecol";i:6849;s:11:"SleSuperVig";i:6850;s:6:"RampRe";i:6851;s:5:"Hongx";i:6852;s:9:"ReticUnpo";i:6853;s:9:"LactiTrip";i:6854;s:7:"PolStTr";i:6855;s:10:"AxonoGraci";i:6856;s:12:"LinSuddeVerm";i:6857;s:9:"CataSuste";i:6858;s:7:"GiRussi";i:6859;s:8:"SextoSla";i:6860;s:5:"Suthe";i:6861;s:6:"PreSha";i:6862;s:10:"MoisRaSpir";i:6863;s:5:"Herco";i:6864;s:9:"TepaWalac";i:6865;s:5:"Tempe";i:6866;s:7:"RaisiUn";i:6867;s:9:"PremUnwif";i:6868;s:6:"MacSta";i:6869;s:12:"EphahPaurUnd";i:6870;s:9:"ParafSurv";i:6871;s:10:"FiRelatUns";i:6872;s:10:"OverdSinto";i:6873;s:5:"NoPup";i:6874;s:10:"CounForPty";i:6875;s:6:"MethVi";i:6876;s:11:"DecayKnocPu";i:6877;s:4:"Xena";i:6878;s:8:"ReRetrUn";i:6879;s:5:"KlTwi";i:6880;s:14:"BeechSubpUnmea";i:6881;s:5:"Unpro";i:6882;s:4:"PrSe";i:6883;s:6:"ReStUn";i:6884;s:8:"FeruOcra";i:6885;s:8:"HomTetry";i:6886;s:5:"IneNa";i:6887;s:7:"IndiPro";i:6888;s:11:"GinGuppScle";i:6889;s:9:"FellLight";i:6890;s:6:"LymPro";i:6891;s:5:"HaUnd";i:6892;s:12:"DaboiIntPant";i:6893;s:11:"CyaJangPann";i:6894;s:8:"PulStink";i:6895;s:12:"BolDevicOpis";i:6896;s:8:"HyTempWh";i:6897;s:7:"CheerSe";i:6898;s:5:"OmPyo";i:6899;s:3:"Tit";i:6900;s:7:"RetiSow";i:6901;s:8:"SemShall";i:6902;s:9:"SecStroVa";i:6903;s:10:"PieSemiSub";i:6904;s:4:"Conv";i:6905;s:10:"PiemaUndex";i:6906;s:11:"BreFrankUng";i:6907;s:9:"OkiTenUnw";i:6908;s:6:"PoStav";i:6909;s:2:"Zi";i:6910;s:11:"DiarrDoWind";i:6911;s:8:"AlicPara";i:6912;s:8:"SarcSnuf";i:6913;s:5:"Poetr";i:6914;s:5:"Nonma";i:6915;s:12:"BathMegUnple";i:6916;s:8:"PoSatUnd";i:6917;s:4:"Exor";i:6918;s:5:"Megar";i:6919;s:7:"HyIndMe";i:6920;s:8:"DuPhUnsp";i:6921;s:7:"SugViki";i:6922;s:9:"PreSquUns";i:6923;s:5:"Tiger";i:6924;s:2:"Dy";i:6925;s:8:"DebaRoun";i:6926;s:10:"PrecTicTra";i:6927;s:5:"Myoca";i:6928;s:9:"PinaSundr";i:6929;s:12:"NonioSonoTes";i:6930;s:8:"PersTone";i:6931;s:7:"PloPrel";i:6932;s:13:"EyepNorthVisc";i:6933;s:7:"RatiRou";i:6934;s:5:"Place";i:6935;s:5:"Proch";i:6936;s:9:"AppRepSto";i:6937;s:6:"OnlySe";i:6938;s:8:"DieguOsk";i:6939;s:11:"MatteOptaRa";i:6940;s:12:"SqTricuUncon";i:6941;s:12:"BreaDolpTrea";i:6942;s:10:"SerabUnlec";i:6943;s:4:"TaUn";i:6944;s:5:"HyPro";i:6945;s:12:"HeteIrraLupi";i:6946;s:6:"OverPa";i:6947;s:4:"Pili";i:6948;s:7:"MediVen";i:6949;s:7:"StUnple";i:6950;s:4:"Toxi";i:6951;s:4:"Medi";i:6952;s:11:"RadiiSoTouc";i:6953;s:8:"PeriPhan";i:6954;s:11:"EpopHeadsSh";i:6955;s:2:"Ka";i:6956;s:6:"IchSpe";i:6957;s:6:"ChTeta";i:6958;s:9:"LateSemih";i:6959;s:4:"Lutr";i:6960;s:10:"BedebHysMa";i:6961;s:8:"SardShru";i:6962;s:10:"CoreDePain";i:6963;s:3:"Tsa";i:6964;s:2:"De";i:6965;s:12:"MotRhinoVoll";i:6966;s:5:"Prefa";i:6967;s:3:"Est";i:6968;s:10:"FitOverdUn";i:6969;s:11:"PontaQuaSem";i:6970;s:8:"ForMaMis";i:6971;s:11:"PlumlPraeZi";i:6972;s:4:"Pseu";i:6973;s:8:"PuritTru";i:6974;s:12:"EquSpirWokex";i:6975;s:8:"SleepUnt";i:6976;s:5:"MidPo";i:6977;s:6:"ProtRu";i:6978;s:6:"ProgRe";i:6979;s:12:"PleniRunoUnu";i:6980;s:8:"BorJeann";i:6981;s:4:"Unwo";i:6982;s:8:"PamphSho";i:6983;s:8:"IneSkirt";i:6984;s:10:"PeRyukTrag";i:6985;s:9:"EnnoRonVa";i:6986;s:14:"MastySikarTrir";i:6987;s:3:"Tem";i:6988;s:15:"MozamRepliUncon";i:6989;s:8:"HecKoUnd";i:6990;s:8:"TellxThi";i:6991;s:7:"PseRadi";i:6992;s:8:"MissTrac";i:6993;s:7:"SlTrust";i:6994;s:8:"MacrSpUn";i:6995;s:9:"DaisyGoWh";i:6996;s:7:"PeriSor";i:6997;s:13:"HyposInteOver";i:6998;s:4:"Maza";i:6999;s:11:"QuSubscWeez";i:7000;s:6:"TruUre";i:7001;s:9:"PamlRecus";i:7002;s:10:"CycliStren";i:7003;s:5:"Serpi";i:7004;s:3:"Top";i:7005;s:3:"Moo";i:7006;s:7:"KneMaSe";i:7007;s:7:"IsatPol";i:7008;s:11:"ErytFattPeu";i:7009;s:7:"OsteUnd";i:7010;s:5:"Bulba";i:7011;s:8:"PaSnVerb";i:7012;s:5:"Unsin";i:7013;s:7:"PseuRub";i:7014;s:5:"Exsan";i:7015;s:10:"InterMiPen";i:7016;s:5:"Pleur";i:7017;s:13:"HydrInterOver";i:7018;s:4:"Coag";i:7019;s:7:"SaSicar";i:7020;s:6:"MonPre";i:7021;s:8:"SaSumbWi";i:7022;s:7:"GePreci";i:7023;s:4:"Tymp";i:7024;s:9:"SinuoUbii";i:7025;s:5:"Overw";i:7026;s:7:"HeorUnp";i:7027;s:6:"NeisUn";i:7028;s:10:"FrKikaPrec";i:7029;s:3:"Obt";i:7030;s:11:"BowmCroMedi";i:7031;s:12:"GreenMasUnde";i:7032;s:10:"CathoComed";i:7033;s:8:"SmeStUna";i:7034;s:5:"Undig";i:7035;s:8:"MicProba";i:7036;s:5:"Unvai";i:7037;s:8:"PreStoTo";i:7038;s:6:"LibWar";i:7039;s:9:"SaccVampi";i:7040;s:9:"ScoSqWanl";i:7041;s:13:"IndiKiloZephy";i:7042;s:3:"Rah";i:7043;s:8:"UncoUnUn";i:7044;s:7:"SwartUn";i:7045;s:4:"Sord";i:7046;s:4:"Nasi";i:7047;s:4:"Phil";i:7048;s:4:"Suss";i:7049;s:9:"HomopRiSp";i:7050;s:3:"Sca";i:7051;s:2:"Bi";i:7052;s:5:"Unret";i:7053;s:5:"LocTo";i:7054;s:6:"RubUnd";i:7055;s:4:"Foca";i:7056;s:7:"PePreRu";i:7057;s:9:"BaptiUnpa";i:7058;s:5:"ScSig";i:7059;s:11:"EndGenevUns";i:7060;s:6:"LepSpi";i:7061;s:4:"Vorl";i:7062;s:4:"Thro";i:7063;s:9:"FugHaHete";i:7064;s:8:"InteNote";i:7065;s:6:"PapUnd";i:7066;s:5:"Lands";i:7067;s:11:"FluPseuThel";i:7068;s:5:"Letha";i:7069;s:8:"GomaRidi";i:7070;s:9:"CourtMadd";i:7071;s:9:"NovePenSh";i:7072;s:2:"Ut";i:7073;s:4:"Vatm";i:7074;s:4:"Taxi";i:7075;s:7:"QuaveSp";i:7076;s:9:"MonstPred";i:7077;s:11:"PedTriUntoi";i:7078;s:11:"PlaUpteVivi";i:7079;s:8:"SurrTabl";i:7080;s:4:"Imid";i:7081;s:6:"NonZoo";i:7082;s:9:"PalaTrich";i:7083;s:5:"Thecl";i:7084;s:13:"OphPaleoUnout";i:7085;s:12:"DrumNebQuila";i:7086;s:11:"EpMiniProag";i:7087;s:13:"MuleRotuTriac";i:7088;s:5:"Ostra";i:7089;s:5:"Endoc";i:7090;s:12:"HydrVaiYouwa";i:7091;s:5:"RevSe";i:7092;s:8:"DermSigh";i:7093;s:5:"Splat";i:7094;s:5:"MerUn";i:7095;s:8:"KrisPaPr";i:7096;s:9:"LodgMaPol";i:7097;s:9:"FlMenoVul";i:7098;s:8:"MyelaRab";i:7099;s:5:"Commo";i:7100;s:10:"HaPhyTorcx";i:7101;s:10:"NomiReUnfo";i:7102;s:5:"Unwal";i:7103;s:10:"MucSiniTea";i:7104;s:9:"PsSolUnha";i:7105;s:7:"CraHyPr";i:7106;s:12:"IntRheuSerop";i:7107;s:12:"ManWindYouth";i:7108;s:8:"NotoSemi";i:7109;s:11:"MaPhytPosts";i:7110;s:8:"DeaPeWal";i:7111;s:12:"NotaPrimaSou";i:7112;s:4:"Sera";i:7113;s:7:"PaSaUnt";i:7114;s:6:"SimSup";i:7115;s:9:"MalpPhoto";i:7116;s:11:"AsbConiPred";i:7117;s:10:"MetatSpoon";i:7118;s:3:"Hol";i:7119;s:8:"DilLoTap";i:7120;s:4:"OuSe";i:7121;s:4:"SeYe";i:7122;s:7:"SulpUns";i:7123;s:7:"FliQuak";i:7124;s:8:"OphtRecu";i:7125;s:6:"CheNon";i:7126;s:12:"PandSarcoUns";i:7127;s:9:"MeMuleOxy";i:7128;s:10:"CerGarisMe";i:7129;s:9:"BoDuskPer";i:7130;s:4:"Moos";i:7131;s:13:"LiberPeaRolle";i:7132;s:4:"Unhi";i:7133;s:9:"ReadUnroo";i:7134;s:6:"BeaUnu";i:7135;s:5:"Taget";i:7136;s:8:"CellExPr";i:7137;s:7:"PaoScSu";i:7138;s:11:"DonHaeSubro";i:7139;s:3:"Gun";i:7140;s:8:"HePyraSu";i:7141;s:13:"AscaErythLett";i:7142;s:10:"HypocZoonu";i:7143;s:7:"SpewiUn";i:7144;s:5:"Sylva";i:7145;s:8:"MesSteno";i:7146;s:9:"RhomTiger";i:7147;s:4:"Rumm";i:7148;s:2:"Hu";i:7149;s:9:"DosGeopRe";i:7150;s:5:"GlWot";i:7151;s:7:"PaUnUno";i:7152;s:8:"CapeYenx";i:7153;s:10:"TouchTrUnf";i:7154;s:11:"EnaPreRigwi";i:7155;s:9:"TyphlUnUn";i:7156;s:12:"MecoMuddSpea";i:7157;s:4:"Myel";i:7158;s:10:"ExcoReiSej";i:7159;s:11:"ConjMotorZy";i:7160;s:6:"JoinUn";i:7161;s:10:"ExudSympUn";i:7162;s:10:"DisHorseSp";i:7163;s:8:"PreTherm";i:7164;s:7:"SupeTuc";i:7165;s:9:"CaDemSame";i:7166;s:5:"Zenag";i:7167;s:3:"Rug";i:7168;s:7:"RatioZo";i:7169;s:12:"InconPrQuidd";i:7170;s:8:"DevoiPal";i:7171;s:11:"CaIndaTourn";i:7172;s:5:"Infer";i:7173;s:9:"HydroPrec";i:7174;s:4:"Yout";i:7175;s:3:"Hak";i:7176;s:7:"UntZeph";i:7177;s:4:"Peed";i:7178;s:8:"MetPiTuc";i:7179;s:11:"LiNonsaOsci";i:7180;s:3:"Hea";i:7181;s:11:"PiprPostSen";i:7182;s:10:"MiSniUnmet";i:7183;s:6:"CaiExt";i:7184;s:6:"UnfWom";i:7185;s:7:"NonfUnw";i:7186;s:6:"NajNon";i:7187;s:10:"PaProTruck";i:7188;s:10:"RemoSemiSt";i:7189;s:8:"PerUnsym";i:7190;s:5:"Xyrid";i:7191;s:7:"AnaChun";i:7192;s:7:"HaLepro";i:7193;s:5:"Veuve";i:7194;s:14:"NoaxOverpWanto";i:7195;s:4:"Lith";i:7196;s:7:"FlaTara";i:7197;s:7:"PsychTe";i:7198;s:3:"Fra";i:7199;s:5:"Sitti";i:7200;s:13:"SnottTroTwitt";i:7201;s:9:"LampWreck";i:7202;s:8:"SuperVar";i:7203;s:7:"NondSym";i:7204;s:7:"FeetaSo";i:7205;s:9:"GrafNonfi";i:7206;s:2:"Od";i:7207;s:5:"Butan";i:7208;s:9:"LippLiPhy";i:7209;s:5:"ParPr";i:7210;s:7:"ScuUnUs";i:7211;s:6:"InPsUn";i:7212;s:7:"PipSaye";i:7213;s:9:"MatcStirr";i:7214;s:13:"ParrTenuUnpla";i:7215;s:7:"DurEnMe";i:7216;s:5:"PoPor";i:7217;s:11:"CriCutSauce";i:7218;s:10:"StViceWeax";i:7219;s:9:"FingNeuri";i:7220;s:6:"SubVir";i:7221;s:7:"TrWildi";i:7222;s:6:"ReSeUn";i:7223;s:10:"IsoPuSandl";i:7224;s:6:"SenSpl";i:7225;s:12:"LabryPrUnfel";i:7226;s:10:"LingaOpist";i:7227;s:10:"HydPeUninq";i:7228;s:15:"CloudObeyeUnder";i:7229;s:4:"Post";i:7230;s:7:"CrPhoto";i:7231;s:10:"MoPredePre";i:7232;s:12:"CardSchisWhi";i:7233;s:11:"JapKolVenom";i:7234;s:4:"Unfl";i:7235;s:8:"PhacVers";i:7236;s:3:"Lab";i:7237;s:3:"Jig";i:7238;s:10:"CorybIrrem";i:7239;s:9:"TaguUnimp";i:7240;s:5:"Whipp";i:7241;s:4:"Stoc";i:7242;s:13:"ConveOwregShi";i:7243;s:10:"BromoCaFor";i:7244;s:9:"MoStahlTy";i:7245;s:14:"IndigLoriStaye";i:7246;s:10:"ElProeUnsu";i:7247;s:8:"PanUnrid";i:7248;s:5:"Prehe";i:7249;s:3:"Leg";i:7250;s:9:"BotryPrem";i:7251;s:5:"UnWed";i:7252;s:7:"CraPudd";i:7253;s:12:"HainSnonSulf";i:7254;s:12:"PerSouthUnpo";i:7255;s:8:"ElGrafTh";i:7256;s:8:"CrassWil";i:7257;s:9:"ConNonOly";i:7258;s:5:"Schni";i:7259;s:9:"TertZygom";i:7260;s:11:"ScrVacYawwe";i:7261;s:10:"ButDisceTr";i:7262;s:11:"ChoriEcarVe";i:7263;s:4:"Femi";i:7264;s:7:"NeUnpos";i:7265;s:13:"MurPerveRunol";i:7266;s:5:"PrSig";i:7267;s:10:"EmbMidmTro";i:7268;s:8:"CestInca";i:7269;s:5:"CorPy";i:7270;s:12:"BylSpoonTymp";i:7271;s:9:"NotiSlunk";i:7272;s:5:"Whitr";i:7273;s:4:"Unba";i:7274;s:10:"MonatTheor";i:7275;s:14:"IdioShoalTamac";i:7276;s:7:"PartiTo";i:7277;s:11:"CounCraMill";i:7278;s:4:"Uppe";i:7279;s:9:"PatavPewt";i:7280;s:4:"CoSa";i:7281;s:4:"Vase";i:7282;s:6:"ColcNo";i:7283;s:14:"ThrifTransWate";i:7284;s:10:"NoTacTartu";i:7285;s:12:"ReiUnforUngl";i:7286;s:8:"CrExpaEx";i:7287;s:5:"ParPh";i:7288;s:6:"SkylUn";i:7289;s:5:"StaUn";i:7290;s:10:"FibuSlTric";i:7291;s:10:"KartvMagne";i:7292;s:13:"BotcCatstSenc";i:7293;s:7:"EnduHem";i:7294;s:12:"MammaStrUnfo";i:7295;s:9:"SlidSpUro";i:7296;s:13:"PatSnabbVolat";i:7297;s:5:"Mysti";i:7298;s:4:"OsSc";i:7299;s:5:"Trick";i:7300;s:5:"Cohen";i:7301;s:12:"HypMoralTinh";i:7302;s:10:"PettySacri";i:7303;s:8:"StrowUlt";i:7304;s:5:"Circu";i:7305;s:4:"Rifl";i:7306;s:13:"FlocxLaxUnacc";i:7307;s:10:"HirsMyodPe";i:7308;s:5:"LiMic";i:7309;s:9:"MesRainWa";i:7310;s:8:"BuntiSyp";i:7311;s:12:"AgroConveOrt";i:7312;s:4:"Rais";i:7313;s:8:"NeSemiTr";i:7314;s:7:"CrGenPe";i:7315;s:7:"RecUnch";i:7316;s:10:"LatchPosse";i:7317;s:9:"BavenWeal";i:7318;s:5:"NonSp";i:7319;s:6:"MontVo";i:7320;s:15:"MisfaNarcaNonto";i:7321;s:5:"DeTuc";i:7322;s:8:"CountEuc";i:7323;s:10:"HaiIliocUn";i:7324;s:10:"CoaptLumPa";i:7325;s:5:"Thoms";i:7326;s:5:"TaTop";i:7327;s:8:"PhalaVib";i:7328;s:6:"BoraKa";i:7329;s:12:"TassTempeVir";i:7330;s:8:"MonosNam";i:7331;s:10:"SpiTethThe";i:7332;s:13:"QuerUnchiVare";i:7333;s:6:"BipNon";i:7334;s:9:"JaPolyoSi";i:7335;s:12:"AmbiEntGloam";i:7336;s:13:"MetalRegalSup";i:7337;s:10:"ErotiRetun";i:7338;s:12:"PyraSteprSyn";i:7339;s:9:"StabUngui";i:7340;s:5:"Pates";i:7341;s:12:"CaEnseeRunbo";i:7342;s:13:"NeurPteroSoma";i:7343;s:10:"RitarSquTa";i:7344;s:3:"Hum";i:7345;s:7:"PangePo";i:7346;s:5:"InOut";i:7347;s:11:"CoExtoUncan";i:7348;s:11:"HomoeNoctUn";i:7349;s:7:"BuPoRes";i:7350;s:8:"PhalRect";i:7351;s:3:"Mea";i:7352;s:13:"JoyleNotReplu";i:7353;s:4:"Nonh";i:7354;s:4:"Reco";i:7355;s:2:"Fu";i:7356;s:9:"OctSaSpic";i:7357;s:8:"NinUntes";i:7358;s:5:"NoSpl";i:7359;s:5:"OuSte";i:7360;s:9:"ResurStau";i:7361;s:14:"RevokSclerThan";i:7362;s:5:"Tombl";i:7363;s:10:"IsMarooNaz";i:7364;s:11:"OtotoSeriSo";i:7365;s:8:"CerePleu";i:7366;s:7:"ForaPse";i:7367;s:8:"CentTube";i:7368;s:14:"CebuEkamaSauce";i:7369;s:10:"GlenxHypog";i:7370;s:6:"HyOafx";i:7371;s:9:"RackWillo";i:7372;s:5:"LoOgh";i:7373;s:9:"WigfuWint";i:7374;s:14:"IndiOrthoProdu";i:7375;s:12:"ClCoconUnpre";i:7376;s:12:"ExpFlunkMega";i:7377;s:9:"ChorInsWo";i:7378;s:4:"Hair";i:7379;s:6:"UncWin";i:7380;s:7:"PaPsoro";i:7381;s:9:"TradUndit";i:7382;s:9:"MakeUnlik";i:7383;s:10:"AntiApMeta";i:7384;s:7:"PhoPhyt";i:7385;s:7:"ColMust";i:7386;s:10:"CathaConPh";i:7387;s:8:"NoTromVe";i:7388;s:3:"Cos";i:7389;s:10:"CapDunPres";i:7390;s:14:"HypanIncuLeopa";i:7391;s:6:"UnasWe";i:7392;s:5:"ExpUn";i:7393;s:5:"IntUn";i:7394;s:5:"Overf";i:7395;s:5:"Rameq";i:7396;s:9:"ImmLoxoUn";i:7397;s:5:"EmaGi";i:7398;s:13:"EsoEurasUnlab";i:7399;s:8:"LobaPrZo";i:7400;s:9:"ProaUnrYa";i:7401;s:8:"SubUnleg";i:7402;s:9:"ComaExNeg";i:7403;s:10:"HandwPrUnc";i:7404;s:3:"Bre";i:7405;s:7:"QuatrVo";i:7406;s:3:"Erm";i:7407;s:8:"ParThioc";i:7408;s:7:"NoctiTr";i:7409;s:7:"ElUncha";i:7410;s:11:"PitiRecStri";i:7411;s:4:"OtPa";i:7412;s:9:"GanoMyrrh";i:7413;s:4:"Redu";i:7414;s:8:"HuPifTon";i:7415;s:6:"NeUnmu";i:7416;s:5:"GeHus";i:7417;s:14:"DisafProvQuino";i:7418;s:11:"LoydQuReman";i:7419;s:3:"Sta";i:7420;s:12:"RefTeasUnchi";i:7421;s:6:"ClaTam";i:7422;s:5:"Disho";i:7423;s:11:"BaNowneRein";i:7424;s:7:"CoFocus";i:7425;s:6:"SemiWa";i:7426;s:14:"BottoObsceUltr";i:7427;s:4:"Shoa";i:7428;s:7:"MonoTri";i:7429;s:10:"LackQuadUn";i:7430;s:13:"SarcTetaTrump";i:7431;s:11:"UmbUntawVin";i:7432;s:14:"NewlSkewnUnpar";i:7433;s:4:"Podo";i:7434;s:3:"Wor";i:7435;s:12:"InterKeratMy";i:7436;s:11:"ResStrTesta";i:7437;s:5:"Unfat";i:7438;s:4:"Prof";i:7439;s:5:"Misco";i:7440;s:10:"ChurLimbUn";i:7441;s:5:"Rammi";i:7442;s:10:"CarCeleoSn";i:7443;s:9:"DysaSeven";i:7444;s:4:"Inno";i:7445;s:5:"PhoUn";i:7446;s:7:"QuiReUn";i:7447;s:14:"AlgarBrumEquiv";i:7448;s:4:"FiWo";i:7449;s:5:"Wuthe";i:7450;s:11:"MoguePorSpr";i:7451;s:7:"HiMarRe";i:7452;s:9:"UndeUnder";i:7453;s:13:"PinUndelUpspe";i:7454;s:9:"SomitSwSy";i:7455;s:7:"FeGaMed";i:7456;s:10:"ImmPowSore";i:7457;s:7:"DisUnop";i:7458;s:9:"LactOliUn";i:7459;s:5:"GroVe";i:7460;s:6:"StTele";i:7461;s:3:"Exc";i:7462;s:4:"MePa";i:7463;s:8:"SubcZaca";i:7464;s:9:"SyUnsWast";i:7465;s:2:"Gh";i:7466;s:9:"RecRobTum";i:7467;s:11:"DistPreUnre";i:7468;s:12:"EbHoerxMatro";i:7469;s:13:"BeeCorreGetae";i:7470;s:12:"LariOutflPro";i:7471;s:9:"InSmVocab";i:7472;s:5:"Hecta";i:7473;s:6:"IgnaTw";i:7474;s:5:"Seric";i:7475;s:8:"SaeSeiTi";i:7476;s:6:"OppPat";i:7477;s:5:"MoTha";i:7478;s:6:"NonvSk";i:7479;s:4:"Sarc";i:7480;s:5:"Autho";i:7481;s:13:"BestoStiUncla";i:7482;s:9:"CanToolUn";i:7483;s:9:"FornModio";i:7484;s:9:"RhapWartx";i:7485;s:10:"GynodImKra";i:7486;s:13:"FirepPreShinz";i:7487;s:12:"KroLucuMetem";i:7488;s:6:"ReUnsu";i:7489;s:5:"Rooib";i:7490;s:9:"ExpFiGout";i:7491;s:8:"EuTeYess";i:7492;s:11:"PluRecReins";i:7493;s:3:"Com";i:7494;s:5:"FooUn";i:7495;s:4:"Tort";i:7496;s:4:"Brac";i:7497;s:5:"EquTo";i:7498;s:9:"ExTrUnsmi";i:7499;s:4:"Lobe";i:7500;s:5:"GynSu";i:7501;s:10:"PondSxTric";i:7502;s:9:"FocaUnreq";i:7503;s:6:"IrrePr";i:7504;s:4:"Styt";i:7505;s:4:"FoPi";i:7506;s:6:"NaTenn";i:7507;s:5:"Bookb";i:7508;s:3:"Yet";i:7509;s:10:"MyxedSpinn";i:7510;s:9:"AweeNonco";i:7511;s:8:"PrenRecr";i:7512;s:7:"CoImmod";i:7513;s:8:"ApocEeMe";i:7514;s:11:"GiNilsxVisa";i:7515;s:4:"Taxe";i:7516;s:4:"Epip";i:7517;s:10:"ConduMeSub";i:7518;s:10:"CrouReVerm";i:7519;s:10:"OversWeath";i:7520;s:6:"ChMeso";i:7521;s:4:"Dipl";i:7522;s:6:"PlinTh";i:7523;s:7:"OrPreSu";i:7524;s:10:"HipPrRevis";i:7525;s:7:"SchSter";i:7526;s:8:"PlasPlum";i:7527;s:5:"UnWol";i:7528;s:7:"MightSp";i:7529;s:9:"PseuSynca";i:7530;s:7:"PsUnpur";i:7531;s:7:"LynxxMa";i:7532;s:4:"Clun";i:7533;s:12:"OverPhlResil";i:7534;s:12:"MetSatiStabl";i:7535;s:5:"Scien";i:7536;s:3:"Tee";i:7537;s:6:"NoVago";i:7538;s:8:"HomMortg";i:7539;s:4:"Stic";i:7540;s:9:"CaCongrUn";i:7541;s:4:"Whar";i:7542;s:9:"OverQuadr";i:7543;s:8:"MisPlata";i:7544;s:9:"CaiSquUnh";i:7545;s:10:"TenUncouUn";i:7546;s:7:"OutTrom";i:7547;s:3:"Lec";i:7548;s:7:"MoSpili";i:7549;s:6:"CaUnab";i:7550;s:10:"MuddPsUnwa";i:7551;s:12:"CoseExpUncon";i:7552;s:5:"Verde";i:7553;s:7:"CoMoWes";i:7554;s:7:"SimuSli";i:7555;s:9:"MylodTran";i:7556;s:7:"CymbSub";i:7557;s:10:"ImThyUnfor";i:7558;s:4:"Stev";i:7559;s:7:"HoploTa";i:7560;s:7:"CoRabix";i:7561;s:7:"OdoTurk";i:7562;s:3:"Fre";i:7563;s:7:"JoKorex";i:7564;s:10:"CofNoneUnc";i:7565;s:3:"Ery";i:7566;s:7:"MusResu";i:7567;s:5:"Valid";i:7568;s:7:"OncoShi";i:7569;s:11:"FoKibbUndim";i:7570;s:3:"Tim";i:7571;s:8:"MortThWi";i:7572;s:13:"ReaalUnsedUnt";i:7573;s:4:"Degl";i:7574;s:9:"MiniOutPe";i:7575;s:13:"HelioHyetMemo";i:7576;s:11:"InvePrediSl";i:7577;s:7:"GhostHe";i:7578;s:10:"RevUncUntr";i:7579;s:5:"Bemai";i:7580;s:7:"TrikeWh";i:7581;s:8:"TelTikix";i:7582;s:9:"MaliUncir";i:7583;s:12:"ForksHepTher";i:7584;s:7:"HypNond";i:7585;s:5:"Wintr";i:7586;s:7:"JeeSubm";i:7587;s:9:"InadeOrUn";i:7588;s:11:"MilnOversTr";i:7589;s:4:"Thal";i:7590;s:3:"Duk";i:7591;s:7:"SleUpla";i:7592;s:2:"Ha";i:7593;s:5:"PeUnm";i:7594;s:9:"DeHipOryz";i:7595;s:4:"PhTe";i:7596;s:8:"UnpZygos";i:7597;s:11:"LovRefiUnco";i:7598;s:6:"DwHodd";i:7599;s:3:"Phi";i:7600;s:7:"DonatSu";i:7601;s:4:"Gran";i:7602;s:8:"LaboTurr";i:7603;s:5:"Geode";i:7604;s:3:"Gle";i:7605;s:5:"Hebra";i:7606;s:4:"Crot";i:7607;s:10:"BloPahSupe";i:7608;s:8:"PostmSer";i:7609;s:6:"IncoUn";i:7610;s:4:"Rere";i:7611;s:11:"ObstTetraUn";i:7612;s:13:"ForehSanieUnc";i:7613;s:6:"SpToma";i:7614;s:5:"Triha";i:7615;s:13:"GallProceWeis";i:7616;s:10:"NonpePolys";i:7617;s:8:"PartrPol";i:7618;s:5:"Volle";i:7619;s:7:"BockPle";i:7620;s:9:"DeDichWel";i:7621;s:10:"IncIndTher";i:7622;s:5:"Minor";i:7623;s:5:"Shado";i:7624;s:6:"ShSlav";i:7625;s:12:"MazalVelVera";i:7626;s:12:"EmargSaUnapp";i:7627;s:11:"OveTussoUnc";i:7628;s:13:"NovaToniUnfor";i:7629;s:12:"FormOnaOutba";i:7630;s:7:"LouvReg";i:7631;s:8:"SerShZau";i:7632;s:10:"EbullIncav";i:7633;s:12:"SmirkStryUna";i:7634;s:7:"StraiUn";i:7635;s:4:"Besn";i:7636;s:6:"DuchIm";i:7637;s:4:"Cowp";i:7638;s:8:"GermJuMi";i:7639;s:11:"BurdHydProm";i:7640;s:9:"PeSeTimbe";i:7641;s:3:"Cac";i:7642;s:6:"MortUn";i:7643;s:5:"Straw";i:7644;s:10:"ExterProph";i:7645;s:11:"RetroSwTele";i:7646;s:6:"ThiUnf";i:7647;s:4:"Unsk";i:7648;s:9:"AnnodHoIl";i:7649;s:5:"ErgEt";i:7650;s:13:"JoviPreabSnee";i:7651;s:5:"Gentl";i:7652;s:3:"Bet";i:7653;s:12:"NuPebaxTitan";i:7654;s:4:"Tato";i:7655;s:7:"SulphUn";i:7656;s:5:"DubSt";i:7657;s:9:"PlowwUnmi";i:7658;s:9:"PaPhotPis";i:7659;s:5:"Subex";i:7660;s:11:"OptoProSubc";i:7661;s:4:"Rout";i:7662;s:4:"Vapo";i:7663;s:9:"HyMidriSy";i:7664;s:7:"NulliWi";i:7665;s:9:"FerStoUnt";i:7666;s:7:"LusSaun";i:7667;s:7:"SterUnc";i:7668;s:5:"BlOst";i:7669;s:7:"InMicro";i:7670;s:5:"Induc";i:7671;s:4:"Tope";i:7672;s:4:"Disi";i:7673;s:5:"SuUte";i:7674;s:7:"CohMiRe";i:7675;s:11:"AmpuScroWin";i:7676;s:10:"MilToUncit";i:7677;s:12:"CenTerrVesic";i:7678;s:8:"QuixUnhu";i:7679;s:10:"SupeTifTub";i:7680;s:4:"Tens";i:7681;s:4:"SeSy";i:7682;s:5:"Precu";i:7683;s:4:"LiSe";i:7684;s:5:"HerLe";i:7685;s:8:"PapyPhot";i:7686;s:9:"ShantSooh";i:7687;s:10:"TradeVicto";i:7688;s:10:"GuardHiYex";i:7689;s:7:"AlkSpra";i:7690;s:4:"EmSt";i:7691;s:8:"HalosPor";i:7692;s:12:"GrotReasVerb";i:7693;s:13:"InchoNonprPor";i:7694;s:8:"LievLubr";i:7695;s:8:"PhalWith";i:7696;s:7:"HemerLu";i:7697;s:9:"HaMoldUnr";i:7698;s:8:"SpanUnwi";i:7699;s:9:"ConnStTri";i:7700;s:7:"HypSuki";i:7701;s:4:"None";i:7702;s:14:"OverPartuUncal";i:7703;s:13:"JessSperSpide";i:7704;s:11:"ReSynodUpba";i:7705;s:4:"Unsq";i:7706;s:10:"MystaReint";i:7707;s:8:"SubcTeno";i:7708;s:7:"ImpaSom";i:7709;s:10:"PsychUncom";i:7710;s:8:"OverProc";i:7711;s:8:"OvePrRec";i:7712;s:6:"DaiPri";i:7713;s:9:"HeavThUnp";i:7714;s:9:"MesolUntr";i:7715;s:11:"SulUncUncoq";i:7716;s:7:"OvileUn";i:7717;s:10:"EnthuSiale";i:7718;s:10:"ErythOrYen";i:7719;s:6:"DeUnVa";i:7720;s:14:"SemidUnpitVege";i:7721;s:8:"CannWhee";i:7722;s:8:"FratPhRe";i:7723;s:7:"MuSulph";i:7724;s:9:"GhaNudiSl";i:7725;s:7:"SomSuTr";i:7726;s:5:"Nomen";i:7727;s:2:"Be";i:7728;s:7:"LabPreb";i:7729;s:6:"UnqUns";i:7730;s:10:"IntraVespe";i:7731;s:8:"HoUncVot";i:7732;s:8:"GroinSto";i:7733;s:5:"Cooke";i:7734;s:12:"OligoScarVol";i:7735;s:12:"CarGoldUndel";i:7736;s:9:"ReceSqUnd";i:7737;s:7:"GaliJam";i:7738;s:3:"Ole";i:7739;s:7:"LaSilve";i:7740;s:3:"Pne";i:7741;s:3:"Sig";i:7742;s:12:"HugPseudVert";i:7743;s:12:"IleocPolVera";i:7744;s:6:"PrShak";i:7745;s:5:"HyRie";i:7746;s:4:"Swee";i:7747;s:12:"PetThelWinet";i:7748;s:11:"DongoHeReci";i:7749;s:9:"MonoPaZyg";i:7750;s:8:"FrowzGap";i:7751;s:6:"GuInte";i:7752;s:4:"File";i:7753;s:6:"AsinHy";i:7754;s:8:"StotUnfi";i:7755;s:11:"RetTetUname";i:7756;s:13:"BiriCedarQuir";i:7757;s:6:"LuPlWe";i:7758;s:6:"IndSty";i:7759;s:7:"ForePlo";i:7760;s:8:"IntOvipo";i:7761;s:13:"TubuUncomUpdr";i:7762;s:9:"BrLilOver";i:7763;s:3:"Kow";i:7764;s:3:"Nut";i:7765;s:3:"Yuk";i:7766;s:11:"PePhoenVege";i:7767;s:8:"PerceUns";i:7768;s:13:"TalloUnqVesic";i:7769;s:4:"Elen";i:7770;s:13:"GriSymmeUsuri";i:7771;s:4:"Esqu";i:7772;s:5:"PaPea";i:7773;s:6:"EtheTo";i:7774;s:7:"PolyPse";i:7775;s:5:"FibUr";i:7776;s:9:"PassiReSo";i:7777;s:14:"EspiGnomoPerch";i:7778;s:9:"InePoUnsp";i:7779;s:10:"SmitSquaTe";i:7780;s:5:"Trunc";i:7781;s:6:"ChClum";i:7782;s:4:"Pear";i:7783;s:8:"BromoCic";i:7784;s:8:"ExItPayr";i:7785;s:6:"GaSulb";i:7786;s:15:"DeindDiscoWoodr";i:7787;s:4:"Sple";i:7788;s:7:"UndreVi";i:7789;s:12:"ImpPerobStur";i:7790;s:5:"Moder";i:7791;s:3:"Det";i:7792;s:8:"SubUndec";i:7793;s:9:"ProphTris";i:7794;s:10:"PlPreSedul";i:7795;s:9:"PedaUlotr";i:7796;s:6:"GeGing";i:7797;s:9:"BugfChrom";i:7798;s:8:"IntPerso";i:7799;s:10:"SicexSplay";i:7800;s:3:"Ces";i:7801;s:12:"PusSelenSten";i:7802;s:11:"NurseReWars";i:7803;s:9:"CoNaUnbir";i:7804;s:5:"Sospi";i:7805;s:7:"CirGeol";i:7806;s:9:"SesquUnpr";i:7807;s:13:"BioLaticPsora";i:7808;s:10:"IntOstarPe";i:7809;s:5:"PiTwa";i:7810;s:5:"Pleom";i:7811;s:13:"PerinProSasse";i:7812;s:10:"GuanUnsWat";i:7813;s:7:"RekiUnt";i:7814;s:7:"CarPoly";i:7815;s:13:"BiangImprLeti";i:7816;s:9:"ObUnapVeg";i:7817;s:13:"OxanaRasSulph";i:7818;s:11:"InidoShuVis";i:7819;s:7:"SciUnha";i:7820;s:5:"Nonpr";i:7821;s:5:"Sandm";i:7822;s:10:"PolygTwUnc";i:7823;s:7:"GenScTa";i:7824;s:8:"PreSuVin";i:7825;s:11:"MeShekUpren";i:7826;s:6:"PaPicr";i:7827;s:3:"Oph";i:7828;s:9:"CracPeria";i:7829;s:9:"DiscDownl";i:7830;s:5:"Trias";i:7831;s:13:"SecSkullVeris";i:7832;s:9:"FructTher";i:7833;s:6:"DiaZea";i:7834;s:4:"Tren";i:7835;s:14:"LudgaMastProge";i:7836;s:8:"ExteMock";i:7837;s:3:"Vir";i:7838;s:5:"RegSt";i:7839;s:7:"OccuUdx";i:7840;s:10:"GrNeriUnha";i:7841;s:10:"SpiraTopic";i:7842;s:5:"Oreas";i:7843;s:5:"Unmou";i:7844;s:3:"Wes";i:7845;s:2:"Ec";i:7846;s:5:"Probo";i:7847;s:9:"DiscoGano";i:7848;s:8:"NonaSmok";i:7849;s:8:"RamTraUn";i:7850;s:9:"LameMusic";i:7851;s:8:"CowPopes";i:7852;s:12:"PorcStrUnbio";i:7853;s:4:"Rash";i:7854;s:11:"FielInteWes";i:7855;s:4:"Anth";i:7856;s:9:"CeCubiSaf";i:7857;s:7:"PeaPoRo";i:7858;s:7:"PrPyram";i:7859;s:7:"DiseaDo";i:7860;s:10:"HeterPeSac";i:7861;s:9:"FixiLaSla";i:7862;s:8:"HuaProSa";i:7863;s:13:"JuglJustTrivi";i:7864;s:5:"Selac";i:7865;s:7:"HaWacke";i:7866;s:3:"Pas";i:7867;s:12:"DeInaniPolyt";i:7868;s:9:"UncomUnim";i:7869;s:5:"Slaug";i:7870;s:8:"UnbloUnc";i:7871;s:6:"NonPho";i:7872;s:6:"ProfTh";i:7873;s:11:"EpictJuriPe";i:7874;s:7:"MerPhyt";i:7875;s:12:"PhiloRoVirgi";i:7876;s:12:"SpVasolWhiff";i:7877;s:7:"NonloPa";i:7878;s:7:"AbFaUnm";i:7879;s:5:"Tetar";i:7880;s:8:"ReideUre";i:7881;s:9:"EsoPrevRe";i:7882;s:5:"Plymo";i:7883;s:10:"HandlNaTra";i:7884;s:12:"DisbGlumVipe";i:7885;s:9:"OppreWhea";i:7886;s:5:"GueKo";i:7887;s:8:"MonuNond";i:7888;s:7:"SalUnpi";i:7889;s:4:"Syne";i:7890;s:14:"SluitWeisWhatk";i:7891;s:10:"GinwOaPerg";i:7892;s:11:"PleurRaUnde";i:7893;s:10:"MetMetPtil";i:7894;s:11:"OchPenSuper";i:7895;s:3:"Jar";i:7896;s:9:"PreciSond";i:7897;s:3:"Des";i:7898;s:9:"HeKenoNon";i:7899;s:13:"ConneNonspOdy";i:7900;s:9:"AspBliIna";i:7901;s:9:"JansePhPi";i:7902;s:6:"RhTamm";i:7903;s:9:"PoetTucka";i:7904;s:6:"TricZo";i:7905;s:4:"Furd";i:7906;s:5:"Squee";i:7907;s:13:"HippoSteTrigo";i:7908;s:14:"CombiInseLoine";i:7909;s:12:"HaemImprQuav";i:7910;s:4:"Unad";i:7911;s:4:"Uros";i:7912;s:4:"Palp";i:7913;s:4:"ReUl";i:7914;s:6:"ExcPea";i:7915;s:6:"PodTri";i:7916;s:14:"QuibbRobiUntem";i:7917;s:5:"Mothe";i:7918;s:12:"MansThorWald";i:7919;s:6:"HearUr";i:7920;s:13:"DarkPalaeUnar";i:7921;s:10:"SpeTeUntra";i:7922;s:5:"Ineff";i:7923;s:9:"LazexMisi";i:7924;s:7:"HarPseu";i:7925;s:13:"IntSalesVeloc";i:7926;s:5:"Halop";i:7927;s:7:"HyPyroc";i:7928;s:11:"HymeJeProdu";i:7929;s:8:"PseSinga";i:7930;s:7:"UtopXan";i:7931;s:5:"OvPro";i:7932;s:11:"SalUnsWoman";i:7933;s:10:"PyrTesToxo";i:7934;s:5:"GunPr";i:7935;s:7:"OveraUn";i:7936;s:12:"LaPacktWease";i:7937;s:5:"Lepto";i:7938;s:6:"MeStTr";i:7939;s:7:"OwlVerm";i:7940;s:4:"Spad";i:7941;s:4:"Unap";i:7942;s:12:"CulgeDideMan";i:7943;s:8:"PoPreaPr";i:7944;s:10:"TelauUnhus";i:7945;s:9:"CalInJuba";i:7946;s:9:"MicUnfVol";i:7947;s:7:"EumeFle";i:7948;s:10:"NumiRuWide";i:7949;s:6:"TritTu";i:7950;s:5:"Sanuk";i:7951;s:7:"HazarQu";i:7952;s:8:"LeroQuUg";i:7953;s:3:"Who";i:7954;s:12:"MakefMetScul";i:7955;s:9:"FamiPluri";i:7956;s:5:"Purse";i:7957;s:9:"PhTissuUn";i:7958;s:12:"AloDemopSens";i:7959;s:9:"InteSluic";i:7960;s:4:"Mesi";i:7961;s:8:"ReTuliUn";i:7962;s:14:"CyanLycanSpenc";i:7963;s:4:"NeRo";i:7964;s:7:"AmmocCe";i:7965;s:6:"MiSika";i:7966;s:8:"LongiNoa";i:7967;s:8:"ColMiPla";i:7968;s:11:"CoincMillPn";i:7969;s:11:"MoraePrUmpx";i:7970;s:7:"PromaSh";i:7971;s:7:"TaTorre";i:7972;s:7:"PresPro";i:7973;s:7:"GayVomi";i:7974;s:7:"EncVagi";i:7975;s:3:"Geo";i:7976;s:12:"GroupOverUnh";i:7977;s:8:"CaltMeNo";i:7978;s:10:"IsochTogVi";i:7979;s:5:"Sphae";i:7980;s:6:"HeHoIn";i:7981;s:4:"Plis";i:7982;s:9:"MisjoPhar";i:7983;s:10:"AscCoUnthi";i:7984;s:12:"HemiPannaSha";i:7985;s:9:"RosiScrol";i:7986;s:8:"RepSepia";i:7987;s:9:"RaReagRev";i:7988;s:5:"SpThe";i:7989;s:8:"FilaPiez";i:7990;s:10:"GeoboHeNec";i:7991;s:7:"OverSen";i:7992;s:3:"Har";i:7993;s:4:"Torp";i:7994;s:9:"OpilUnder";i:7995;s:7:"KaSupra";i:7996;s:4:"Ikat";i:7997;s:5:"Forea";i:7998;s:6:"FoRere";i:7999;s:13:"PeridSecluTru";i:8000;s:10:"JeewhPigeo";i:8001;s:5:"DiPer";i:8002;s:8:"IndTheUn";i:8003;s:13:"ChlorHesSacri";i:8004;s:7:"UncoUnk";i:8005;s:6:"ComInc";i:8006;s:10:"HelLimPlet";i:8007;s:3:"Tau";i:8008;s:13:"HexaRepreSidd";i:8009;s:11:"LeptoReliUt";i:8010;s:3:"Din";i:8011;s:10:"ChronCuSay";i:8012;s:15:"CaphtHymenPsych";i:8013;s:4:"Saim";i:8014;s:5:"RenSh";i:8015;s:6:"AntiFo";i:8016;s:9:"RecadUnad";i:8017;s:3:"Ben";i:8018;s:5:"Forma";i:8019;s:4:"OrQu";i:8020;s:11:"AutGuesInnk";i:8021;s:10:"DacryUltZa";i:8022;s:8:"SubcoTes";i:8023;s:4:"Sapp";i:8024;s:14:"EuchPostaRipco";i:8025;s:7:"PatbRej";i:8026;s:5:"Maxil";i:8027;s:10:"HeMalaProv";i:8028;s:14:"RakexStagVomer";i:8029;s:9:"MonosPrRa";i:8030;s:4:"Oket";i:8031;s:7:"PaTrack";i:8032;s:9:"CausUnYaw";i:8033;s:9:"IntraUncl";i:8034;s:5:"Staro";i:8035;s:4:"Walk";i:8036;s:4:"Pitt";i:8037;s:10:"SappSmaTan";i:8038;s:6:"ReUnhu";i:8039;s:5:"Measl";i:8040;s:9:"UrbaWhimb";i:8041;s:7:"JaSucci";i:8042;s:10:"CoPyrTeles";i:8043;s:9:"MelilScle";i:8044;s:8:"IntPrStr";i:8045;s:11:"CatFrogSwif";i:8046;s:7:"DiscPar";i:8047;s:10:"PeRecoSpon";i:8048;s:10:"EpiOvePont";i:8049;s:14:"PrefeScatTrans";i:8050;s:7:"OutTele";i:8051;s:8:"HenKluxe";i:8052;s:14:"GametGrubrMono";i:8053;s:3:"Sho";i:8054;s:8:"EschaIso";i:8055;s:11:"EcliPropUnd";i:8056;s:6:"DePros";i:8057;s:13:"BetocCaecaInt";i:8058;s:7:"PrescPr";i:8059;s:12:"DeGeniiSkiam";i:8060;s:5:"SlTre";i:8061;s:11:"SeSpooTrich";i:8062;s:8:"PteroZor";i:8063;s:3:"Tru";i:8064;s:6:"FlSter";i:8065;s:12:"ReptiTrUndec";i:8066;s:14:"HyperProphWood";i:8067;s:10:"TimbaUnfis";i:8068;s:8:"CaSenUni";i:8069;s:8:"RathUrrh";i:8070;s:5:"ReSte";i:8071;s:8:"AutoTheg";i:8072;s:5:"Unrus";i:8073;s:10:"CorLePeaco";i:8074;s:10:"DepaMeRewh";i:8075;s:5:"HomUp";i:8076;s:7:"DeguFis";i:8077;s:10:"DreyfRefal";i:8078;s:4:"IlIn";i:8079;s:8:"OutsOvPr";i:8080;s:9:"RoteSulfa";i:8081;s:12:"CladuPolyStu";i:8082;s:5:"Susce";i:8083;s:8:"CoHexaHu";i:8084;s:12:"KnaggPsychUg";i:8085;s:5:"Pytho";i:8086;s:5:"FloTa";i:8087;s:6:"OuSple";i:8088;s:8:"CliPiWee";i:8089;s:4:"Unar";i:8090;s:10:"InvoMarPar";i:8091;s:5:"Homoo";i:8092;s:7:"SaSceni";i:8093;s:6:"FreQua";i:8094;s:13:"ArchsArtilCoc";i:8095;s:7:"MalMult";i:8096;s:5:"Scree";i:8097;s:10:"PhaPoisThy";i:8098;s:7:"GeLemTo";i:8099;s:5:"Perip";i:8100;s:9:"MartWineb";i:8101;s:5:"Trise";i:8102;s:10:"HyRatTehue";i:8103;s:9:"ExImsonUn";i:8104;s:4:"Warf";i:8105;s:11:"LarriMinWit";i:8106;s:6:"TriUps";i:8107;s:11:"MangTaTouch";i:8108;s:10:"ReddiTange";i:8109;s:7:"SwirVal";i:8110;s:11:"IliaInfiNab";i:8111;s:10:"BackFrieUn";i:8112;s:8:"ChaThrom";i:8113;s:5:"JeOve";i:8114;s:5:"Royet";i:8115;s:5:"Plian";i:8116;s:12:"ImprePeUnspi";i:8117;s:5:"Kaffi";i:8118;s:4:"Skim";i:8119;s:6:"MuRewa";i:8120;s:5:"Wicex";i:8121;s:8:"PruReSce";i:8122;s:10:"ConEpiUnli";i:8123;s:9:"MoPresaSc";i:8124;s:3:"Wel";i:8125;s:5:"PyrPy";i:8126;s:9:"CordoStTi";i:8127;s:12:"PetrSteUnsmo";i:8128;s:9:"HissOtocr";i:8129;s:8:"EuMeSlas";i:8130;s:4:"Leth";i:8131;s:5:"IrLon";i:8132;s:9:"AuliIsUin";i:8133;s:6:"GameYe";i:8134;s:9:"NoncNonsa";i:8135;s:12:"FamisPoStrew";i:8136;s:4:"Stan";i:8137;s:8:"ShiSulph";i:8138;s:10:"MyogrResUn";i:8139;s:13:"MultUncheUnco";i:8140;s:7:"KasOpUn";i:8141;s:10:"DiploPaTyl";i:8142;s:4:"Mann";i:8143;s:11:"LuffxUnbUne";i:8144;s:6:"OutsSu";i:8145;s:5:"Shape";i:8146;s:9:"ScreTripe";i:8147;s:13:"CrimeNapPreim";i:8148;s:6:"MiZest";i:8149;s:5:"Suito";i:8150;s:5:"Learx";i:8151;s:14:"CursoOracPenna";i:8152;s:6:"EnstRa";i:8153;s:9:"ChowdPref";i:8154;s:9:"ClansThre";i:8155;s:7:"BlCurub";i:8156;s:6:"SaffUn";i:8157;s:8:"OuScUndr";i:8158;s:7:"DeWista";i:8159;s:7:"AnImmMe";i:8160;s:8:"InscUnra";i:8161;s:9:"RanceUnco";i:8162;s:3:"Lob";i:8163;s:8:"HumorPoo";i:8164;s:4:"GoSu";i:8165;s:6:"TalaTe";i:8166;s:10:"ChoirPoVet";i:8167;s:12:"MinNecrNonra";i:8168;s:4:"Unes";i:8169;s:11:"ForOvePrele";i:8170;s:4:"GaPe";i:8171;s:5:"IntTh";i:8172;s:5:"Prece";i:8173;s:7:"PoikiSc";i:8174;s:13:"CloudMedSuper";i:8175;s:5:"PreQu";i:8176;s:7:"SwagSwi";i:8177;s:10:"MorOutliPr";i:8178;s:13:"NaissNuclePre";i:8179;s:4:"Chla";i:8180;s:4:"Zapt";i:8181;s:10:"VentrWeany";i:8182;s:7:"ParabSa";i:8183;s:9:"CenoFillx";i:8184;s:5:"Undul";i:8185;s:5:"Trade";i:8186;s:8:"BizeColc";i:8187;s:6:"PhPugh";i:8188;s:12:"PreSporSport";i:8189;s:8:"GyneZarz";i:8190;s:6:"CalcIp";i:8191;s:8:"UnqVakia";i:8192;s:9:"SeUnViola";i:8193;s:7:"HySnoop";i:8194;s:5:"MaiTo";i:8195;s:12:"GaviaMeliNic";i:8196;s:4:"Prae";i:8197;s:6:"HorrMo";i:8198;s:8:"TauroWag";i:8199;s:5:"Punic";i:8200;s:4:"Matr";i:8201;s:5:"Jimba";i:8202;s:2:"Ya";i:8203;s:10:"JapRemaScr";i:8204;s:12:"PrThiasUnten";i:8205;s:11:"MisdPhanPsy";i:8206;s:5:"Store";i:8207;s:6:"StTigh";i:8208;s:9:"ApChoreSl";i:8209;s:4:"Refo";i:8210;s:14:"BlowlHydraPump";i:8211;s:11:"PalaeUnpWha";i:8212;s:8:"ArBiUnco";i:8213;s:12:"ConvPrevSuet";i:8214;s:9:"HemocStag";i:8215;s:9:"TutuVario";i:8216;s:6:"HeiUns";i:8217;s:5:"InTen";i:8218;s:8:"SeroSphe";i:8219;s:4:"Tarr";i:8220;s:5:"ExKro";i:8221;s:8:"MukdPoly";i:8222;s:10:"SpliVotWas";i:8223;s:8:"RecomSca";i:8224;s:11:"ManaPneumSo";i:8225;s:3:"Cha";i:8226;s:9:"MicrOvRet";i:8227;s:8:"PreyTwad";i:8228;s:7:"MoNoRes";i:8229;s:7:"EpUnboi";i:8230;s:7:"DodgiUn";i:8231;s:8:"NovSotti";i:8232;s:8:"RenSubSu";i:8233;s:10:"JurisUncVe";i:8234;s:12:"SteatSutToxo";i:8235;s:11:"TucUncUnpro";i:8236;s:8:"EpiSciss";i:8237;s:8:"OleUnave";i:8238;s:5:"CaFor";i:8239;s:9:"PostRedSc";i:8240;s:7:"ImpeSav";i:8241;s:7:"LocSupr";i:8242;s:8:"HaddInSu";i:8243;s:13:"PolitVerWorth";i:8244;s:9:"MaxilSymp";i:8245;s:3:"Wha";i:8246;s:5:"Oroba";i:8247;s:10:"HolocPurSe";i:8248;s:12:"ChEnsmaMonia";i:8249;s:14:"SaponToxeUnven";i:8250;s:9:"CriDosPre";i:8251;s:5:"Turbo";i:8252;s:13:"FisnHyponVine";i:8253;s:3:"Sol";i:8254;s:10:"PunctSuspe";i:8255;s:12:"BedeCaninPer";i:8256;s:3:"Uro";i:8257;s:7:"StiUnsu";i:8258;s:9:"PaleShore";i:8259;s:7:"LeQuadx";i:8260;s:8:"CryExcTa";i:8261;s:8:"PaProUnp";i:8262;s:9:"PinelScol";i:8263;s:8:"CoHyLayl";i:8264;s:8:"HeOptPri";i:8265;s:8:"PaSliTry";i:8266;s:6:"HaPumi";i:8267;s:6:"SofTra";i:8268;s:4:"Redh";i:8269;s:5:"Round";i:8270;s:10:"PhyUltrVel";i:8271;s:9:"TyrtaZami";i:8272;s:12:"ProbRumeTins";i:8273;s:6:"FlooLa";i:8274;s:9:"RaroSolei";i:8275;s:11:"GorgeNavSma";i:8276;s:4:"Dicy";i:8277;s:5:"ReSpi";i:8278;s:9:"InOcPastu";i:8279;s:11:"CounMyStell";i:8280;s:3:"Sau";i:8281;s:4:"InUn";i:8282;s:11:"NonPredSpin";i:8283;s:5:"Outpr";i:8284;s:11:"ComMilRebuf";i:8285;s:10:"HyStraTell";i:8286;s:6:"PiTris";i:8287;s:6:"RoTect";i:8288;s:6:"SloSpe";i:8289;s:10:"MuttoUnpre";i:8290;s:8:"ChUnwoVe";i:8291;s:5:"Thirt";i:8292;s:9:"PredUnres";i:8293;s:12:"FreelHydroIn";i:8294;s:4:"SaUn";i:8295;s:8:"ErraSage";i:8296;s:10:"QuadrSeaYa";i:8297;s:5:"PerSo";i:8298;s:12:"AvenNonNookl";i:8299;s:4:"DaHe";i:8300;s:8:"ChiDemUk";i:8301;s:8:"MaUnWind";i:8302;s:6:"CaInWe";i:8303;s:11:"EndeOsseoUn";i:8304;s:8:"MyriPeri";i:8305;s:10:"PestpSonat";i:8306;s:4:"PhRa";i:8307;s:9:"HawxPemph";i:8308;s:6:"ReTrem";i:8309;s:9:"NonciScot";i:8310;s:10:"MicPerSpon";i:8311;s:3:"Chr";i:8312;s:9:"GuttTrico";i:8313;s:7:"HagiScr";i:8314;s:6:"PlesPo";i:8315;s:10:"MesorMinNi";i:8316;s:8:"DisPlano";i:8317;s:8:"HyaInOut";i:8318;s:6:"OmTosh";i:8319;s:6:"SuTrVa";i:8320;s:12:"CombiPyrenUn";i:8321;s:5:"PhaPo";i:8322;s:8:"ImpInInt";i:8323;s:8:"ParWandx";i:8324;s:4:"Robo";i:8325;s:9:"MulQuiSpa";i:8326;s:6:"MicOsm";i:8327;s:6:"OvZoog";i:8328;s:11:"OveRemanSel";i:8329;s:10:"BrickGuLac";i:8330;s:12:"PapaSakSensi";i:8331;s:11:"HistPondTel";i:8332;s:4:"Vert";i:8333;s:7:"OestrRe";i:8334;s:10:"MagStrTetr";i:8335;s:8:"BridChry";i:8336;s:7:"ResubUn";i:8337;s:10:"EqPolyTabl";i:8338;s:5:"Matri";i:8339;s:10:"ElectWater";i:8340;s:7:"AmnesUn";i:8341;s:7:"GeRetro";i:8342;s:10:"OstPicValu";i:8343;s:10:"HolPrSpatt";i:8344;s:7:"BrighSe";i:8345;s:11:"MusiNonUniv";i:8346;s:9:"NonSittVo";i:8347;s:4:"Rewo";i:8348;s:7:"ResoUra";i:8349;s:9:"MaPaTolfr";i:8350;s:8:"BunWicke";i:8351;s:13:"HobParasPindy";i:8352;s:8:"IllfMiOu";i:8353;s:11:"DisrTabiUnd";i:8354;s:10:"KoffxPaRua";i:8355;s:13:"GogoxSunkeTom";i:8356;s:9:"UnbeWafer";i:8357;s:9:"SecreSpUp";i:8358;s:4:"Mend";i:8359;s:11:"HallmHydrWa";i:8360;s:7:"ChInPer";i:8361;s:10:"IsoQuinSte";i:8362;s:7:"RelUnta";i:8363;s:7:"GigbaLo";i:8364;s:13:"DrumPipeVentr";i:8365;s:4:"Unil";i:8366;s:10:"PneProviSh";i:8367;s:6:"InInte";i:8368;s:8:"LiUnhuUn";i:8369;s:12:"MismaNeWrith";i:8370;s:5:"Preor";i:8371;s:8:"GeiPrUnp";i:8372;s:9:"NonsuPoly";i:8373;s:5:"Ivory";i:8374;s:9:"TaleYowlr";i:8375;s:13:"MitosNotTrisi";i:8376;s:11:"MeniNeuTatu";i:8377;s:14:"ImprMetalUncon";i:8378;s:12:"PessReaSilic";i:8379;s:12:"RooUnmatVina";i:8380;s:5:"Pedan";i:8381;s:5:"Hattx";i:8382;s:4:"Sham";i:8383;s:3:"Bum";i:8384;s:5:"Polyn";i:8385;s:8:"BaeSerfd";i:8386;s:11:"InureUnleWe";i:8387;s:5:"Outmi";i:8388;s:10:"PaleoTaVer";i:8389;s:11:"HexasLaddSt";i:8390;s:7:"ChevrNo";i:8391;s:7:"LanMeMe";i:8392;s:6:"PhoUps";i:8393;s:6:"ResWri";i:8394;s:8:"PlatTerr";i:8395;s:15:"BilgyDisedUgand";i:8396;s:4:"CrDe";i:8397;s:12:"CholeSaTommy";i:8398;s:9:"OmniSheep";i:8399;s:9:"PeaPtQuat";i:8400;s:8:"IsocNilx";i:8401;s:6:"CeHete";i:8402;s:4:"Voci";i:8403;s:6:"SamSca";i:8404;s:4:"Lapa";i:8405;s:10:"DeuSimUnfr";i:8406;s:9:"HucMarsSc";i:8407;s:11:"PerPlatSpor";i:8408;s:12:"GratuHeKaemp";i:8409;s:13:"MastiShipTemp";i:8410;s:7:"PsePycn";i:8411;s:9:"NubecPipp";i:8412;s:12:"FrencManiuUn";i:8413;s:3:"Gas";i:8414;s:9:"MesoScSub";i:8415;s:12:"FomenMerobUn";i:8416;s:8:"OverPhre";i:8417;s:7:"IcostMi";i:8418;s:10:"BioscMisPa";i:8419;s:10:"ArcExtWham";i:8420;s:9:"TickTitex";i:8421;s:9:"ForfNiSpe";i:8422;s:5:"Unbig";i:8423;s:10:"IrredLiftm";i:8424;s:12:"AugeDebbyUnr";i:8425;s:11:"BleekImmPax";i:8426;s:5:"Dowdi";i:8427;s:8:"SilThimb";i:8428;s:14:"NonrePantiStre";i:8429;s:13:"IncesLentoMar";i:8430;s:12:"CytoPonSludx";i:8431;s:7:"CoSuper";i:8432;s:4:"RoTh";i:8433;s:6:"NiSupe";i:8434;s:7:"ExpPhlo";i:8435;s:3:"Via";i:8436;s:9:"PurbYiddi";i:8437;s:10:"OrPedaRush";i:8438;s:5:"LaTec";i:8439;s:3:"Sod";i:8440;s:9:"CorvDreIm";i:8441;s:9:"NoProUngr";i:8442;s:8:"ContInIn";i:8443;s:5:"Stupe";i:8444;s:4:"Sync";i:8445;s:4:"Sloo";i:8446;s:3:"Hep";i:8447;s:7:"DesFePh";i:8448;s:4:"Toxo";i:8449;s:11:"ConjHeLater";i:8450;s:5:"Mistu";i:8451;s:8:"KnapPsSi";i:8452;s:7:"ArFacQu";i:8453;s:13:"PneuSemiWoode";i:8454;s:7:"KnNisha";i:8455;s:9:"PoQueStak";i:8456;s:13:"LegitRelicRom";i:8457;s:7:"HaIchno";i:8458;s:10:"EleutScept";i:8459;s:7:"ProSter";i:8460;s:11:"HatbSequUna";i:8461;s:4:"Micr";i:8462;s:6:"GlGlut";i:8463;s:12:"ChannMicrPre";i:8464;s:4:"Tyri";i:8465;s:4:"Supp";i:8466;s:5:"Unpre";i:8467;s:9:"JiggeSong";i:8468;s:5:"Wards";i:8469;s:5:"FeInd";i:8470;s:3:"Spa";i:8471;s:12:"BuoyDecuRedu";i:8472;s:5:"Wolfh";i:8473;s:7:"PolonRe";i:8474;s:5:"Astro";i:8475;s:14:"ClareUnlaUntom";i:8476;s:4:"Tria";i:8477;s:11:"UnjarUnUnva";i:8478;s:10:"LampMyeUnb";i:8479;s:4:"Unho";i:8480;s:11:"FraiRiXebec";i:8481;s:9:"EndoThUnr";i:8482;s:9:"FulgMaMin";i:8483;s:13:"HydrPosolProf";i:8484;s:10:"EcholSaumo";i:8485;s:13:"CopeDozexMona";i:8486;s:7:"FlePort";i:8487;s:5:"Usnin";i:8488;s:4:"PaUn";i:8489;s:6:"MourSa";i:8490;s:9:"ChiLarNon";i:8491;s:3:"Sna";i:8492;s:11:"KioPhotPrep";i:8493;s:9:"OligSubco";i:8494;s:8:"InflPiUn";i:8495;s:9:"GlaHetTre";i:8496;s:10:"ChronEucPh";i:8497;s:6:"RuiUne";i:8498;s:10:"ResumUngla";i:8499;s:4:"Corp";i:8500;s:7:"IndPape";i:8501;s:8:"PalProje";i:8502;s:4:"Sepi";i:8503;s:9:"OvariSpin";i:8504;s:10:"QuiveUncor";i:8505;s:5:"Picra";i:8506;s:14:"MythiOutbWedan";i:8507;s:6:"ShowUl";i:8508;s:11:"ParasRecRev";i:8509;s:7:"EugGall";i:8510;s:3:"Coe";i:8511;s:6:"SurrWr";i:8512;s:12:"FlabHetePsyc";i:8513;s:5:"ChDev";i:8514;s:9:"NoneUnbek";i:8515;s:12:"DiagrMesPopu";i:8516;s:8:"ShSupeUn";i:8517;s:11:"KeysMeVicto";i:8518;s:13:"ShriTirriWhos";i:8519;s:15:"PhilaPrickRutil";i:8520;s:13:"EnfesRortyThe";i:8521;s:3:"Ket";i:8522;s:12:"GreePerniPur";i:8523;s:7:"OoSorce";i:8524;s:7:"SonioUn";i:8525;s:8:"QuadSaum";i:8526;s:7:"CorCrGr";i:8527;s:11:"PistShaThou";i:8528;s:6:"DisePh";i:8529;s:8:"SatinWit";i:8530;s:7:"SchisSp";i:8531;s:4:"Upcu";i:8532;s:13:"MadeUnconWitc";i:8533;s:4:"Antu";i:8534;s:10:"SecatTaqua";i:8535;s:5:"Proco";i:8536;s:7:"UntwiWr";i:8537;s:8:"PiProRep";i:8538;s:13:"OstScrimVadim";i:8539;s:5:"Subsu";i:8540;s:10:"CruttLappa";i:8541;s:6:"ShUnli";i:8542;s:7:"EpitRes";i:8543;s:4:"Kuld";i:8544;s:4:"Stru";i:8545;s:6:"UncUne";i:8546;s:3:"Tel";i:8547;s:4:"MuTu";i:8548;s:12:"FreckRippRoc";i:8549;s:10:"UnbefVilay";i:8550;s:9:"HydrInflo";i:8551;s:9:"NotidPred";i:8552;s:12:"GoniLustfUnd";i:8553;s:12:"ImbosMaOverb";i:8554;s:7:"ShThymo";i:8555;s:6:"KaMuPl";i:8556;s:10:"PyogRecoRe";i:8557;s:10:"NestoTrUnt";i:8558;s:12:"MissMonodNon";i:8559;s:6:"UncoUn";i:8560;s:6:"UnUned";i:8561;s:3:"Nes";i:8562;s:6:"DumHem";i:8563;s:4:"Yawn";i:8564;s:5:"Medie";i:8565;s:7:"MetSubi";i:8566;s:9:"SeltSteSt";i:8567;s:8:"LivOlivi";i:8568;s:4:"LiPa";i:8569;s:4:"Sarr";i:8570;s:5:"Porit";i:8571;s:8:"NebbyRam";i:8572;s:6:"PloTim";i:8573;s:9:"MilliOrri";i:8574;s:9:"GessHomTh";i:8575;s:6:"HighLi";i:8576;s:13:"HetHypogSupra";i:8577;s:5:"Stave";i:8578;s:8:"LingTass";i:8579;s:4:"StTh";i:8580;s:6:"SeSong";i:8581;s:4:"Gyra";i:8582;s:9:"OnaxUnhos";i:8583;s:4:"Noto";i:8584;s:6:"PeVerb";i:8585;s:5:"Opera";i:8586;s:7:"SaSoUni";i:8587;s:10:"OphrPeUrea";i:8588;s:5:"InWin";i:8589;s:13:"NecesNoweQues";i:8590;s:7:"SauteSu";i:8591;s:7:"BloBrac";i:8592;s:9:"ExHebriLu";i:8593;s:7:"MpangRa";i:8594;s:12:"DemagEllipMo";i:8595;s:10:"ImprSkiWat";i:8596;s:8:"LocTende";i:8597;s:7:"PaUnadv";i:8598;s:11:"SavSlagStra";i:8599;s:8:"BlTasUnd";i:8600;s:4:"Riss";i:8601;s:5:"MoTah";i:8602;s:5:"Hypot";i:8603;s:5:"Prali";i:8604;s:11:"ExsOverSola";i:8605;s:4:"Omni";i:8606;s:10:"MacrSeSupe";i:8607;s:7:"NautWin";i:8608;s:7:"UnswWhe";i:8609;s:5:"Untoo";i:8610;s:5:"Mimmo";i:8611;s:10:"NotoRegrSu";i:8612;s:7:"MonaPse";i:8613;s:9:"ChiRetSqu";i:8614;s:11:"CatarEpiIde";i:8615;s:8:"SerUnrev";i:8616;s:7:"UnoWrit";i:8617;s:9:"MisSuTeas";i:8618;s:8:"MonWhare";i:8619;s:14:"ElzevRoughUtri";i:8620;s:5:"Uninf";i:8621;s:4:"Volu";i:8622;s:9:"ExpOdsRet";i:8623;s:10:"DiaFendPhy";i:8624;s:7:"OversPh";i:8625;s:3:"Van";i:8626;s:3:"Pic";i:8627;s:3:"Hoo";i:8628;s:7:"MetPneu";i:8629;s:11:"GranuTeleTe";i:8630;s:2:"Ep";i:8631;s:5:"Verti";i:8632;s:8:"BerdaRei";i:8633;s:11:"PolStylWasa";i:8634;s:10:"TemUnbowUn";i:8635;s:10:"HiSeldToli";i:8636;s:8:"PlaRaTre";i:8637;s:7:"SalUnro";i:8638;s:6:"LipoQu";i:8639;s:5:"Refle";i:8640;s:9:"OutloScUn";i:8641;s:7:"GrMaVat";i:8642;s:7:"ShardTr";i:8643;s:6:"PolStr";i:8644;s:9:"StacTiTub";i:8645;s:10:"GangGrooZi";i:8646;s:4:"Titi";i:8647;s:7:"LocPala";i:8648;s:4:"Skin";i:8649;s:11:"DiaphRapSpu";i:8650;s:8:"GlarMeta";i:8651;s:7:"InOffic";i:8652;s:4:"Hete";i:8653;s:7:"RefeSuc";i:8654;s:5:"MeSie";i:8655;s:4:"DaMe";i:8656;s:4:"Yard";i:8657;s:4:"Vene";i:8658;s:8:"MelaShUn";i:8659;s:9:"EjPiUnnab";i:8660;s:9:"OrnitPayn";i:8661;s:12:"CotarPhohUnd";i:8662;s:9:"HaitiUnUn";i:8663;s:8:"NealxSup";i:8664;s:4:"Tetr";i:8665;s:8:"UnlitZeu";i:8666;s:9:"GammaGuid";i:8667;s:9:"TaTraUndi";i:8668;s:10:"SizSuWahah";i:8669;s:7:"PoliPun";i:8670;s:3:"Sum";i:8671;s:13:"PenthRegrTasi";i:8672;s:9:"RetroUnat";i:8673;s:11:"ElInveNickx";i:8674;s:14:"EbionParmeSpid";i:8675;s:4:"Gurg";i:8676;s:4:"Ente";i:8677;s:9:"IntMilTer";i:8678;s:11:"HomoPiRetar";i:8679;s:8:"InspReco";i:8680;s:14:"RealSanctSepar";i:8681;s:12:"CyclOuttSlag";i:8682;s:12:"ExogNadiSupe";i:8683;s:4:"LeTe";i:8684;s:10:"BenefInter";i:8685;s:10:"HarmRotUpp";i:8686;s:8:"PseTunna";i:8687;s:10:"PreResSati";i:8688;s:6:"FloTra";i:8689;s:5:"Troph";i:8690;s:4:"InMo";i:8691;s:15:"PlaceRaciaRuntx";i:8692;s:9:"CauSrTolu";i:8693;s:12:"IncomXenopXe";i:8694;s:12:"CaeciCaPaleo";i:8695;s:4:"LoPh";i:8696;s:12:"GamIodyXerop";i:8697;s:5:"Chond";i:8698;s:5:"MeTel";i:8699;s:8:"ObediPou";i:8700;s:4:"PhVa";i:8701;s:5:"UndUr";i:8702;s:5:"Rachi";i:8703;s:11:"BrickGreeLe";i:8704;s:5:"Unreb";i:8705;s:7:"EnwiTew";i:8706;s:13:"EleonHearScha";i:8707;s:4:"PrRe";i:8708;s:8:"SchUnabr";i:8709;s:6:"CoEmMi";i:8710;s:6:"HypeSh";i:8711;s:5:"FuUna";i:8712;s:10:"RabiSanjTr";i:8713;s:13:"ExamiPredUnsh";i:8714;s:12:"InflaNaSchop";i:8715;s:13:"BoldDoliUnpla";i:8716;s:6:"PaUntw";i:8717;s:4:"Tasi";i:8718;s:3:"Cat";i:8719;s:15:"CountMerisSpott";i:8720;s:3:"Sco";i:8721;s:9:"ManPuWhau";i:8722;s:9:"BlocCapri";i:8723;s:9:"BakinLaLi";i:8724;s:9:"ScreStran";i:8725;s:5:"Thick";i:8726;s:10:"DunkPePost";i:8727;s:9:"SkippStim";i:8728;s:4:"Subr";i:8729;s:5:"CaDer";i:8730;s:5:"MarTe";i:8731;s:5:"Terna";i:8732;s:10:"TelThUnver";i:8733;s:6:"NuclPn";i:8734;s:3:"Vim";i:8735;s:4:"Teac";i:8736;s:5:"PaUno";i:8737;s:11:"CompoDiShru";i:8738;s:6:"MonUnr";i:8739;s:6:"SeSwee";i:8740;s:7:"ErgaPol";i:8741;s:11:"EmbOveStrid";i:8742;s:12:"ArsonPrRepud";i:8743;s:9:"PaliProvi";i:8744;s:9:"MopinOxyp";i:8745;s:9:"GobKentUn";i:8746;s:12:"HypeOdontSpi";i:8747;s:12:"ChiRefoUnthr";i:8748;s:7:"PePress";i:8749;s:11:"HiPeriUndep";i:8750;s:12:"LieutSeToywo";i:8751;s:4:"Gunp";i:8752;s:9:"GuSchTele";i:8753;s:9:"DrumMulti";i:8754;s:8:"InflPray";i:8755;s:9:"PrebTubVi";i:8756;s:9:"DeturEnfi";i:8757;s:7:"KishaWe";i:8758;s:12:"MicPapuThala";i:8759;s:5:"Univa";i:8760;s:6:"PruUrx";i:8761;s:5:"Meato";i:8762;s:5:"SiUro";i:8763;s:11:"PterSnarTeb";i:8764;s:9:"MiscoMySy";i:8765;s:10:"ScotcSubVe";i:8766;s:4:"Unwe";i:8767;s:6:"MasPol";i:8768;s:3:"Pra";i:8769;s:5:"SheWe";i:8770;s:12:"NandSeasoWat";i:8771;s:8:"ImpSnake";i:8772;s:9:"HaubeOver";i:8773;s:11:"NaRetroUnma";i:8774;s:7:"SlSombr";i:8775;s:4:"ExTe";i:8776;s:10:"ReducSancy";i:8777;s:6:"CrVent";i:8778;s:5:"Death";i:8779;s:5:"EmTri";i:8780;s:4:"Bric";i:8781;s:5:"Paron";i:8782;s:10:"MyzOmmiaRe";i:8783;s:14:"ChirThereWallo";i:8784;s:10:"ProceUnUnt";i:8785;s:10:"ConSchapSu";i:8786;s:7:"GymPrUn";i:8787;s:7:"PrimiTr";i:8788;s:9:"OitiTraff";i:8789;s:4:"Mini";i:8790;s:10:"RegeStoiZy";i:8791;s:15:"CategInhabResth";i:8792;s:10:"DipEnsHexo";i:8793;s:9:"PlaPsTana";i:8794;s:10:"NontrTiUnd";i:8795;s:7:"CutirMe";i:8796;s:9:"JamdSubtu";i:8797;s:7:"PredoUn";i:8798;s:11:"BenzoHyVoca";i:8799;s:7:"ChDuHus";i:8800;s:12:"TsarUntuVuls";i:8801;s:9:"PrStTrich";i:8802;s:8:"UnbudVer";i:8803;s:6:"SanaUn";i:8804;s:7:"MoScint";i:8805;s:7:"CaDaFac";i:8806;s:8:"AmbCarpo";i:8807;s:10:"KukSipinUn";i:8808;s:7:"SaUnVen";i:8809;s:13:"PentSubbUnthi";i:8810;s:5:"Unbea";i:8811;s:6:"OrgiPe";i:8812;s:7:"ReScSle";i:8813;s:4:"Whis";i:8814;s:11:"DexPalUnher";i:8815;s:4:"Vaga";i:8816;s:4:"Sins";i:8817;s:4:"PhRe";i:8818;s:7:"KatUnsw";i:8819;s:8:"PenSynov";i:8820;s:9:"QuerRxUnl";i:8821;s:3:"Rac";i:8822;s:6:"OstrUb";i:8823;s:7:"GiMoMon";i:8824;s:5:"ReUnb";i:8825;s:5:"Moral";i:8826;s:14:"PhantUrostVajr";i:8827;s:13:"NoncUnfriUnto";i:8828;s:6:"HarPin";i:8829;s:9:"PeSpaTrim";i:8830;s:10:"CollIrToby";i:8831;s:10:"StyTroUnsl";i:8832;s:6:"LuckPh";i:8833;s:5:"Resou";i:8834;s:8:"MatriPap";i:8835;s:4:"Sugg";i:8836;s:3:"Pos";i:8837;s:10:"ReSquamUnh";i:8838;s:8:"HonInves";i:8839;s:6:"ScreSu";i:8840;s:4:"Whel";i:8841;s:5:"InWag";i:8842;s:9:"MembUnfil";i:8843;s:6:"PrUnth";i:8844;s:14:"HierSubnuUnent";i:8845;s:10:"PogRetinUn";i:8846;s:8:"TidedWit";i:8847;s:9:"EphorSnob";i:8848;s:12:"KinetLopeMit";i:8849;s:10:"NoRaspaSub";i:8850;s:11:"AmaMisSarga";i:8851;s:7:"SuThUns";i:8852;s:13:"DefleThreYeme";i:8853;s:3:"Fas";i:8854;s:4:"Viha";i:8855;s:2:"Kh";i:8856;s:10:"GirJudiOxy";i:8857;s:10:"NeurNonPro";i:8858;s:9:"HydroUnco";i:8859;s:5:"Forec";i:8860;s:7:"EmbExub";i:8861;s:5:"Unpho";i:8862;s:13:"AristPreseTra";i:8863;s:8:"PaSyntUn";i:8864;s:7:"HiPledg";i:8865;s:8:"PostUnze";i:8866;s:10:"MallaUngui";i:8867;s:12:"DrOceanUncor";i:8868;s:6:"CalTur";i:8869;s:3:"Vul";i:8870;s:3:"Ult";i:8871;s:8:"RecTitul";i:8872;s:6:"DilaEa";i:8873;s:7:"OnesWhe";i:8874;s:10:"PlSeroTolu";i:8875;s:7:"RecoSoc";i:8876;s:4:"Leuc";i:8877;s:10:"CaninEnPro";i:8878;s:11:"DisStrTrans";i:8879;s:4:"Pani";i:8880;s:6:"PrSten";i:8881;s:4:"Misc";i:8882;s:6:"NoSemi";i:8883;s:13:"GannPodoSlipo";i:8884;s:5:"MyrUn";i:8885;s:5:"Helio";i:8886;s:10:"MonVerniWa";i:8887;s:4:"Gyno";i:8888;s:7:"InnWint";i:8889;s:11:"CeroShoUnav";i:8890;s:8:"PushSiUn";i:8891;s:8:"DioNeSup";i:8892;s:4:"AuTh";i:8893;s:7:"FeHyPyr";i:8894;s:9:"MePamiUnl";i:8895;s:7:"MePikey";i:8896;s:5:"MacUn";i:8897;s:9:"PoleUnexc";i:8898;s:9:"GeocViper";i:8899;s:9:"JointUnUr";i:8900;s:9:"BuCheTigx";i:8901;s:5:"Ochra";i:8902;s:9:"NjaNoStro";i:8903;s:12:"CnemiGrUnreq";i:8904;s:9:"MellRamSc";i:8905;s:11:"InPistoSubr";i:8906;s:6:"ElamKe";i:8907;s:9:"CounDiagr";i:8908;s:8:"CorRevil";i:8909;s:11:"PurSuoUrini";i:8910;s:3:"Sma";i:8911;s:8:"GralHudd";i:8912;s:10:"BurgaGossi";i:8913;s:8:"FimbIdeo";i:8914;s:5:"Viril";i:8915;s:13:"JuverPredeRep";i:8916;s:7:"PhysiUn";i:8917;s:7:"NiTrans";i:8918;s:11:"IsPrickTrol";i:8919;s:7:"GoOment";i:8920;s:9:"TectUnfus";i:8921;s:10:"OvProtUnen";i:8922;s:5:"LeTre";i:8923;s:4:"Vaul";i:8924;s:8:"PhenRecu";i:8925;s:7:"SaTwelv";i:8926;s:5:"Trypt";i:8927;s:5:"Schiz";i:8928;s:6:"FeNonc";i:8929;s:12:"PolarScirSer";i:8930;s:8:"PraseTin";i:8931;s:10:"OfTattlWis";i:8932;s:4:"ShUn";i:8933;s:11:"GiNemeVicar";i:8934;s:7:"FotUnab";i:8935;s:6:"HeMiOr";i:8936;s:13:"FanxLegatUnid";i:8937;s:3:"Med";i:8938;s:14:"EarnScoroSlaye";i:8939;s:7:"GujrSci";i:8940;s:5:"VeViv";i:8941;s:5:"MeaUn";i:8942;s:6:"PtSpor";i:8943;s:6:"MoisSp";i:8944;s:10:"MopinOtViv";i:8945;s:12:"ExognPanoSpo";i:8946;s:7:"HedSuff";i:8947;s:7:"FifteRe";i:8948;s:4:"Sack";i:8949;s:9:"PiRasSerr";i:8950;s:3:"Vam";i:8951;s:11:"EnFlinPertu";i:8952;s:5:"Vexed";i:8953;s:5:"HenSu";i:8954;s:5:"Picad";i:8955;s:10:"CuirReTusc";i:8956;s:11:"PinPreaSeco";i:8957;s:14:"LaddePapaiUrey";i:8958;s:11:"GirlMoPhoen";i:8959;s:5:"Subli";i:8960;s:9:"UnjaVerti";i:8961;s:9:"ReUnUndes";i:8962;s:4:"PsTe";i:8963;s:7:"ParaWhi";i:8964;s:9:"LetxRetou";i:8965;s:9:"PinfeRass";i:8966;s:12:"RacReadStyle";i:8967;s:7:"TriclUn";i:8968;s:7:"SauSpre";i:8969;s:10:"IncoRhyUnc";i:8970;s:9:"HeftiUtra";i:8971;s:10:"ForSeasoUn";i:8972;s:15:"KorunPlateSynus";i:8973;s:10:"ForHappePo";i:8974;s:9:"SeSquZoon";i:8975;s:12:"HypocOriPent";i:8976;s:11:"SheTanUltra";i:8977;s:8:"OctaUpca";i:8978;s:13:"MuzhiPleniTip";i:8979;s:10:"MiscPhoeSa";i:8980;s:4:"Quan";i:8981;s:9:"NariTricl";i:8982;s:6:"InsTro";i:8983;s:13:"CantoPontoSub";i:8984;s:12:"MirReciSavan";i:8985;s:9:"LeNoVivif";i:8986;s:4:"Paid";i:8987;s:9:"OversTagu";i:8988;s:7:"MyVaunt";i:8989;s:8:"OversStr";i:8990;s:8:"ThyUndup";i:8991;s:10:"LaOuthoRou";i:8992;s:4:"Lepi";i:8993;s:12:"NoUnkinUnmut";i:8994;s:5:"Sermo";i:8995;s:5:"StiSt";i:8996;s:4:"Neos";i:8997;s:8:"IsothNon";i:8998;s:8:"SluiWind";i:8999;s:7:"FelicPa";i:9000;s:7:"UndisUn";i:9001;s:8:"NeoSupra";i:9002;s:10:"JaLontaTus";i:9003;s:5:"Outpa";i:9004;s:12:"HeteSubSupra";i:9005;s:5:"Nephe";i:9006;s:9:"LeProSqua";i:9007;s:10:"CountLofti";i:9008;s:12:"DiagSpeciTiw";i:9009;s:4:"Wien";i:9010;s:8:"PhoniSup";i:9011;s:10:"HardePolyo";i:9012;s:7:"RaRecha";i:9013;s:11:"CydGalInobs";i:9014;s:3:"Yah";i:9015;s:8:"SiStTorn";i:9016;s:4:"Anab";i:9017;s:7:"HolaWen";i:9018;s:7:"CaGusSt";i:9019;s:8:"BinPhoPl";i:9020;s:12:"TracUnaffUnc";i:9021;s:12:"GuHeremOvige";i:9022;s:12:"StimyUnVirus";i:9023;s:8:"MultUnfa";i:9024;s:8:"ChoComCo";i:9025;s:8:"ErotPres";i:9026;s:6:"HypSul";i:9027;s:8:"PrSwUnen";i:9028;s:9:"PeThamViv";i:9029;s:9:"ThyrUnder";i:9030;s:10:"PorphPrRiv";i:9031;s:10:"SchooSubju";i:9032;s:5:"SnaUn";i:9033;s:10:"LamasOvers";i:9034;s:7:"HyUnwis";i:9035;s:3:"Rho";i:9036;s:12:"FrigPawnSani";i:9037;s:9:"SnUnUntar";i:9038;s:11:"BundEnigWhe";i:9039;s:9:"PrusRebUn";i:9040;s:10:"CuOverPutt";i:9041;s:6:"EphyNo";i:9042;s:4:"Stap";i:9043;s:5:"PasPo";i:9044;s:5:"EnwGa";i:9045;s:5:"ExtZy";i:9046;s:7:"RoofwSm";i:9047;s:6:"InfMal";i:9048;s:9:"BriDeVale";i:9049;s:7:"PhasSna";i:9050;s:7:"CaExSpr";i:9051;s:8:"ObiRiSla";i:9052;s:8:"ThuUnpre";i:9053;s:6:"PleUri";i:9054;s:7:"FoSemei";i:9055;s:5:"ImpPh";i:9056;s:13:"RootShemuSpec";i:9057;s:5:"KoSem";i:9058;s:11:"MetaNaphPer";i:9059;s:10:"PolReTrans";i:9060;s:7:"MitPres";i:9061;s:7:"NonspOn";i:9062;s:12:"PancPoinUnre";i:9063;s:11:"GerLactTrin";i:9064;s:4:"ObQu";i:9065;s:5:"Saliv";i:9066;s:4:"Symb";i:9067;s:7:"LopSlee";i:9068;s:8:"PropTaip";i:9069;s:12:"LacteMediSwa";i:9070;s:13:"PeacPreteStyl";i:9071;s:6:"RaWorr";i:9072;s:10:"InspiSacro";i:9073;s:11:"CouFactoSmo";i:9074;s:7:"CoelFat";i:9075;s:8:"StagnSym";i:9076;s:5:"SchTr";i:9077;s:4:"Myth";i:9078;s:3:"Pap";i:9079;s:11:"ReSubcThumb";i:9080;s:4:"InWa";i:9081;s:4:"Coen";i:9082;s:6:"RecThy";i:9083;s:10:"KumbiPhPos";i:9084;s:10:"StradVedan";i:9085;s:10:"LiturSubTi";i:9086;s:12:"PediSubaSupp";i:9087;s:3:"Eff";i:9088;s:14:"InterNeogPreab";i:9089;s:6:"CoPrUn";i:9090;s:8:"InPacTro";i:9091;s:5:"AlUnd";i:9092;s:6:"IsoPen";i:9093;s:6:"PyWave";i:9094;s:9:"OculPithi";i:9095;s:9:"RecSithUr";i:9096;s:6:"ReTant";i:9097;s:7:"TheraTr";i:9098;s:9:"ReRespeWh";i:9099;s:6:"DvaTri";i:9100;s:7:"MiVolca";i:9101;s:11:"PaniPelvUne";i:9102;s:12:"CoutDonSpoon";i:9103;s:4:"DaIr";i:9104;s:12:"OvervScUnder";i:9105;s:7:"OuPolys";i:9106;s:13:"GratiPhyTitan";i:9107;s:12:"ScStaliTipma";i:9108;s:11:"RoVeheVelif";i:9109;s:12:"NiccoPostWas";i:9110;s:7:"MendiPr";i:9111;s:9:"MareUnpVe";i:9112;s:6:"OliSal";i:9113;s:6:"AmpOct";i:9114;s:6:"HeToad";i:9115;s:10:"NapOutpWyc";i:9116;s:10:"CaMerchSup";i:9117;s:5:"Telod";i:9118;s:3:"Cyc";i:9119;s:8:"PraepVol";i:9120;s:4:"Syre";i:9121;s:3:"Ble";i:9122;s:6:"FlSubj";i:9123;s:13:"DelMaledUpait";i:9124;s:7:"PopinUn";i:9125;s:9:"DemiEntSe";i:9126;s:12:"FiltRamaRheo";i:9127;s:9:"RacUntWit";i:9128;s:2:"Pf";i:9129;s:5:"FloPi";i:9130;s:14:"ProinSalaWinge";i:9131;s:11:"GodSmashUnl";i:9132;s:14:"MartMicroNeuro";i:9133;s:6:"PaikUn";i:9134;s:4:"ErUs";i:9135;s:6:"UnUnch";i:9136;s:7:"ConUred";i:9137;s:7:"TaysUng";i:9138;s:7:"LacMyxa";i:9139;s:10:"GouSerXylo";i:9140;s:11:"DeIpomeUnre";i:9141;s:11:"HomoJuTatsa";i:9142;s:8:"TetraUnd";i:9143;s:8:"BragPici";i:9144;s:13:"CompDissQuart";i:9145;s:10:"FuNonvStra";i:9146;s:5:"RaRid";i:9147;s:9:"RisTheUnd";i:9148;s:11:"DendrHySand";i:9149;s:8:"ChrJuWor";i:9150;s:10:"CalDemicSn";i:9151;s:5:"UnaUn";i:9152;s:8:"OppPolTu";i:9153;s:4:"PrTr";i:9154;s:8:"NoncoWor";i:9155;s:7:"BookHyp";i:9156;s:12:"BakesCrEpaul";i:9157;s:5:"Tarum";i:9158;s:5:"Helli";i:9159;s:7:"ImpSpon";i:9160;s:5:"Unpri";i:9161;s:13:"OversPasVerst";i:9162;s:7:"CaPaUnr";i:9163;s:10:"LuMonNeckm";i:9164;s:9:"InSerdaSt";i:9165;s:11:"SemicSucVej";i:9166;s:5:"Portu";i:9167;s:5:"Whiti";i:9168;s:8:"PyopSann";i:9169;s:4:"GyMa";i:9170;s:13:"InduInterMega";i:9171;s:5:"Mykis";i:9172;s:4:"Phot";i:9173;s:12:"InamOssuaSca";i:9174;s:12:"SuTweenZoeal";i:9175;s:5:"SqWor";i:9176;s:8:"RepTarge";i:9177;s:7:"OpUntri";i:9178;s:8:"QueeStSw";i:9179;s:8:"DiopThul";i:9180;s:7:"MyitStr";i:9181;s:8:"SemiTrap";i:9182;s:9:"SluStaSty";i:9183;s:5:"Neorn";i:9184;s:5:"Socia";i:9185;s:6:"LameUn";i:9186;s:5:"Weigh";i:9187;s:5:"ByMor";i:9188;s:9:"MinceSaun";i:9189;s:11:"OvUnceUnper";i:9190;s:11:"ForecHaemPh";i:9191;s:10:"IndusLoSpi";i:9192;s:5:"PanSc";i:9193;s:5:"Vomit";i:9194;s:9:"MazucPoly";i:9195;s:8:"JeProVil";i:9196;s:5:"Swith";i:9197;s:4:"Wave";i:9198;s:9:"OrgaTramf";i:9199;s:12:"InsePrelSuwa";i:9200;s:8:"LoggiOve";i:9201;s:13:"PetPleurSolpu";i:9202;s:9:"SclerTrif";i:9203;s:4:"Wrea";i:9204;s:4:"Dysc";i:9205;s:13:"LapidNonThack";i:9206;s:10:"CumKinsmSh";i:9207;s:8:"GaIdeKas";i:9208;s:12:"BrokaGuiltSy";i:9209;s:11:"BrothFaLoca";i:9210;s:10:"LabPlUncon";i:9211;s:7:"EquimMa";i:9212;s:11:"DiHomoPinak";i:9213;s:5:"Holop";i:9214;s:7:"OliOxyt";i:9215;s:6:"GoWenr";i:9216;s:11:"BaCompTerna";i:9217;s:6:"LymMyt";i:9218;s:8:"CountMon";i:9219;s:9:"ShramTack";i:9220;s:10:"PreScToher";i:9221;s:4:"Wool";i:9222;s:6:"HaPeSo";i:9223;s:10:"EnHypeMarq";i:9224;s:9:"NonPaResu";i:9225;s:10:"SupThyUntu";i:9226;s:9:"FetteFoPa";i:9227;s:12:"MayanThrUnte";i:9228;s:7:"PeerlUn";i:9229;s:10:"ChrysFruTi";i:9230;s:8:"OverbVej";i:9231;s:6:"CirCri";i:9232;s:10:"PuinaTabet";i:9233;s:5:"Pulkx";i:9234;s:7:"UnpreUn";i:9235;s:4:"JaRa";i:9236;s:4:"Disa";i:9237;s:11:"ItinOculoOp";i:9238;s:8:"SophTeet";i:9239;s:4:"Text";i:9240;s:12:"PrehPunnRivu";i:9241;s:11:"ImpSupSyner";i:9242;s:10:"DiscoKensp";i:9243;s:4:"Nond";i:9244;s:6:"MariSe";i:9245;s:7:"BalafHa";i:9246;s:8:"PoPreTea";i:9247;s:8:"LuxNapSc";i:9248;s:5:"Bookk";i:9249;s:5:"AspVa";i:9250;s:10:"MonoNoSkew";i:9251;s:11:"BuDirgUnenv";i:9252;s:12:"LumbTrochVir";i:9253;s:5:"Escha";i:9254;s:7:"NarPeti";i:9255;s:9:"ThorUnmas";i:9256;s:13:"CollFarleUnqu";i:9257;s:8:"FerroPol";i:9258;s:6:"ReimSa";i:9259;s:5:"Robus";i:9260;s:10:"DorSpSpong";i:9261;s:9:"SubdSuper";i:9262;s:7:"PhSalic";i:9263;s:9:"PaulRecor";i:9264;s:7:"IliacUn";i:9265;s:8:"ExPeSupe";i:9266;s:10:"ForehIntRo";i:9267;s:12:"CoHeterNoset";i:9268;s:9:"ScumlStan";i:9269;s:5:"Bulbo";i:9270;s:9:"PateQuVis";i:9271;s:11:"FaradGhoMes";i:9272;s:12:"MurraNidOste";i:9273;s:10:"NeyanPinke";i:9274;s:10:"BewilDoOve";i:9275;s:5:"Prefe";i:9276;s:10:"GasManaPhr";i:9277;s:7:"DiGaPho";i:9278;s:5:"Saltw";i:9279;s:12:"HypeLaiLeuca";i:9280;s:13:"CenEgomiReocc";i:9281;s:8:"HeOmiRet";i:9282;s:14:"CottoGaumPrima";i:9283;s:4:"Flec";i:9284;s:9:"PowelToUn";i:9285;s:3:"Net";i:9286;s:4:"MoPi";i:9287;s:7:"DidHowa";i:9288;s:4:"View";i:9289;s:7:"HadLuUn";i:9290;s:6:"ConvIr";i:9291;s:4:"Xyri";i:9292;s:5:"MalPe";i:9293;s:11:"ScSperTetra";i:9294;s:7:"PreSigh";i:9295;s:4:"Suan";i:9296;s:6:"EquSta";i:9297;s:11:"CoptMitSubc";i:9298;s:10:"EuhModifRo";i:9299;s:9:"RepinStab";i:9300;s:9:"SpleUnipo";i:9301;s:5:"PhoPh";i:9302;s:8:"SlTriWha";i:9303;s:4:"HoSp";i:9304;s:5:"Bystr";i:9305;s:14:"NonsuOxycScorb";i:9306;s:11:"LympSkaffWa";i:9307;s:12:"MonosMudProt";i:9308;s:10:"KinMenViro";i:9309;s:3:"Enz";i:9310;s:8:"SupUnfis";i:9311;s:10:"MorpUnUret";i:9312;s:12:"OveRevisUnre";i:9313;s:9:"NoPauldRe";i:9314;s:5:"ObOld";i:9315;s:12:"FlyflPrUphol";i:9316;s:9:"PetrTouri";i:9317;s:5:"Vesti";i:9318;s:4:"Xiph";i:9319;s:11:"FlouSuzThia";i:9320;s:5:"Tzapo";i:9321;s:9:"PercyWate";i:9322;s:7:"PiSeSpa";i:9323;s:9:"BlizzSupe";i:9324;s:10:"MyopProPse";i:9325;s:9:"TotUnUnre";i:9326;s:8:"LepSubTh";i:9327;s:5:"NonRe";i:9328;s:9:"SatScSpon";i:9329;s:5:"FlVir";i:9330;s:6:"GoIgPu";i:9331;s:10:"PuiSuUnive";i:9332;s:5:"DyMye";i:9333;s:9:"CarHuKhok";i:9334;s:7:"OxoniWe";i:9335;s:7:"PaPerXa";i:9336;s:12:"AteloChPheno";i:9337;s:4:"Jagr";i:9338;s:12:"NewTheaUndro";i:9339;s:7:"DisInfe";i:9340;s:6:"HemMas";i:9341;s:6:"KhOcel";i:9342;s:4:"TaTw";i:9343;s:12:"RedawStUngro";i:9344;s:14:"BuffwImmeOctog";i:9345;s:12:"PardoPhReste";i:9346;s:9:"OrnitSynd";i:9347;s:5:"Suspi";i:9348;s:9:"PancaProv";i:9349;s:6:"PhylVi";i:9350;s:4:"HoIn";i:9351;s:8:"CheNonbe";i:9352;s:8:"MicPraUn";i:9353;s:6:"SuUnes";i:9354;s:5:"StiTy";i:9355;s:7:"SlUnska";i:9356;s:4:"VeWh";i:9357;s:6:"LupiPr";i:9358;s:10:"KaRufVoidl";i:9359;s:7:"RaRhina";i:9360;s:8:"FungSubm";i:9361;s:10:"PatmiPerdu";i:9362;s:3:"Tog";i:9363;s:12:"CanCoalSlips";i:9364;s:10:"DrDubioTri";i:9365;s:3:"Spu";i:9366;s:5:"There";i:9367;s:10:"HomeLecWoo";i:9368;s:5:"BloFo";i:9369;s:7:"ParsPho";i:9370;s:13:"BlizCeroQuitr";i:9371;s:4:"Surr";i:9372;s:10:"QuakReorSu";i:9373;s:13:"DressRhySteno";i:9374;s:5:"OveTo";i:9375;s:5:"PnPol";i:9376;s:10:"MicroThecl";i:9377;s:11:"EchelRuScan";i:9378;s:7:"FluorPr";i:9379;s:4:"Some";i:9380;s:4:"Rhyn";i:9381;s:6:"ChEuro";i:9382;s:13:"PrenuRoariTri";i:9383;s:8:"GuiPosse";i:9384;s:10:"EnsInlySer";i:9385;s:9:"EffUnUnsp";i:9386;s:10:"DoHaluTang";i:9387;s:12:"LeNomadRootw";i:9388;s:10:"HorSincStr";i:9389;s:8:"IntKePre";i:9390;s:10:"CastrFubLo";i:9391;s:5:"Theur";i:9392;s:10:"LubOmeReto";i:9393;s:11:"PreSubopUss";i:9394;s:7:"OrOtWai";i:9395;s:7:"GoverIn";i:9396;s:6:"DrHowk";i:9397;s:6:"ExigPo";i:9398;s:5:"Teler";i:9399;s:9:"MarSpUnbr";i:9400;s:5:"Unrec";i:9401;s:6:"MuNeot";i:9402;s:8:"LeSeaSom";i:9403;s:6:"GaJaco";i:9404;s:6:"CoDisp";i:9405;s:6:"OceTub";i:9406;s:7:"StomUnl";i:9407;s:4:"Stea";i:9408;s:8:"PamShore";i:9409;s:8:"PolysSof";i:9410;s:9:"OpporPres";i:9411;s:5:"ConUn";i:9412;s:4:"Imbo";i:9413;s:11:"PoliStTelev";i:9414;s:11:"ChrysHemiRa";i:9415;s:9:"DivPopPse";i:9416;s:6:"PhUnid";i:9417;s:12:"MiniSomThlas";i:9418;s:5:"ShaSo";i:9419;s:8:"SataTryp";i:9420;s:5:"Visce";i:9421;s:4:"Decl";i:9422;s:6:"DiRoga";i:9423;s:5:"Leafl";i:9424;s:8:"BalStagn";i:9425;s:6:"PrefSc";i:9426;s:6:"LithUm";i:9427;s:8:"IntTruss";i:9428;s:7:"ChlamEx";i:9429;s:5:"Shedh";i:9430;s:8:"CoelOvan";i:9431;s:8:"ProUnder";i:9432;s:8:"EmbryUnk";i:9433;s:4:"Pedo";i:9434;s:7:"MurksWi";i:9435;s:9:"PhiliShSl";i:9436;s:5:"SupTa";i:9437;s:10:"DropoHaLou";i:9438;s:8:"BrChylUn";i:9439;s:10:"PrTetUnpre";i:9440;s:8:"PreScowb";i:9441;s:13:"LimiTortZygon";i:9442;s:9:"HansSpiWa";i:9443;s:7:"DiNonUl";i:9444;s:7:"MalUnco";i:9445;s:9:"IncoStave";i:9446;s:4:"Muni";i:9447;s:10:"PsychTurbi";i:9448;s:5:"Nonig";i:9449;s:9:"CorParPos";i:9450;s:7:"GrUrrho";i:9451;s:5:"Priva";i:9452;s:3:"Gue";i:9453;s:5:"TreUn";i:9454;s:8:"GirnyNom";i:9455;s:9:"GeInPavag";i:9456;s:5:"Poeta";i:9457;s:7:"PrerUro";i:9458;s:8:"PalSacch";i:9459;s:9:"CutliSesq";i:9460;s:4:"Sine";i:9461;s:8:"EasPedun";i:9462;s:11:"DotaNonpSic";i:9463;s:13:"CotyStudiTher";i:9464;s:7:"InTripu";i:9465;s:5:"SteUr";i:9466;s:8:"ConvSeab";i:9467;s:3:"Yor";i:9468;s:4:"Prov";i:9469;s:5:"ChiHe";i:9470;s:7:"NePaPre";i:9471;s:5:"Seque";i:9472;s:10:"EmeHaUpswa";i:9473;s:9:"SorceUnde";i:9474;s:11:"StrTacklUnw";i:9475;s:5:"Johan";i:9476;s:7:"PaSpira";i:9477;s:11:"UncaUncoUnd";i:9478;s:5:"CoGor";i:9479;s:8:"BarrMaWi";i:9480;s:4:"TuXy";i:9481;s:6:"MinPro";i:9482;s:6:"MetUnm";i:9483;s:5:"PrWei";i:9484;s:14:"TatoUnbedWovex";i:9485;s:15:"HuronOpeidPetas";i:9486;s:8:"ThesTool";i:9487;s:8:"MalUnbro";i:9488;s:4:"Tars";i:9489;s:7:"DiGrain";i:9490;s:9:"OgOvRiver";i:9491;s:10:"NonclVexer";i:9492;s:4:"Morg";i:9493;s:10:"BushiParTo";i:9494;s:5:"CanWh";i:9495;s:14:"KerriLampMetap";i:9496;s:11:"MayhaUnpWil";i:9497;s:8:"CtenoNin";i:9498;s:10:"CoDisepSta";i:9499;s:5:"Vitri";i:9500;s:5:"Lecit";i:9501;s:12:"HaqPatriSulp";i:9502;s:5:"Loyal";i:9503;s:12:"KeraRuniTene";i:9504;s:3:"Sne";i:9505;s:13:"HemaShadZeall";i:9506;s:5:"Undif";i:9507;s:8:"DisfrSqu";i:9508;s:6:"SeTrip";i:9509;s:14:"EumitNetbrSche";i:9510;s:7:"MoWentl";i:9511;s:11:"OveProfUnme";i:9512;s:9:"DetesMyZi";i:9513;s:4:"Napa";i:9514;s:10:"DeprMetUnt";i:9515;s:12:"InduParUnrec";i:9516;s:10:"PeachUnthe";i:9517;s:5:"PssTu";i:9518;s:5:"DoTom";i:9519;s:5:"Gavia";i:9520;s:8:"DifDumEp";i:9521;s:9:"NonwaShor";i:9522;s:9:"NeStaUnde";i:9523;s:6:"TestUn";i:9524;s:8:"DemZonoc";i:9525;s:6:"InmUnl";i:9526;s:8:"RedUrino";i:9527;s:9:"StenUsurp";i:9528;s:4:"TaZi";i:9529;s:4:"Phys";i:9530;s:12:"HemoIxoSulph";i:9531;s:6:"HoonSh";i:9532;s:8:"RendeVet";i:9533;s:7:"ScrStun";i:9534;s:6:"NonPat";i:9535;s:5:"Rundi";i:9536;s:9:"GyOmTankm";i:9537;s:5:"Ravel";i:9538;s:4:"MeSe";i:9539;s:10:"LiSeymShel";i:9540;s:14:"CeltEnsnaJesti";i:9541;s:3:"Wal";i:9542;s:14:"InwrTressUnsap";i:9543;s:5:"Remen";i:9544;s:8:"DaUndWit";i:9545;s:8:"PhalaRec";i:9546;s:4:"Vicx";i:9547;s:3:"Pel";i:9548;s:4:"MoPo";i:9549;s:5:"Profr";i:9550;s:8:"JustiRed";i:9551;s:5:"IdThr";i:9552;s:5:"ExMet";i:9553;s:5:"Gleno";i:9554;s:13:"IndeParaTerte";i:9555;s:4:"Uniq";i:9556;s:15:"FilipSourhUnhur";i:9557;s:12:"EuhyoRopeUnr";i:9558;s:12:"FervScapuTec";i:9559;s:7:"IrrKisl";i:9560;s:6:"DefNon";i:9561;s:7:"OmniShe";i:9562;s:6:"OidSup";i:9563;s:10:"MetanSpWin";i:9564;s:5:"Torso";i:9565;s:7:"PrRoShi";i:9566;s:9:"OutpRicke";i:9567;s:7:"InWarmu";i:9568;s:9:"EpaPosPro";i:9569;s:11:"CroInviOvof";i:9570;s:5:"DemRa";i:9571;s:5:"Sacka";i:9572;s:4:"SiSy";i:9573;s:4:"Movi";i:9574;s:5:"Neore";i:9575;s:6:"AmWatc";i:9576;s:6:"ScleSm";i:9577;s:11:"BipaFreWalk";i:9578;s:5:"ArtSh";i:9579;s:8:"PaPikRoo";i:9580;s:13:"ImporTodeVari";i:9581;s:5:"Upste";i:9582;s:10:"MesVolWugg";i:9583;s:7:"CharmPa";i:9584;s:12:"PennWashwWur";i:9585;s:7:"UnWeath";i:9586;s:4:"TrUr";i:9587;s:9:"NervResti";i:9588;s:7:"CruSeSp";i:9589;s:7:"NonnaTe";i:9590;s:5:"Uncal";i:9591;s:11:"HyKatciUnin";i:9592;s:5:"Katun";i:9593;s:11:"ThemsUnUnde";i:9594;s:5:"UndVa";i:9595;s:14:"GapyxIliaMesen";i:9596;s:5:"StUnj";i:9597;s:4:"TiUn";i:9598;s:12:"CreatDecUnfa";i:9599;s:8:"DrumxIrr";i:9600;s:12:"HawPennSceno";i:9601;s:7:"InLitPr";i:9602;s:11:"MisbRumpxTh";i:9603;s:12:"ForesIaJamni";i:9604;s:6:"QuaTal";i:9605;s:13:"HenhPerrUnsto";i:9606;s:7:"ShStere";i:9607;s:7:"SterVan";i:9608;s:10:"NeotPlSwar";i:9609;s:5:"UnWac";i:9610;s:10:"BrFrostInd";i:9611;s:8:"BarbNoNu";i:9612;s:9:"FleKnoPho";i:9613;s:8:"MayPaSub";i:9614;s:8:"ScrawUns";i:9615;s:6:"SeThev";i:9616;s:11:"FiresFoWood";i:9617;s:10:"OvigePaSub";i:9618;s:13:"HolidIntelLuc";i:9619;s:13:"MonotOximaSen";i:9620;s:14:"GangSawhoWoman";i:9621;s:10:"HoImpTepeh";i:9622;s:5:"ImpMe";i:9623;s:8:"LuvaSpee";i:9624;s:9:"DeFolGoye";i:9625;s:12:"EboniMoXiphi";i:9626;s:5:"Milts";i:9627;s:5:"QuSte";i:9628;s:10:"QuisShepSu";i:9629;s:7:"ScatoSp";i:9630;s:9:"RadiSigge";i:9631;s:9:"LollRampa";i:9632;s:10:"JuluRehTri";i:9633;s:8:"CeliObPa";i:9634;s:3:"Scl";i:9635;s:6:"PhoPro";i:9636;s:7:"IrUnWhi";i:9637;s:4:"Whee";i:9638;s:12:"HepOutgrPost";i:9639;s:5:"TacWa";i:9640;s:9:"DulaPleas";i:9641;s:10:"PrPunkeTet";i:9642;s:5:"Presu";i:9643;s:7:"ReSepte";i:9644;s:11:"MicMorphRet";i:9645;s:6:"IcTurn";i:9646;s:7:"CoSlumb";i:9647;s:3:"Kho";i:9648;s:9:"MajPosSti";i:9649;s:6:"OveTyr";i:9650;s:9:"JuraUnali";i:9651;s:10:"HeterUpbur";i:9652;s:4:"Invo";i:9653;s:7:"ChaRuma";i:9654;s:5:"Otoco";i:9655;s:10:"FoHouUnrab";i:9656;s:7:"FumaRom";i:9657;s:10:"BriOnaxUnj";i:9658;s:7:"QuSecUn";i:9659;s:11:"IodaNaumUnc";i:9660;s:9:"PoRiTonsu";i:9661;s:9:"BridHespe";i:9662;s:7:"HeliSup";i:9663;s:11:"LefPoroReso";i:9664;s:5:"NinUn";i:9665;s:14:"LitasPrebPresb";i:9666;s:6:"SaUnUn";i:9667;s:10:"GrHidPsych";i:9668;s:9:"PreceRaRo";i:9669;s:13:"OverPittSadrx";i:9670;s:5:"Ignif";i:9671;s:8:"LighUnsa";i:9672;s:9:"OvUnYeast";i:9673;s:7:"DumFiMi";i:9674;s:4:"Scou";i:9675;s:7:"SexteWr";i:9676;s:5:"HieNo";i:9677;s:6:"CaMala";i:9678;s:4:"StUn";i:9679;s:6:"GrotUn";i:9680;s:5:"InSpi";i:9681;s:10:"PolycShama";i:9682;s:12:"ConEncepRest";i:9683;s:5:"PlaSm";i:9684;s:5:"Blatt";i:9685;s:14:"BilipCensuCont";i:9686;s:7:"DiscOve";i:9687;s:8:"InsLeaUn";i:9688;s:2:"Id";i:9689;s:6:"ShSqui";i:9690;s:10:"StatuTaxed";i:9691;s:7:"StUnale";i:9692;s:7:"PyVampi";i:9693;s:12:"PsyTranqUnco";i:9694;s:4:"Mise";i:9695;s:4:"Wayw";i:9696;s:7:"FleLoSp";i:9697;s:10:"InLixOvert";i:9698;s:9:"IsfahOrPu";i:9699;s:11:"BeniCacoUlt";i:9700;s:8:"FleTarra";i:9701;s:4:"DiOp";i:9702;s:4:"HyPo";i:9703;s:13:"NaumaOveRolle";i:9704;s:10:"TeartUncon";i:9705;s:5:"CirDa";i:9706;s:13:"ImploNondTurb";i:9707;s:10:"DyaSexTric";i:9708;s:10:"GaImpWeapo";i:9709;s:4:"Myas";i:9710;s:11:"OpSpadVolse";i:9711;s:5:"Olent";i:9712;s:11:"BiopLaevoSn";i:9713;s:8:"TagalWai";i:9714;s:8:"HeOxyVal";i:9715;s:4:"Infr";i:9716;s:9:"MicrUmbel";i:9717;s:9:"FaNeedlPe";i:9718;s:11:"LysisPseuUn";i:9719;s:10:"CadChroSib";i:9720;s:12:"GooSyncaTuft";i:9721;s:10:"QuaShodeUn";i:9722;s:5:"OveSt";i:9723;s:4:"Perl";i:9724;s:4:"HuPo";i:9725;s:13:"IntoParaSedim";i:9726;s:5:"Carce";i:9727;s:9:"KlMegaPyr";i:9728;s:12:"OrthSeeaWind";i:9729;s:9:"InsulRhin";i:9730;s:6:"MicRis";i:9731;s:15:"IsaacMediaUnmed";i:9732;s:8:"DeStSwim";i:9733;s:14:"KadayPhospPrea";i:9734;s:10:"MesaPrProb";i:9735;s:11:"PediPrPromo";i:9736;s:4:"Dawt";i:9737;s:11:"NariOrThean";i:9738;s:4:"Word";i:9739;s:10:"ExcitPassu";i:9740;s:7:"HoPoPre";i:9741;s:5:"Sampl";i:9742;s:9:"LePhilaUn";i:9743;s:8:"MalebOve";i:9744;s:9:"BoyaTrigi";i:9745;s:8:"DaDiGuai";i:9746;s:9:"DeerhLiQu";i:9747;s:9:"EpicSquee";i:9748;s:6:"PiSupp";i:9749;s:6:"SenSub";i:9750;s:9:"RemuSubba";i:9751;s:12:"HighlSophiSu";i:9752;s:9:"OptimSpor";i:9753;s:4:"Sech";i:9754;s:9:"AmCartCys";i:9755;s:9:"MushmOsPa";i:9756;s:5:"Decon";i:9757;s:4:"Sexh";i:9758;s:10:"ResisSaThu";i:9759;s:3:"Zec";i:9760;s:4:"Vill";i:9761;s:3:"Pod";i:9762;s:9:"OweSisWai";i:9763;s:11:"EmWassaWeig";i:9764;s:9:"HaPostTub";i:9765;s:4:"Stel";i:9766;s:8:"PePrScru";i:9767;s:6:"GastPr";i:9768;s:10:"TeretTrich";i:9769;s:7:"ResiSpu";i:9770;s:3:"Gre";i:9771;s:14:"CunjOversPneum";i:9772;s:8:"OdOzoUmi";i:9773;s:10:"ConfuCover";i:9774;s:9:"PaSubuUnb";i:9775;s:3:"Och";i:9776;s:6:"OvPorc";i:9777;s:11:"ReweSubvWin";i:9778;s:7:"FouUnfo";i:9779;s:12:"FluMicrRecre";i:9780;s:14:"RecruUnivUnspr";i:9781;s:10:"UnsavUnsea";i:9782;s:9:"HormoPiSe";i:9783;s:8:"SpheTwis";i:9784;s:5:"Thema";i:9785;s:13:"DidelTailUnwh";i:9786;s:5:"Marsi";i:9787;s:14:"InfrPleurPurre";i:9788;s:12:"MekomPreSync";i:9789;s:8:"EartNota";i:9790;s:6:"SoddWa";i:9791;s:3:"Obv";i:9792;s:14:"MarmNaphtPunis";i:9793;s:9:"PhillTaut";i:9794;s:12:"InomaNaPremi";i:9795;s:6:"PagaSa";i:9796;s:9:"LoMidleRe";i:9797;s:14:"MonotPsalTetar";i:9798;s:3:"Mum";i:9799;s:13:"PopuTheorWone";i:9800;s:5:"Worth";i:9801;s:6:"SkWhiz";i:9802;s:8:"DemEroHo";i:9803;s:5:"Soili";i:9804;s:7:"StoutTa";i:9805;s:11:"InteManShaf";i:9806;s:11:"DeyxSpTouri";i:9807;s:7:"GlagLac";i:9808;s:8:"RedaSauc";i:9809;s:11:"PseuTintiTo";i:9810;s:7:"NoachOa";i:9811;s:11:"HyPacinSnor";i:9812;s:4:"Ghee";i:9813;s:5:"Shive";i:9814;s:3:"Urv";i:9815;s:15:"SupraUnaptUnhis";i:9816;s:11:"BullwCrUnge";i:9817;s:11:"GuitaPredVa";i:9818;s:5:"Tarqu";i:9819;s:10:"InsecQuaTh";i:9820;s:11:"EnOverTrans";i:9821;s:11:"DelPeloPeti";i:9822;s:7:"DiFicLo";i:9823;s:4:"FoMa";i:9824;s:11:"InexObjPara";i:9825;s:5:"Stere";i:9826;s:7:"LaiSail";i:9827;s:10:"PrRiddScir";i:9828;s:7:"UnVinat";i:9829;s:7:"OvPileo";i:9830;s:7:"BliCuTr";i:9831;s:12:"ArchsHormoTo";i:9832;s:7:"ForMaTh";i:9833;s:5:"ConDi";i:9834;s:7:"HabblSy";i:9835;s:11:"BioSetulTra";i:9836;s:5:"Twigl";i:9837;s:12:"DarnHesteMed";i:9838;s:9:"ElFrShagb";i:9839;s:9:"DeSticUnc";i:9840;s:5:"Repug";i:9841;s:12:"PamSacroValo";i:9842;s:10:"ThrasUnmod";i:9843;s:6:"DidEco";i:9844;s:11:"AntiDolefRh";i:9845;s:8:"EucomSer";i:9846;s:6:"OxyhPo";i:9847;s:10:"MononPulmo";i:9848;s:4:"Urba";i:9849;s:11:"HolPhoUnshi";i:9850;s:11:"TiamUnguaUn";i:9851;s:6:"EnRoUn";i:9852;s:5:"Semia";i:9853;s:9:"DisbPyrTe";i:9854;s:4:"Geob";i:9855;s:8:"BeInSupe";i:9856;s:8:"DepreNon";i:9857;s:7:"PoWhoms";i:9858;s:4:"Pori";i:9859;s:12:"MisprPhUnarg";i:9860;s:3:"Oxy";i:9861;s:12:"CephaMaPanti";i:9862;s:9:"HyotPhala";i:9863;s:12:"InexUntVoidl";i:9864;s:13:"HendePolysVan";i:9865;s:11:"FusilInTast";i:9866;s:6:"UngUnv";i:9867;s:13:"CaleInterMors";i:9868;s:5:"HeTri";i:9869;s:8:"ElHaPrer";i:9870;s:9:"EcoUnUnwa";i:9871;s:10:"RefinRhTub";i:9872;s:6:"MallPl";i:9873;s:5:"Overm";i:9874;s:14:"EntraProfSynge";i:9875;s:8:"MonUnemb";i:9876;s:4:"Osci";i:9877;s:8:"SwoThink";i:9878;s:14:"OsiaOveriWimpl";i:9879;s:7:"BuHinny";i:9880;s:7:"SulUpgr";i:9881;s:5:"PlSma";i:9882;s:8:"SacchSir";i:9883;s:9:"GristRift";i:9884;s:8:"UncUppar";i:9885;s:10:"SnSubUntho";i:9886;s:4:"Loaf";i:9887;s:13:"PyrocSteekTax";i:9888;s:3:"Zac";i:9889;s:10:"OyeUnVioli";i:9890;s:12:"GimMyeliPyro";i:9891;s:10:"RhodSeliTe";i:9892;s:12:"FisUnadUnsub";i:9893;s:8:"WudgXant";i:9894;s:10:"NoOinPyrop";i:9895;s:6:"ExItal";i:9896;s:5:"Totty";i:9897;s:5:"Octam";i:9898;s:11:"OverReticUl";i:9899;s:9:"ElabrFenc";i:9900;s:13:"EnantUnnUnoxi";i:9901;s:5:"PeSyr";i:9902;s:9:"EtymPhoto";i:9903;s:5:"LilMi";i:9904;s:12:"GlypMedUnfet";i:9905;s:8:"LiaThrWi";i:9906;s:10:"OrShoaThur";i:9907;s:8:"UnthUpru";i:9908;s:7:"LepNihi";i:9909;s:12:"FiliMesoUpst";i:9910;s:9:"PetaTenni";i:9911;s:5:"PedRe";i:9912;s:12:"ColoProfTher";i:9913;s:10:"KatabOvSyr";i:9914;s:5:"Cequi";i:9915;s:7:"DisDiEn";i:9916;s:9:"DorMyzoWi";i:9917;s:8:"NiphaPol";i:9918;s:4:"BiCo";i:9919;s:12:"CompPolypUls";i:9920;s:8:"HeaveStr";i:9921;s:5:"MaMos";i:9922;s:13:"EudoMacarSore";i:9923;s:4:"OlVa";i:9924;s:4:"Cosw";i:9925;s:11:"MalOverdSub";i:9926;s:10:"DiKilUnder";i:9927;s:4:"MaPe";i:9928;s:10:"MetrPeProm";i:9929;s:6:"TekUnd";i:9930;s:7:"PlotiSu";i:9931;s:11:"PaPetalTrac";i:9932;s:11:"MoneyUndUnv";i:9933;s:7:"InsurYo";i:9934;s:11:"FoosMicTact";i:9935;s:7:"OlOther";i:9936;s:7:"MonMyOn";i:9937;s:11:"EmexPySymbo";i:9938;s:11:"HypaxSpiTar";i:9939;s:8:"AntUmbVi";i:9940;s:7:"OcclSqu";i:9941;s:8:"ExaPlush";i:9942;s:12:"PreSuccuVerv";i:9943;s:3:"Tre";i:9944;s:5:"MerTo";i:9945;s:11:"GlossSeptSo";i:9946;s:4:"Magn";i:9947;s:13:"QuintSourwThu";i:9948;s:5:"Kinet";i:9949;s:4:"NoUn";i:9950;s:4:"MaUn";i:9951;s:12:"MatriSpirSwe";i:9952;s:8:"OnlooUnl";i:9953;s:13:"MorwoSubarUne";i:9954;s:4:"Impo";i:9955;s:4:"Phac";i:9956;s:8:"DeprePri";i:9957;s:10:"CatLakarPr";i:9958;s:14:"GuttLophoMildr";i:9959;s:12:"OnychSmeUnwe";i:9960;s:5:"GroWo";i:9961;s:8:"LiRadTru";i:9962;s:13:"IncomTitVidui";i:9963;s:12:"ImpMerePrein";i:9964;s:7:"MaProto";i:9965;s:12:"RamusShrTibi";i:9966;s:11:"BurwFalciSn";i:9967;s:5:"RodTh";i:9968;s:8:"DoxaOlch";i:9969;s:4:"Tere";i:9970;s:4:"DiPo";i:9971;s:8:"DenucLit";i:9972;s:11:"CountInKusk";i:9973;s:5:"HeKok";i:9974;s:5:"PrSta";i:9975;s:7:"PhrenPr";i:9976;s:8:"MyoneNet";i:9977;s:14:"GeasMisfeRepre";i:9978;s:13:"OphidProveSub";i:9979;s:11:"PolProrSien";i:9980;s:5:"Grudg";i:9981;s:10:"LiReglUnde";i:9982;s:7:"DiMyoSu";i:9983;s:7:"SymTyVu";i:9984;s:11:"FiguHebSpon";i:9985;s:8:"CeDoTele";i:9986;s:10:"OkiTipTyig";i:9987;s:9:"PySphTetr";i:9988;s:3:"Pep";i:9989;s:11:"PiuUncUnson";i:9990;s:9:"OchiPrWor";i:9991;s:8:"RequiRyn";i:9992;s:13:"JimbeReceRegi";i:9993;s:4:"HeMe";i:9994;s:5:"Toffy";i:9995;s:7:"EriodSu";i:9996;s:9:"UnexViola";i:9997;s:11:"OrgaUnUnhos";i:9998;s:3:"Dav";i:9999;s:6:"HydRev";} \ No newline at end of file diff --git a/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/devtools/junk/testLazyJsonMapper.php b/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/devtools/junk/testLazyJsonMapper.php new file mode 100755 index 0000000..f47480d --- /dev/null +++ b/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/devtools/junk/testLazyJsonMapper.php @@ -0,0 +1,1897 @@ += 0; + +$json = << 'int', + 'just_a_string' => 'float[]', + 'test_pure_lazymapper_object' => '\LazyJsonMapper\LazyJsonMapper', + // test the shortcut to avoid having to write the whole path: + 'test_pure_lazymapper_object_shortcut' => 'LazyJsonMapper', + 'test_pure_lazymapper_object_shortarr' => 'LazyJsonMapper[][]', + // test without strict case-sensitive checking: (MUST fail) + // 'test_pure_lazymapper_object_shortcut' => 'lazyJsonMapper', + ]; +} +// var_dump(new TestDeep()); // look at the LazyJsonMapper shortcut success +class TestMid extends TestDeep +{ +} + +class Test extends TestMid +{ + const JSON_PROPERTY_MAP = [ + 'just_a_string' => 'string', + 'camelCaseProp' => 'int', + // full namespace path, with case-sensitivity typos on purpose (php + // allows it, but LazyJsonMapper compiles this to the proper name + // instead so that we have strict names internally): + 'self_object' => '\foo\Test', + 'string_array' => 'string[]', + // relative notation instead of full "\namespace\path": + // when relative mode is used, it looks in the defining class' own namespace. + 'self_array' => 'Test[]', + ]; +} + +$jsonData = json_decode($json, true, 512, JSON_BIGINT_AS_STRING); + +var_dump($jsonData); + +$x = new Test($jsonData, true); + +// begin with basic tests... +var_dump($x); +$sub = $x->getSelfObject(); +$sub->setJustAString('modifying nested object and propagating the change to root object $x'); +var_dump($sub); +var_dump($x->getSelfObject()); +var_dump($x); +$multi = $x->getSelfArray(); // resolves all objects in array, but avoids doing + // it recursively. sub-properties are lazy-converted + // when they are actually requested. +var_dump($multi); // array of objects, with no resolved sub-objects yet +var_dump($x); // now has array of objects +$deepsub = $multi[1]->getSelfObject(); // causes nested sub to be resolved +var_dump($multi); +var_dump($x); +$deepsub->setJustAString('wow, propagating change of very deep object!'); +var_dump($multi); +var_dump($x); +var_dump($x->getCamelCaseProp()); +var_dump($x->getJustAString()); +var_dump($x->isJustAString()); +var_dump($x->getJustAString()); +var_dump($x); +var_dump($x->getSelfObject()); +var_dump($x->getSelfObject()->getJustAString()); +var_dump($x->self_object->just_a_string); +var_dump($x->getStringArray()); +var_dump($x->getSelfArray()); + +try { + echo $x->a_missing_property_not_in_data_or_def; +} catch (LazyJsonMapperException $e) { + printf("Test missing property via property access Exception: %s\n", $e->getMessage()); +} + +try { + $x->getAMissingPropertyNotInDataOrDef(); +} catch (LazyJsonMapperException $e) { + printf("Test missing property via magic getter Exception: %s\n", $e->getMessage()); +} + +$x = new Test($jsonData, true); +var_dump($x); // no data is resolved yet +// test deeply nested chain of getters and setters. +$x->getSelfArray()[1]->getSelfObject()->setJustAString('chained command for deep modification')->setCamelCaseProp(9944); +var_dump($x); // chain has been resolved and change has propagated +var_dump($x->getSelfArray()[1]->getSelfObject()->getCamelCaseProp()); // int(9944) + +class SubClassOfTest extends Test +{ +} +$foo = new SubClassOfTest(); // Test acceptance of subclasses of required class. +$x->setSelfObject($foo); +var_dump($x->getSelfObject()); +var_dump($x->getSelfObject()->getJustAString()); + +try { + $x->setSelfObject('x'); // trying to set non-object value for object property +} catch (LazyJsonMapperException $e) { + printf("Test non-object assignment Exception: %s\n", $e->getMessage()); +} + +class Bleh +{ +} + +try { + $x->setSelfObject(new Bleh()); // trying wrong class for property +} catch (LazyJsonMapperException $e) { + printf("Test wrong object assignment Exception: %s\n", $e->getMessage()); +} + +$foo = new Test(['just_a_string' => 'example']); +$x->setSelfObject($foo); +var_dump($x->getSelfObject()); +var_dump($x->getSelfObject()->getJustAString()); +$x->printJson(); +var_dump($x->just_a_string); +var_dump(isset($x->just_a_string)); +unset($x->just_a_string); +var_dump($x->just_a_string); +var_dump(isset($x->just_a_string)); +unset($x->self_array); +unset($x->camelCaseProp); +$x->printJson(); + +var_dump('---------------------'); + +// test creation of objects from empty object-arrays "{}" in JSON +class EmptyObjTest extends LazyJsonMapper +{ + const JSON_PROPERTY_MAP = [ + 'self' => '\foo\EmptyObjTest', + ]; +} +$x = new EmptyObjTest(json_decode('{"self":null}', true)); // allow null object +var_dump($x->getSelf()); +// NOTE: the empty-array test is because empty arrays are indistinguishable from +// objects when decoded from JSON. but if it had been an actual JSON array +// (which is always non-associative), then we detect that it's non-object data. +$x = new EmptyObjTest(json_decode('{"self":{}}', true)); // allow empty object +var_dump($x->getSelf()); +$x = new EmptyObjTest(json_decode('{"self":[]}', true)); // allow empty array +var_dump($x->getSelf()); +$x = new EmptyObjTest(json_decode('{"self":[1,2]}', true)); // forbid non-object +try { + var_dump($x->getSelf()); +} catch (\Exception $e) { + printf("Test converting invalid regular JSON array to object Exception: %s\n", $e->getMessage()); +} + +var_dump('---------------------'); + +class TestUndefinedProps extends LazyJsonMapper +{ + const JSON_PROPERTY_MAP = [ + 'self' => '\foo\TestUndefinedProps', + 'selfArray' => '\foo\TestUndefinedProps[]', + 'foo_bar' => 'int[][]', + 'property' => 'string', + ]; +} + +$json = <<getMessage()); +} + +// now create the class without analysis enabled... which enables regular +// operation where the user can access the undefined properties too. +$y = new TestUndefinedProps($data, false); +var_dump($y); // look at the internal data and the compiled class map + +// now verify what the exported property map says. +// the only defined properties are foo_bar, self, selfArray and property. +// the undefined ones are missing_property and another_missing. +$allowRelativeTypes = true; +$includeUndefined = true; +$descriptions = $y->exportPropertyDescriptions($allowRelativeTypes, $includeUndefined); +var_dump($descriptions); +foreach ($descriptions as $property) { + printf("* Property: '%s'. Defined: %s\n", $property->name, + $property->is_defined ? 'Yes!' : 'No.'); +} + +// Now just test the automatic printing function too... +$showFunctions = true; +$y->printPropertyDescriptions($showFunctions, $allowRelativeTypes, $includeUndefined); +$showFunctions = true; +$allowRelativeTypes = false; +$includeUndefined = false; +$y->printPropertyDescriptions($showFunctions, $allowRelativeTypes, $includeUndefined); +$showFunctions = false; +$y->printPropertyDescriptions($showFunctions, $allowRelativeTypes, $includeUndefined); + +// And test it on the main class too: +$y = new Test(); +$y->printPropertyDescriptions(); +$y->printPropertyDescriptions(false, true); // without functions, with relative + +var_dump('---------------------'); + +// Test the hasX() functions, which are useful when verifying that non-defined +// (not in class definition) fields exist in data before trying to read, to +// avoid causing any exceptions in the getter. +$x = new Test($data); +var_dump($x->hasReallyMissing()); // false, since it's not in class def or data. +var_dump($x->hasAnotherMissing()); // true, since it's in data (but not in class def) +var_dump($x->hasJustAString()); // true, since it's in class def (but not in data) +var_dump($x->getJustAString()); // null, since it's not in data (but is in class def) +try { + $x->getReallyMissing(); // exception, since it's not in class def or data. + // var_dump($x->really_missing); // also exception, "no such object property". +} catch (LazyJsonMapperException $e) { + printf("Test getReallyMissing() Exception: %s\n", $e->getMessage()); +} + +try { + $x->setReallyMissing('a'); // exception, since it's not in class def or data. + // $x->really_missing = 'a'; // also exception, "no such object property". +} catch (LazyJsonMapperException $e) { + printf("Test setReallyMissing() Exception: %s\n", $e->getMessage()); +} +// intended usage by end-users when accessing undefined values: +if ($x->hasReallyMissing()) { + // this won't run, since ReallyMissing didn't exist. but if it HAD existed + // in the JSON data, this function call would now be safe without exceptions: + var_dump($x->getReallyMissing()); +} else { + var_dump('not running getReallyMissing() since the property is missing'); +} + +var_dump('---------------------'); + +class TestNotSubClass extends LazyJsonMapper +{ + const JSON_PROPERTY_MAP = [ + 'not_subclass' => '\foo\NotSubClass', + ]; +} +class NotSubClass +{ +} // Not instance of LazyJsonMapper + +$json = <<getNotSubclass(); +} catch (LazyJsonMapperException $e) { + printf("TestNotSubClass Exception: %s\n", $e->getMessage()); +} + +var_dump('---------------------'); + +class TestMissingClass extends LazyJsonMapper +{ + const JSON_PROPERTY_MAP = [ + 'a_missing_class' => '\foo\Missing', + ]; +} + +$json = <<getMessage()); +} + +var_dump('---------------------'); + +class TestMissingPropAndMissingClass extends LazyJsonMapper +{ + const JSON_PROPERTY_MAP = [ + 'a_missing_class' => '\foo\Missing', + ]; +} + +$json = <<getMessage()); +} + +var_dump('---------------------'); + +// this test checks two things: +// definitions that do not match the data. +// properties whose classes cannot be constructed (due to custom _init() fail). +class TestUnmappableAndFailConstructor extends LazyJsonMapper +{ + const JSON_PROPERTY_MAP = [ + 'bad_definition' => 'int[][][]', // too deep arrays for the data + // this one will not be able to construct during the getting of this property... + 'impossible_constructor' => '\foo\WithBadConstructor', + ]; +} + +class WithBadConstructor extends LazyJsonMapper +{ + protected function _init() + { + // Uncomment this other exception to test the "Invalid exception thrown + // by _init(). Must use LazyUserException." error when users throw the + // wrong exception: + // throw new \Exception('test'); + + throw new \LazyJsonMapper\Exception\LazyUserException('Hello world! Thrown by a failing constructor.'); + } +} + +$json = <<getMessage()); +} + +var_dump('---------------------'); + +class TestImpossibleSubPropertyCompile extends LazyJsonMapper +{ + const JSON_PROPERTY_MAP = [ + // this one links to a class that exists but isn't compiled yet and + // therefore must be sub-compiled. but that particular one actually + // failed its own compilation earlier (above) - because the + // "TestMissingClass" class CANNOT be compiled... so OUR map compilation + // HERE will succeed (it points at TestMissingClass which is a + // LazyJsonMapper class which exists), but then WE will fail with a + // sub-property class error when we try to ALSO compile OUR property's + // uncompiled class ("TestMissingClass") at the end of its own + // compilation process. + 'impossible_subcompilation' => '\foo\TestMissingClass', + ]; +} + +try { + $x = new TestImpossibleSubPropertyCompile(); +} catch (\Exception $e) { + printf("Test impossible sub-property class compilation: %s\n", $e->getMessage()); +} + +var_dump('---------------------'); + +// this test is very similar to the previous test, but it ensures that the +// validation works for deep hierarchies too, of classes with properties that +// refer to classes with properties that refer to classes that refer to +// uncompilable classes. +class TopFailChainClass extends LazyJsonMapper +{ + // successfully compiles since MiddleFailChainClass exists and is based on LazyJsonMapper + const JSON_PROPERTY_MAP = [ + 'middle_fail_chain_class' => '\Foo\MiddleFailChainClass', + ]; +} + +class MiddleFailChainClass extends LazyJsonMapper +{ + // successfully compiles since DeepFailChainBadClass exists and is based on LazyJsonMapper + const JSON_PROPERTY_MAP = [ + 'deep_fail_chain_class' => '\Foo\DeepFailChainBadClass', + ]; +} + +class DeepFailChainBadClass extends LazyJsonMapper +{ + // this map will fail to compile, which should stop the compilation of + // whichever class began the compilation process that pointed at us... + const JSON_PROPERTY_MAP = [ + 'not_a_valid_class' => '/What/ever/...', + ]; +} + +// try starting the compilation with each of the 3 classes: +// it doesn't matter which one we start with, since the compiler cache will +// notice the failures in them all and will auto-rollback their compilations. +// which means that the other classes won't incorrectly see anything in the +// cache. so each attempt to create any of these classes will be as if it was +// the first-ever call for compiling the classes in its hierarchy. + +try { + // fails immediately since this class map cannot be compiled + $x = new DeepFailChainBadClass(); +} catch (\Exception $e) { + printf("Test compiling DeepFailChainBadClass Exception: %s\n", $e->getMessage()); +} + +try { + // succeeds at compiling its own map, but then fails when trying to compile + // the property classes (DeepFailChainBadClass) it found in the hierarchy. + $x = new MiddleFailChainClass(); +} catch (\Exception $e) { + printf("Test compiling MiddleFailChainClass Exception: %s\n", $e->getMessage()); +} + +try { + // succeeds at compiling its own map, then looks at its properties and + // succeeds at compiling MiddleFailChainClass, and then looks at that one's + // properties and fails at compiling the DeepFailChainBadClass it refers to. + $x = new TopFailChainClass(); +} catch (\Exception $e) { + printf("Test compiling TopFailChainClass Exception: %s\n", $e->getMessage()); +} + +var_dump('---------------------'); + +class TestNullValue extends LazyJsonMapper +{ + const JSON_PROPERTY_MAP = [ + 'this_is_null' => '\foo\TestNullValue', + ]; +} + +$json = <<getThisIsNull()); +} catch (LazyJsonMapperException $e) { + printf("TestNullValue Exception: %s\n", $e->getMessage()); +} + +var_dump('---------------------'); + +class TestNoCastValue extends LazyJsonMapper +{ + const JSON_PROPERTY_MAP = [ + 'no_cast1' => '', + 'no_cast2' => 'mixed', // same as '' + 'no_cast3' => '', + ]; +} + +$json = <<getNoCast1()); + var_dump($x->getNoCast2()); + var_dump($x->getNoCast3()); + $x->setNoCast1('should succeed without type-forcing'); + var_dump($x->getNoCast1()); +} catch (LazyJsonMapperException $e) { + printf("TestNoCastValue Exception: %s\n", $e->getMessage()); +} + +var_dump('---------------------'); + +class TestDepth extends LazyJsonMapper +{ + const JSON_PROPERTY_MAP = [ + 'array_of_arrays_of_arrays_of_int' => 'int[][][]', + ]; +} +$x = new TestDepth([]); // Init with no data. +try { + $x->setArrayOfArraysOfArraysOfInt([[new Test()]]); +} catch (LazyJsonMapperException $e) { + printf("Test non-array value at depth 2 of 3 Exception: %s\n", $e->getMessage()); +} + +try { + $x->setArrayOfArraysOfArraysOfInt([[[new Test()]]]); +} catch (LazyJsonMapperException $e) { + printf("Test invalid value at depth 3 of 3 Exception: %s\n", $e->getMessage()); +} + +try { + $x->setArrayOfArraysOfArraysOfInt([[[[]]]]); +} catch (LazyJsonMapperException $e) { + printf("Test invalid array-value at depth 3 of 3 Exception: %s\n", $e->getMessage()); +} +$x->setArrayOfArraysOfArraysOfInt([[[1, '456', 100, 5.5]], [], null, [[20]]]); +var_dump($x); // 1, 456, 100, 5, 20 + +var_dump('---------------------'); + +// test working with raw properties not defined in the class property map. +class UndefinedPropertyAccess extends LazyJsonMapper +{ +} +$json = '{"some_undefined_prop":null}'; +$data = json_decode($json, true, 512, JSON_BIGINT_AS_STRING); + +try { + // if we run with VALIDATION (TRUE), this will throw an exception + // since the data's "some_undefined_prop" is not in the class property map. + // NOTE: We already did this test as TestUndefinedProps earlier... + $x = new UndefinedPropertyAccess($data, true); +} catch (\Exception $e) { + printf("Test creating class instance with validation detecting undefined properties Exception: %s\n", $e->getMessage()); +} + +$x = new UndefinedPropertyAccess($data); +var_dump($x); +var_dump($x->hasSomeUndefinedProp()); // true +var_dump($x->isSomeUndefinedProp()); // false (the null evaluates to false) +var_dump($x->getSomeUndefinedProp()); // null +$x->setSomeUndefinedProp(['no data validation since it is undefined']); +var_dump($x->isSomeUndefinedProp()); // true (the array evaluates to true) +var_dump($x->getSomeUndefinedProp()); // array with a string in it +$x->setSomeUndefinedProp('xyz'); +var_dump($x->hasSomeUndefinedProp()); // true +var_dump($x->getSomeUndefinedProp()); // "xyz" +var_dump($x->isSomeUndefinedProp()); // true (the string evaluates to true) +$x->setSomeUndefinedProp(null); +var_dump($x->hasSomeUndefinedProp()); // true +var_dump($x->getSomeUndefinedProp()); // null + +var_dump('---------------------'); + +// test of advanced multi-class inheritance: +// OurTree* is a set of classes inheriting (extending) each other. +// Unrelated* are two other classes extending each other. +// FarClass is a single class without any other parents except LazyJsonMapper. +// +// OurTreeThree compiles its own inherited hierarchy, which then imports +// UnrelatedTwo, which compiles its own hierarchy, which then finally imports +// FarClass. The result is a final, compiled map which includes all classes. +// +// (and as noted in the main source code, memory cost of inheritance is 0 since +// all classes inherit each other's PropertyDefinition objects; the only cost is +// the amount of RAM it takes for an array["key"] to link to the borrowed object) +class FarClass extends LazyJsonMapper +{ + const JSON_PROPERTY_MAP = [ + 'farclass' => '\Foo\FarClass', + ]; +} +class UnrelatedOne extends LazyJsonMapper +{ + const JSON_PROPERTY_MAP = [ + FarClass::class, + 'unrelated_one' => 'int', + ]; +} +class UnrelatedTwo extends UnrelatedOne +{ + const JSON_PROPERTY_MAP = [ + 'unrelated_two' => 'float', + 'conflicting_prop1' => '\Foo\UnrelatedOne', + 'conflicting_prop2' => '\Foo\UnrelatedOne', + ]; +} +class OurTreeOne extends LazyJsonMapper +{ + const JSON_PROPERTY_MAP = [ + 'ourtree_one' => 'string', + ]; +} +class OurTreeTwo extends OurTreeOne +{ + const JSON_PROPERTY_MAP = [ + 'ourtree_two' => 'int', + 'conflicting_prop1' => '\Foo\OurTreeThree', // will be overwritten + UnrelatedTwo::class, // ... by this import + 'conflicting_prop2' => '\Foo\OurTreeThree', // will overwrite the import + ]; +} +class OurTreeThree extends OurTreeTwo +{ + const JSON_PROPERTY_MAP = [ + 'ourtree_three' => 'bool[]', + ]; + + protected function _init() + { + echo "Hello world from the init function!\n"; + } +} + +$x = new OurTreeThree(); +var_dump($x); + +var_dump('---------------------'); + +// LOTS OF TESTS OF DIRECT BY-REFERENCE ACCESS, BOTH INTERNALLY AND EXTERNALLY. +// INTERNALLY: &_getProperty(), EXTERNALLY: &__get() +class TestGetProperty extends LazyJsonMapper +{ + const JSON_PROPERTY_MAP = [ + 'foo' => '\Foo\TestGetProperty[]', + 'bar' => 'int', + ]; + + protected function _init() + { + // always set "bar" to a good value after construction (this is just to + // avoid code repetition during the testing... and to test the _init function) + $this->_setProperty('bar', 1234); + + // uncommenting this will test the "must be LazyUserException" error: + // throw new \Exception('x'); // is rejected and replaced with generic + // throw new LazyUserException('What'); // okay, message propagates + } + + public function runTest() + { + // just show the current internal data (nothing exists) + var_dump($this); // foo is empty + + // test retrieving prop, but not saving missing NULL to _objectData + var_dump($this->_getProperty('foo')); // NULL + var_dump($this); // foo still empty + + // test saving reference to return-value, but not saving default NULL to _objectData + $val = &$this->_getProperty('foo'); // missing important createMissingValue param + $val = 'hi'; // does NOT modify the real property, modifies some temp var + var_dump($this); // foo still empty + + // test saving reference to return-value, and creating + filling default + // inner NULL value so that we can properly trust our references to always + // return to real inner data. this is the correct way to save the return + // by reference. + $val = &$this->_getProperty('foo', true); + var_dump($this); // foo now has a NULL value in its object data + $val = 'hi, this worked because we are linked to real internal data!'; + var_dump($this); // overwritten internal value thanks to proper link + + // notice how we have set an invalid STRING value to the internal data? + // the "foo" property was specified to need to be an array of object + // instances (or NULL is ok too). well, we will detect that the next + // time we try to retrieve that property! + try { + $this->_getProperty('foo'); + } catch (\Exception $e) { + printf("Test _getProperty() with invalid data inserted by reference Exception: %s\n", $e->getMessage()); + } + + // let's try satisfying its array requirements (but still fail its type req) + $val = ['string inside array now fits array req but not type req']; + + try { + $this->_getProperty('foo'); + } catch (\Exception $e) { + printf("Test _getProperty() with valid array but invalid type inserted by reference Exception: %s\n", $e->getMessage()); + } + + // now let's fix the property (set it back to NULL, or we could have + // made an array of this class to satisfy the requirement). + $val = null; + var_dump($this); // foo is now NULL again + + // lastly, let's show that the value is always copy-on-write if the & + // operator is omitted from the function call. because then PHP is told + // to make $val into copy-on-write. + unset($val); // important: break its current reference to avoid assigning to it + $val = $this->_getProperty('foo'); + $val = 'not modified!'; + var_dump($this); // foo still NULL, since $val is not a reference + + // for completeness sake, also test the "bar" property which has a value + // and therefore ignores the "createMissingValue" + $bar = &$this->_getProperty('bar', true); + var_dump($bar); // int(1234), since a value already existed + var_dump($this); // bar still 1234 + $bar = 456; + var_dump($this); // bar is now 456 + } +} + +// run the internal $this->_getProperty call tests: +$x = new TestGetProperty(); +$x->runTest(); + +// run the external __get() call tests used for "virtual property access": +$x = new TestGetProperty(); // reset the internal data +var_dump($x); // has no "foo" data, only "bar" +// accessing ->foo calls __get(), which creates "foo" since virtual prop access +// requires true internal data references, otherwise they would misbehave. so it +// creates the missing property and gives it the default NULL value. +var_dump($x->foo); // null +var_dump($x); // also has "foo" now +// accessing ->bar calls __get() which sees that it exists, and gives us its value. +var_dump($x->bar); // 1234 +// trying to set varibles via equals causes __set() to run, which validates all data: +try { + $x->bar = ['invalid']; // int is expected +} catch (\Exception $e) { + printf("Test __set() with invalid value Exception: %s\n", $e->getMessage()); +} +$x->bar = '932'; // this is okay, __set() sees it is valid and casts it to int +var_dump($x); // "bar" is now int(932) +// now let's do some evil special cases! we will steal a direct reference to the +// internal _objectData['bar'], and then modify it, thus bypassing all validation. +$evilRef = &$x->bar; // now holds reference to bar +$evilRef = ['invalid']; // works, since we're literally modifying internal data +var_dump($x); // "bar" now has an invalid value (an array with a string) +try { + // luckily, every call to _getProperty() (which __get() uses) will validate + // the data to ensure that its internal state is valid and fits the class map. + var_dump($x->bar); +} catch (\Exception $e) { + printf("Test detection of injected invalid data during next __get() Exception: %s\n", $e->getMessage()); +} +$x->bar = 789; // call __set() and give it a new, valid value again. +var_dump($x->bar); // int(789), it is now fixed! +// lastly, let's play with direct access to internal arrays. anytime you access +// an array, it will call __get() to get the array, and then PHP resolves your +// [] brackets on the returned array. which means that we can modify arrays by +// reference automatically! +$x->foo = []; // runs __set(): create empty array for this "\Foo\TestGetProperty[]" property +var_dump($x->foo); // runs __get() which sees valid empty array and returns it +$x->foo[] = new TestGetProperty(); // okay data written by ref to "foo" array +var_dump($x->foo); // runs __get(), which sees valid array of 1 item of right type +$x->foo[] = 'invalid'; // this is allowed because __get() gets "foo" and then + // PHP just directly modifies the array... +var_dump($x); // the "foo" prop now has invalid data in it +// but luckily, anything that calls _getProperty() again, such as __get(), will +// cause validation of the data: +try { + // var_dump($x->foo); // calls __get(), would also throw the error. + $x->foo[] = 'x'; // calls __get() again to resolve "->foo" and throws error +} catch (\Exception $e) { + printf("Test detection of invalid injected data via array access Exception: %s\n", $e->getMessage()); +} +$x->foo = [new TestGetProperty(), new TestGetProperty()]; // run __set() to give okay array again, with 2 entries +var_dump($x->foo); // shows the array with 2 objects in it +$x->foo[0] = null; // runs __get(), gets "foo" by reference, and directly modifies element +var_dump($x->foo); // shows array with 1 NULL in it (this array is valid hence no error) +// we can also __get() the internal array, then loop over the +// values-by-reference, to directly modify them without any validation: +foreach ($x->foo as $k => &$fooVal) { + $fooVal = 'invalid'; +} +var_dump($x); // array with string "invalid". +try { + // if this had been unset($x->foo) it would work, but we try to unset a + // sub-element which means it actually calls __get() instead of __unset() + unset($x->foo[0]); // calls __get(), sees that the data is now invalid +} catch (\Exception $e) { + printf("Test detection of invalid injected data via array by-reference value loop Exception: %s\n", $e->getMessage()); +} +var_dump($x); // two "invalid" remains +$x->foo = [null, new TestGetProperty()]; // let's make it a valid 2-element +var_dump($x->foo); // array of [null,obj]; +unset($x->foo[0]); // runs __get() on "foo", then unsets the 0th element + +// these tests were commented out after adding strict sequence valiation: +// var_dump($x->foo); // array of [obj];, with the 0 array key missing +// unset($x->foo[1]->bar); // runs__get() on "foo", gets array, finds 1st elem, +// // sees object, runs __unset() on that ones "bar" +// var_dump($x); // reveals that the inner object no longer has any "bar" value + +// let's test accessing an object inside an array. and for fun add in regular getters +// NOTE: there is a subtle difference. getFoo() returns copy-on-write array, but +// any objects within it are of course objects and can be modified and will propagate. +$x->foo = [new TestGetProperty()]; +var_dump($x->foo[0]->bar); // int(1234) +var_dump($x->foo[0]->getBar()); // int(1234) +var_dump($x->getFoo()[0]->getBar()); // int(1234) +var_dump($x->getFoo()[0]->bar); // int(1234) +$x->getFoo()[0]->setBar(10); +var_dump($x); // the 0th "foo" array element has a bar of int(10) now +$x->getFoo()[0] = 'xyz'; // this does nothing, since getFoo() is always copy-on-write. +var_dump($x); // still intact, statement above had no effect, which is as intended +// now let's modify the array by reference to avoid constant __get() calls... +$arr = &$x->foo; +$arr = [1, 2, 'f'=>'bar', [['very invalid data']]]; +$arr[] = 'more invalid stuff...'; +var_dump($x); // very invalid stuff... +try { + var_dump($x->foo); +} catch (\Exception $e) { + printf("Test __get() after lots of bad array edits by reference Exception: %s\n", $e->getMessage()); +} +$arr = null; +var_dump($x->foo); // now it is fine again (NULL) +// let's call a normal array-command on the returned array-by-ref +$x->foo = []; // first make it into an array +array_push($x->foo, 'zzz'); // now __get() "foo" and then directly push (same as $x->foo[] = 'bar';) +var_dump($x); // we have directly added invalid data "zzz" into the array. +$x = null; // release the object... + +var_dump('---------------------'); + +// Test PropertyDefinition equality: +$a = new PropertyDefinition('int[]'); +$b = new PropertyDefinition('int'); +$c = new PropertyDefinition('int[]'); +var_dump($a->equals($b)); // false +var_dump($a->equals($c)); // true +var_dump($b->equals($a)); // false +var_dump($b->equals($c)); // false +var_dump($c->equals($a)); // true +var_dump($c->equals($b)); // false + +var_dump('---------------------'); + +// Test inheriting from base-class and also importing from other-class +class OtherBase extends LazyJsonMapper +{ + const JSON_PROPERTY_MAP = [ + 'otherbase' => 'string[]', + // ImportMapTest::class, // triggers circular map error IF ImportMapTest + // // itself refers to our class hierarchy. + ]; +} +class Other extends OtherBase +{ + const JSON_PROPERTY_MAP = [ + 'identical_key' => 'float', + 'other' => 'int', + ]; +} +class Base extends LazyJsonMapper +{ + const JSON_PROPERTY_MAP = [ + 'base' => 'float', + ]; +} +class ImportMapTest extends Base +{ + const JSON_PROPERTY_MAP = [ + // Base::class, // self-hierarchy reference (not allowed) + // ImportMapTest::class, // self-class reference (not allowed) + C::class, // reference to deeper version of self (not allowed) + Other::class, // okay, since it's another class.. but only ok if that + // other class doesn't have its circular reference enabled! + 'identical_key' => 'string', // should be string, since we add it after + // importing Other. but the one in Other should + // remain as its own one (float). + ]; +} +class C extends ImportMapTest +{ +} + +try { + $x = new C(); // comment in/out various class references above to test + // various arrangements of bad circular references. + var_dump($x); // if successful inheritance, print the + // _compiledPropertyMapLink so we can verify that all values + // are properly merged. +} catch (\Exception $e) { + printf("Test resolved-shared-ancestor circular map Exception: %s\n", $e->getMessage()); +} + +class AA extends LazyJsonMapper +{ + const JSON_PROPERTY_MAP = [ + BB::class, + ]; +} +class BB extends LazyJsonMapper +{ + const JSON_PROPERTY_MAP = [ + AA::class, + ]; +} + +try { + $x = new AA(); +} catch (\Exception $e) { + printf("Test resolved-shared-ancestor circular map Exception: %s\n", $e->getMessage()); +} + +// ensure that the locks are empty after all the construction failures above +$x = new LazyJsonMapper(); +$reflect = new \ReflectionProperty($x, '_propertyMapCache'); +$reflect->setAccessible(true); +var_dump($reflect->getValue()->compilerLocks); // should be empty array + +var_dump('---------------------'); + +// this was written to test PropertyDefinition re-use (RAM saving) when a class +// re-defines a property to the exact same settings that it already inherited. +// properties in LazyJsonMapper will keep their parent's/imported value if their +// new value is identical, thus avoiding needless creation of useless objects +// that just describe the exact same settings. it makes identical re-definitions +// into a zero-cost operation! +class HasFoo extends LazyJsonMapper +{ + const JSON_PROPERTY_MAP = [ + 'foo' => 'string[]', + ]; +} +class RedefinesFoo extends HasFoo +{ + const JSON_PROPERTY_MAP = [ + // 'foo' => 'float', // tests non-identical settings (should USE NEW obj) + 'foo' => 'string[]', // tests identical settings (should KEEP parent + // obj). memory usage should be same as if 'foo' + // wasn't re-defined on THIS object at all. + // 'foo' => 'string[][]', // tests non-identical settings (should USE NEW obj) + 'extra' => '\LazyJsonMapper\LazyJsonMapper', + ]; +} +$mem = memory_get_usage(); +$x = new RedefinesFoo(); +unset($x); // free the object itself, so we only keep the compiled map cache +printf("Memory increased by %d bytes.\n", memory_get_usage() - $mem); + +var_dump('---------------------'); + +// test function overriding: +class OverridesFunction extends LazyJsonMapper +{ + const JSON_PROPERTY_MAP = [ + 'foo' => 'string', + ]; + + public function getFoo() + { + $value = $this->_getProperty('foo'); + + return 'Custom getter: '.var_export($value, true); + } + + public function setFoo( + $value) + { + $value = sprintf('Tried "%s" but we will write "%s" instead.', $value, md5(time())); + $this->_setProperty('foo', $value); + + return $this; + } +} + +$x = new OverridesFunction(); +var_dump($x->getFoo()); +$x->setFoo('ignored'); +var_dump($x->getFoo()); + +var_dump('---------------------'); + +// Test rejection of associative array keys in "array of" JSON definition, since +// those are illegal JSON. + +class TestArrayKeyValidation extends LazyJsonMapper +{ + const JSON_PROPERTY_MAP = [ + 'obj_arr' => '\Foo\TestArrayKeyValidation[]', + ]; +} + +// normal sequence, okay: +$x = new TestArrayKeyValidation(['obj_arr' => [null, null, null, new TestArrayKeyValidation()]]); +var_dump($x->getObjArr()); + +// gap in sequence, not okay: +$x = new TestArrayKeyValidation(['obj_arr' => [1 => new TestArrayKeyValidation()]]); + +try { + var_dump($x->getObjArr()); +} catch (\Exception $e) { + printf("* Test numeric gap in typed 'array of' sequence: %s\n", $e->getMessage()); +} + +// This is ok because of a PHP quirk which converts '0' to 0 if used as array +// key in certain cases such as this one. The key here is literally int(0): +$x = new TestArrayKeyValidation(['obj_arr' => ['0' => new TestArrayKeyValidation()]]); +var_dump($x->getObjArr()); + +// string key in numerically indexed "array of", not okay: +$x = new TestArrayKeyValidation(['obj_arr' => ['not_allowed_to_have_key' => new TestArrayKeyValidation()]]); + +try { + var_dump($x->getObjArr()); +} catch (\Exception $e) { + printf("* Test illegal string-based key in typed 'array of' sequence: %s\n", $e->getMessage()); +} + +var_dump('---------------------'); + +// Test validation of mixed data (only allows NULL, int, float, string, bool, or +// numerically indexed arrays of any of those types). Untyped arrays always do +// array key validation. + +class TestUntypedValidation extends LazyJsonMapper +{ + const JSON_PROPERTY_MAP = [ + 'unt' => '', // behavior would be the same if this property is not + // defined in class map but then it would have to exist in + // the original input data, so I defined it here as untyped. + 'strict_depth' => 'mixed[][]', // enforces mixed non-array data at 2 + // levels deep within an array + ]; +} + +$x = new TestUntypedValidation(); + +try { + $x->setUnt(new \stdClass()); +} catch (\Exception $e) { + printf("* Test set-untyped rejection of object \stdClass: %s\n", $e->getMessage()); +} + +try { + $x->setUnt([new \stdClass()]); +} catch (\Exception $e) { + printf("* Test set-untyped rejection of object [\stdClass]: %s\n", $e->getMessage()); +} + +$fh = null; + +try { + $fh = fopen(__DIR__.'/../funcListData.serialized', 'r'); + + try { + $x->setUnt($fh); + } catch (\Exception $e) { + printf("* Test set-untyped rejection of Resource: %s\n", $e->getMessage()); + } + + try { + $x->setUnt([$fh]); + } catch (\Exception $e) { + printf("* Test set-untyped rejection of [Resource]: %s\n", $e->getMessage()); + } +} finally { + if (is_resource($fh)) { + fclose($fh); + } +} + +// all other types are allowed in this untyped field: +$x->setUnt(null); +$x->setUnt(1); +$x->setUnt(1.5); +$x->setUnt('2'); +$x->setUnt(true); +$x->setUnt([null, 1, [1.5], '2', true]); +var_dump($x->getUnt()); + +// we also allow associative keys in untyped fields (which will become JSON +// objects), since that allows people to access "objects" in json data (arrays) +// without needing to manually map the properties to actual LazyJsonMapper +// NOTE: mixing key types can create weird JSON objects. so if people want +// strict validation they need to use typed fields instead, as seen above. +$x->setUnt([null, 1, ['foo' => 1.5], '2', true]); +var_dump($x->getUnt()); + +// lastly, test the "mixed[]" strict_depth which specified untyped data but +// exactly 1 array level deep. +var_dump($x->getStrictDepth()); +$x->setStrictDepth([[null, 1, null, '2', true], null, []]); +var_dump($x->getStrictDepth()); + +try { + $x->setStrictDepth([[null, 1, null, '2', true], 'not_array', []]); +} catch (\Exception $e) { + printf("* Test set-untyped rejection of non-array at array-depth: %s\n", $e->getMessage()); +} + +try { + $x->setStrictDepth([[null, 1, null, '2', true], 'foo' => 'bar', []]); +} catch (\Exception $e) { + printf("* Test set-untyped rejection of associative key in strict array depth: %s\n", $e->getMessage()); +} + +try { + $x->setStrictDepth([[null, 1, null, '2', true], [['too_deep']], []]); +} catch (\Exception $e) { + printf("* Test set-untyped rejection of array deeper than max depth: %s\n", $e->getMessage()); +} +var_dump($x->getStrictDepth()); +$x->setStrictDepth([]); // okay, since we never reach maxdepth +var_dump($x->getStrictDepth()); //accepted +$x->setStrictDepth(null); // null is always okay +var_dump($x->getStrictDepth()); //accepted +try { + $x->setStrictDepth('foo'); // rejected since the value is not at specified depth +} catch (\Exception $e) { + printf("* Test set-untyped rejection of value at not enough depth: %s\n", $e->getMessage()); +} + +var_dump('---------------------'); + +// Test FunctionCase name translations into property names: + +$x = new FunctionTranslation('ExportProp'); +var_dump($x); + +try { + // Invalid single-word lowercase FuncCase name. + $x = new FunctionTranslation('somelowercase'); +} catch (\Exception $e) { + printf("Test invalid single-word lowercase FuncCase name 'somelowercase' Exception: %s\n", $e->getMessage()); +} +$x = new FunctionTranslation('_MessageList'); +var_dump($x); +$x = new FunctionTranslation('Nocamelcase'); // Single uppercase = no camel. +var_dump($x); +$x = new FunctionTranslation('WithCamelCase'); // Multiple Ucwords = camel. +var_dump($x); + +var_dump('---------------------'); + +// Test property name translations into FunctionCase, and back... +// They must be 100% identical in both directions! + +// Test function names to property names, and then ensure that both snake_case +// and camelCase variants translate back to the same function name via PropertyTranslation. +$funcList = [ + 'getSome0XThing', + 'getSome0xThing', + 'getSomeThing', + 'get_Messages', + 'get__MessageList', + 'get0m__AnUn0x', + 'get__Foo_Bar__XBaz__', + 'get__Foo_Bar_', + 'get___M', + 'get_M', + 'get_0', + 'get_', + 'get___', + 'get123', + 'get123prop', + 'get123Prop', +]; +foreach ($funcList as $f) { + echo "---\n"; + list($functionType, $funcCase) = FunctionTranslation::splitFunctionName($f); + + $x = new FunctionTranslation($funcCase); + printf("* Function: '%s'\n- Type: '%s'\n- FuncCase: '%s'\n > snake: '%s',\n > camel: '%s'\n", $f, $functionType, $funcCase, $x->snakePropName, $x->camelPropName); + + $y = new PropertyTranslation($x->snakePropName); + $getter = 'get'.$y->propFuncCase; + printf("* Property: '%s' (snake)\n > func: '%s' (%s)\n", $x->snakePropName, $getter, $getter === $f ? 'ok' : 'fail'); + if ($x->camelPropName === null) { + echo "* Property: No Camel Property, skipping...\n"; + } else { + $y = new PropertyTranslation($x->camelPropName); + $getter = 'get'.$y->propFuncCase; + printf("* Property: '%s' (camel)\n > func: '%s' (%s)\n", $x->camelPropName, $getter, $getter === $f ? 'ok' : 'fail'); + } + echo "---\n"; +} + +var_dump('---------------------'); + +// test the special operator translator +$result = 'A + and - and * and / and finally % symbol... And some ++--**//%% close ones...'; +var_dump($result); +$result = \LazyJsonMapper\Magic\SpecialOperators::encodeOperators($result); +var_dump($result); +$result = \LazyJsonMapper\Magic\SpecialOperators::decodeOperators($result); +var_dump($result); + +class OperatorTest extends LazyJsonMapper +{ + const JSON_PROPERTY_MAP = [ + 'en+US' => 'string', + 'en-US' => 'string', + 'en/US' => 'string', + 'en%US' => 'string', + 'en*US' => 'string', + 'with;semicolon_and@at' => 'string', + ]; +} + +$optest = new OperatorTest([ + 'en+US' => 'plus', + 'en-US' => 'minus', + 'en/US' => 'divide', + 'en%US' => 'modulo', + 'en*US' => 'multiply', + 'with;semicolon_and@at' => 'complex characters here!', +]); + +$optest->printPropertyDescriptions(); +var_dump($optest->getEn_x2B_US()); // plus +var_dump($optest->getEn_x2D_US()); // minus +var_dump($optest->getEn_x2F_US()); // divide +var_dump($optest->getEn_x25_US()); // modulo +var_dump($optest->getEn_x2A_US()); // multiply +var_dump($optest->getWith_x3B_semicolonAnd_x40_at()); + +var_dump('---------------------'); + +// Test the property description system (the parameters are so strict that there +// isn't really anything to test, apart from the relative property param...) +$ownerClassName = get_class(new Test()); +$desc = new PropertyDescription( + $ownerClassName, + 'the_property', + new PropertyDefinition('\Foo\Test[][]'), + false // do not allow relative paths +); +var_dump($desc); + +$desc = new PropertyDescription( + $ownerClassName, + 'the_property', + new PropertyDefinition('\Foo\Test[][]'), + true // allow relative paths +); +var_dump($desc); + +// and now test the is_defined detection of UndefinedProperty: + +$desc = new PropertyDescription( + $ownerClassName, + 'the_property', + \LazyJsonMapper\Property\UndefinedProperty::getInstance(), + false // do not allow relative paths +); +var_dump($desc); + +$desc = new PropertyDescription( + $ownerClassName, + 'the_property', + \LazyJsonMapper\Property\UndefinedProperty::getInstance(), + true // allow relative paths +); +var_dump($desc); + +var_dump('---------------------'); + +// calculate the memsize of a propertydefinition under various circumstances + +// first... force autoloading to find each class if not already loaded, to avoid +// messing up the measurement. +$x = new PropertyDefinition(); +$x = new LazyJsonMapper(); + +unset($x); +$mem = memory_get_usage(); +$x = new PropertyDefinition('\LazyJsonMapper\LazyJsonMapper'); +printf("Memory size of a PropertyDefinition object referring to '\\LazyJsonMapper\\LazyJsonMapper': %d bytes.\n", memory_get_usage() - $mem); + +unset($x); +$mem = memory_get_usage(); +$x = new PropertyDefinition(); +printf("Memory size of a PropertyDefinition object referring to NULL ('mixed'/untyped): %d bytes.\n", memory_get_usage() - $mem); + +unset($x); +$mem = memory_get_usage(); +$x = new PropertyDefinition('int'); +printf("Memory size of a PropertyDefinition object referring to 'int': %d bytes.\n", memory_get_usage() - $mem); + +unset($x); +$mem = memory_get_usage(); +$x = new PropertyDefinition('int[]'); +printf("Memory size of a PropertyDefinition object referring to 'int[]': %d bytes.\n", memory_get_usage() - $mem); + +unset($x); +$mem = memory_get_usage(); +$x = new PropertyDefinition('float[][][]'); +printf("Memory size of a PropertyDefinition object referring to 'float[][][]': %d bytes.\n", memory_get_usage() - $mem); + +var_dump('---------------------'); + +// Test detection of undefined properties: +$undef = UndefinedProperty::getInstance(); +$def = new PropertyDefinition(); +var_dump($undef instanceof UndefinedProperty); // true +var_dump($def instanceof UndefinedProperty); // false + +var_dump('---------------------'); + +// the following test analyzes memory usage of FunctionTranslation objects vs +// storing the cache values as a regular array (without objects). since objects +// are specialized arrays in PHP, with lower memory needs, we see some pretty +// great space savings by using objects. +// +// these tests will determine the memory needs for the runtime function call +// translation cache. I'd say normally people use around 100 different +// properties. this code generates very realistic function names that mimic +// real-world data. + +// if true, re-use pre-built list of 10000 function names and ignore the other +// params below. that's useful because building the list of names is slow on PHP5. +// and especially because it guarantees comparable runs between PHP binaries. +$usePrebuiltFuncList = true; + +// how many function names to generate. large sample sizes = accurate averages. +// this only happens if "useprebuilt" is disabled. +// $funcCacheCount = 10000; // recommended +$funcCacheCount = 100; // fast for testing but gives inaccurate memory averages + +/* + * Here are results for PHP7 and PHP5 with 10000x entries to really demonstrate + * the correct averages by having a large enough sample size. + * + * PHP7: Array of 10000x FunctionTranslation objects: 2485704 bytes total, ~248.6 bytes per entry. + * PHP7: Array of 10000x numerically indexed arrays: 5394584 bytes total, ~539.5 bytes per entry. + * PHP5: Array of 10000x FunctionTranslation objects: 5104024 bytes total, ~510.4 bytes per entry. + * PHP5: Array of 10000x numerically indexed arrays: 6640864 bytes total, ~664.4 bytes per entry. + * + * Those numbers include the object AND the overhead of their associatively + * named string key in the parent (cache) array. The array key is the FuncCase + * portion of the name (meaning it lacks the functionType prefix like "get" or + * "unset"). + * + * The actual LazyJsonMapper project uses FunctionTranslation objects, and + * normal users can be expected to need around 100 cache entries to cover every + * property they use in their project. + * + * That's ~25kb of RAM on PHP7 or ~51kb on PHP5. ;-) + */ + +function randWords( + array $allWords) +{ + global $allWords; + + // pick 1-3 words + $keys = array_rand($allWords, mt_rand(2, 4)); + array_shift($keys); + + // ensure that they are all lowercase, with an uppercase first letter + $words = []; + foreach ($keys as $k) { + $w = ucfirst(preg_replace('/[^a-z]+/', 'x', strtolower($allWords[$k]))); + // The wordlist has many insanely long words... + // Limit the length to 2-5 chars per word (JSON programmers are terse) + $w = substr($w, 0, mt_rand(2, 5)); + $words[] = $w; + } + + return $words; +} +function randFunctionName( + array $allWords) +{ // Generates a valid "realistic" function name + // Commented out because we no longer use the function type as part of the + // parsing of FuncCase names. So we don't need it in the test-data. + // $functionType = ['has', 'get', 'set', 'is', 'unset'][mt_rand(0, 4)]; + // return $functionType.implode(randWords($allWords)); + + return implode(randWords($allWords)); +} +function buildFuncList( + array $allWords, + $count = 500) +{ // Generates a list of unique functions. + if (count($allWords) < 100) { + die('Not enough words...'); + } + $funcList = []; + while (count($funcList) < $count) { + $funcList[randFunctionName($allWords)] = true; + } + + return array_keys($funcList); +} + +if (!$usePrebuiltFuncList) { + if (!is_file('/usr/share/dict/words')) { + die('Dictionary file missing.'); + } + $allWords = @file('/usr/share/dict/words'); + + // build a list of $funcCacheCount amount functions that we'll put in a lookup cache + echo "- creating a list of {$funcCacheCount} random function names...\n"; + $funcList = buildFuncList($allWords, $funcCacheCount); + + // debug/list generation: + // var_dump($funcList); // uncomment to see quality of name generation + // file_put_contents(__DIR__.'/../funcListData.serialized', serialize($funcList)); + + echo "- function list built... running cache test...\n"; +} else { + if (!is_file(__DIR__.'/../funcListData.serialized')) { + die('No serialized function list.'); + } + $funcList = unserialize(file_get_contents(__DIR__.'/../funcListData.serialized')); +} + +// force autoloading of the class to prevent counting the class itself in mem +$x = new FunctionTranslation('Example'); + +// try storing them as FunctionTranslation objects +$holder = []; +foreach ($funcList as $funcCase) { + $newFuncCase = $funcCase.'x'; // Avoid variable re-use of incoming string. + $holder[$newFuncCase] = new FunctionTranslation($newFuncCase); + unset($newFuncCase); +} +// var_dump($holder); // warning: don't uncomment while testing; increases mem +$mem = memory_get_usage(); +unset($holder); +$totalmem = $mem - memory_get_usage(); +$indivmem = $totalmem / count($funcList); // includes the parent array overhead +printf("PHP%d: Array of %dx FunctionTranslation objects: %d bytes total, ~%.1f bytes per entry.\n", $hasSeven ? 7 : 5, count($funcList), $totalmem, $indivmem); + +// try storing them as a regular non-associative array +$holder = []; +foreach ($funcList as $funcCase) { + $newFuncCase = $funcCase.'y'; // Avoid variable re-use of incoming string. + $translation = new FunctionTranslation($newFuncCase); + $y = [ + // paranoid about PHP re-using the object's value, so let's tweak all: + substr($translation->snakePropName, 0, -1).'y', + $translation->camelPropName === null ? null : substr($translation->camelPropName, 0, -1).'y', + ]; + $holder[$newFuncCase] = $y; + unset($translation); + unset($newFuncCase); + unset($y); +} +// var_dump($holder); // warning: don't uncomment while testing; increases mem +$mem = memory_get_usage(); +unset($holder); +$totalmem = $mem - memory_get_usage(); +$indivmem = $totalmem / count($funcList); // includes the parent array overhead +printf("PHP%d: Array of %dx numerically indexed arrays: %d bytes total, ~%.1f bytes per entry.\n", $hasSeven ? 7 : 5, count($funcList), $totalmem, $indivmem); + +var_dump('---------------------'); + +// test cache clearing and the memory usage of each cache from this test-file. +$mem = memory_get_usage(); +$lookupCount = LazyJsonMapper::clearGlobalMagicLookupCache(); +printf("Saved %d bytes by clearing the magic function lookup cache, which contained %d function name translations.\n", $mem - memory_get_usage(), $lookupCount); + +$mem = memory_get_usage(); +$classCount = LazyJsonMapper::clearGlobalPropertyMapCache(); +printf("Saved %d bytes by clearing %d compiled class maps. But not all may have been freed from memory by PHP yet, if any class instance variables are still in scope.\n", $mem - memory_get_usage(), $classCount); + +var_dump('---------------------'); + +// perform lots of tests of the array converter: + +// assign the normal json data array, but do not recursively validate (convert) +// it since we want a mix of converted and unconverted data during this test... +$x = new Test($jsonData); + +// +// the magic: asArray() CLONES the internal data, then recursively validates all +// of it and then converts it back to a plain array. the result is therefore +// fully validated/type-converted as a side-effect of the conversion process. +// +// it does not touch the contents of the original object: +// +$x->getSelfObject(); // force self_object to evaluate and parse +var_dump($x); // look at raw data... nothing is parsed except self_object + +$asArray = $x->asArray(); + +// look at the original object... still nothing is parsed except self_object, +// which remains obj, with the exact same instance number. this verifies that +// asArray did not manipulate/destroy data in our object. +var_dump($x); + +// validate the asArray result for correctness: +// $asArray[] = 'x'; // uncomment this to trigger a mismatch below +// var_dump($asArray); // look at asarray contents +printf("The asArray() result matches original input array? %s\n", + // NOTE: Array === operator checks all keys, keytypes, key order, values, + // valuetypes and counts recursively. If true, arrays contain IDENTICAL. + ($asArray === $jsonData ? 'YES!' : 'No...')); + +// try tweaking the input data so that the class definition no longer matches: +$jsonData['self_array'] = [$jsonData['self_array']]; // wrap in extra array depth +$x = new Test($jsonData); + +try { + $asArray = $x->asArray(); +} catch (\Exception $e) { + printf("Trying asArray() with data that mismatches class map Exception: %s\n", $e->getMessage()); +} +$jsonData['self_array'] = $jsonData['self_array'][0]; // fix data again + +// try undefined/untyped (missing) field with acceptable basic non-object data: +// acceptable basic data is: "int, float, string, bool, NULL" (and arrays of those). +$jsonData['untyped_field_with_non_object'] = '123456foo'; +$x = new Test($jsonData); +$asArray = $x->asArray(); +printf("As array with untyped/undefined missing but ok data: %s\n", + ($asArray === $jsonData ? 'YES!' : 'No...')); + +// try undefined/untyped (missing) field with a LazyJsonMapper object. this will +// NOT be okay because untyped fields only allow basic PHP types mentioned above. +// NOTE: This can NEVER happen via real json_decode() data. It is a test against +// user's custom data arrays with bad values... +$jsonData['untyped_field_with_lazy_object'] = new LazyJsonMapper(['inner_val' => '123foo']); + +try { + $x = new Test($jsonData, true); // true = run with validation +} catch (\Exception $e) { + printf("Test construction with validation enabled, and having an illegal value (object) in an undefined property Exception: %s\n", $e->getMessage()); +} +// try with non-LazyJsonMapper object too in a different property (will fail too +// since ALL OBJECTS are forbidden in undefined/untyped properties): +$jsonData['untyped_field_with_bad_object'] = new \stdClass(); +$x = new Test($jsonData); // now construct it WITHOUT validation, so the illegal + // value is undetected... +try { + $asArray = $x->asArray(); +} catch (\Exception $e) { + // should warn about BOTH the lazy and the "bad" object: + printf("Test asArray() on previously unvalidated object containing illegal values in data array Exception: %s\n", $e->getMessage()); +} + +try { + $x->getUntypedFieldWithBadObject(); +} catch (\Exception $e) { + // should warn about BOTH the lazy and the "bad" object: + printf("Test getUntypedFieldWithBadObject() on previously unvalidated object with illegal value in that field Exception: %s\n", $e->getMessage()); +} + +// now remove the fake data value again to restore the original jsonData... +unset($jsonData['untyped_field_with_lazy_object']); +unset($jsonData['untyped_field_with_bad_object']); + +$x = new Test($jsonData); +$asArray = $x->asArray(); // works again since all bad data is gone! +var_dump($asArray); + +// now try type-conversion to ensure that the type-map is followed: +class ForceType extends LazyJsonMapper +{ + const JSON_PROPERTY_MAP = [ + 'arr' => 'float[]', + ]; +} +$x = new ForceType(['arr' => ['1', '232', '94.2', 123.42]]); +var_dump($x->asArray()); // all are floats, exactly as the class-map requested +var_dump($x); // and as usual... internal _objectData remains untouched. + +// try non-integer arguments +try { + $x->asJson(false); +} catch (\Exception $e) { + printf("Test asJson() with non-int arg1: %s\n", $e->getMessage()); +} + +try { + $x->asJson(0, false); +} catch (\Exception $e) { + printf("Test asJson() with non-int arg2: %s\n", $e->getMessage()); +} + +// try requesting a json data depth that is way too low for the data: +try { + $x->asJson(0, 1); +} catch (\Exception $e) { + printf("Test asJson() with too low depth parameter Exception: %s\n", $e->getMessage()); +} + +// and for fun... +var_dump($x->asArray()); +var_dump($x->asJson()); +var_dump($x->asJson(JSON_PRETTY_PRINT)); +$x->printJson(); + +var_dump('---------------------'); + +$x = new Test($jsonData); + +// ensure that "convert object to string" works and properly outputs JSON... +echo '['.$x."]\n\n"; +echo $x; +echo PHP_EOL; + +// and test invalid data being output as with <> brackets as intended: +$bad = new Test(['self_object' => 1]); +echo PHP_EOL.'Test of clearly bad input data error handling as message string (since __toString cannot throw): '.$bad.PHP_EOL; + +var_dump('---------------------'); + +// try unsetting properties from the internal JSON data tree: +$x = new Test($jsonData); +$x->printJson(); +$x->unsetSelfArray() // NOTE: This tests the "chained unsetter" feature too. + ->unsetCamelCaseProp() + ->setSelfObject(new Test(['just_a_string' => '123 new object!'])); // Tests chained setter together with unsetters. +$x->printJson(); +$x->unsetSelfObject(); +$x->printJson(); + +// now try reading a property and then unsetting and reading it again: +var_dump($x->getStringArray()); +$x->unsetStringArray(); +var_dump($x->getStringArray()); +$x->printJson(); + +// also try using the direct unset() on the remaining values +unset($x->just_a_string); +unset($x->untyped_field_with_non_object); +$x->printJson(); + +var_dump('---------------------'); + +// Let's do some serialization tests: + +// First, run a recursive analysis to force all unparsed properties to evaluate +// into creating inner LazyJsonMapper objects. +$x = new Test($jsonData); +$x->exportClassAnalysis(); +var_dump($x); // tree of objects + +// Now test the secret, internal "tight packing" serialization method which +// returns the internal data as a plain array instead of as a serialized string: +$secretArr = $x->serialize($x); +var_dump($secretArr); + +// Now serialize the object into an actual, serialized string. The same way an +// end-user would do it. +// NOTE: This resolves all nested objects and serializes the root object with a +// single serialized, plain array within it. +$str = serialize($x); +var_dump($str); // no nested serialized objects + +// test the ability to fake "unserialize" into re-constructing objects from any +// serialized array. NOTE: this is just for testing proper re-building / +// unserialization in a different way. users should never do this. it's dumb. +// they should just create their "new TheClass([...])" instead. +$fakeunserialize = new Test(); +var_dump($fakeunserialize); // empty _objectData +$fakeunserialize->unserialize(serialize(['my_data' => 'hehe'])); +var_dump($fakeunserialize); // objectdata now has my_data + +// test exception when calling the function directly with bad params +try { + $fakeunserialize->unserialize(); + $fakeunserialize->unserialize(null); +} catch (\Exception $e) { + printf("Test unserialize manual call with bad params Exception: %s\n", $e->getMessage()); +} + +// lastly, let's test real unserialization as a new object instance. +// this creates a brand new object with the data array, and has no links to the +// original object (except using the same shared, compiled classmap since we are +// still in the same runtime and have a shared classmap cache entry available). +$new = unserialize($str); +// verify that all _objectData is there in the new object, and that unlike the +// original object (which had exportClassAnalysis() to create inner objects), +// this unserialized copy just has a plain data array: +var_dump($new); +// get a random property to cause it to convert it to its destination format: +$new->getSelfArray(); +var_dump($new); // self_array is now an array of actual objects + +var_dump('---------------------'); + +// test asArray/asStdClass which are aliases to exportObjectDataCopy +var_dump($x->exportObjectDataCopy('array')); +var_dump($x->asArray()); +// var_dump($x->exportObjectDataCopy('Array')); // test invalid type +var_dump($x->exportObjectDataCopy('stdClass')); +var_dump($x->asStdClass()); + +var_dump('---------------------'); + +// test new data assignment at a later time via assignObjectData(): +$foo = new Test(['just_a_string' => '123']); +$foo->printPropertyDescriptions(); +$foo->printJson(); +$foo->assignObjectData(['camelCaseProp' => 999, 'string_array' => ['a', 'b', 'c']]); +$foo->printJson(); + +var_dump('---------------------'); + +class TestJSONArrayKeys extends LazyJsonMapper +{ + const JSON_PROPERTY_MAP = [ + 'array_of_strings' => 'string[]', + ]; +} + +// first, test the fact that objects must always be output as {} notation. +$test = new TestJSONArrayKeys(); +var_dump($test->asJson()); +$test->printJson(); // {} +$test->setArrayOfStrings(['a', 'b']); +var_dump($test->asJson()); +$test->printJson(); // {"array_of_strings":["a","b"]} +$test = new TestJSONArrayKeys(['a', 'b']); +var_dump($test->asJson()); +$test->printJson(); // {"0":"a","1":"b"} + +// now do a test of the fact that properties defined as "array of" only allow +// sequential, numerical keys. +$test->setArrayOfStrings(['a', 'b']); +$test->setArrayOfStrings(['0' => 'a', 'b']); // works because PHP converts "0" to int(0) +$test->setArrayOfStrings([0 => 'a', 1 => 'b']); // correct order +try { + $test->setArrayOfStrings([1 => 'a', 0 => 'b']); // bad order +} catch (\Exception $e) { + printf("Test wrong order array keys, Exception: %s\n", $e->getMessage()); +} + +try { + $test->setArrayOfStrings([4 => 'a', 5 => 'b']); // not starting at 0 +} catch (\Exception $e) { + printf("Test array keys not starting at 0, Exception: %s\n", $e->getMessage()); +} + +try { + $test->setArrayOfStrings(['a', 'b', 'foo' => 'b']); // string-key +} catch (\Exception $e) { + printf("Test non-numeric array key in numeric array, Exception: %s\n", $e->getMessage()); +} + +var_dump('---------------------'); + +// test that our forced-object-notation {} JSON output works in all cases (even +// when strictly numeric keys or empty arrays). +// the correct, intended format is: The outer container (the object) is always +// {}, but any inner arrays in properties are []. + +$foo = new Test(); // no internal array assigned, so uses empty default array +var_dump($foo->asJson()); +$foo->printJson(); +$foo = new Test([1, [11, 22, 33], 3]); +var_dump($foo->asJson()); +$foo->printJson(); +$foo = new Test([0=>1, 1=>[11, 22, 33], 2=>3]); +var_dump($foo->asJson()); +$foo->printJson(); +$foo = new Test([0=>1, '1'=>[11, 22, 33], 2=>3]); +var_dump($foo->asJson()); +$foo->printJson(); +$foo = new Test([0=>1, 2=>3, 1=>[11, 22, 33]]); +var_dump($foo->asJson()); +$foo->printJson(); +$foo = new Test([1, [11, 22, 33], 3, 'x'=>1]); +var_dump($foo->asJson()); +$foo->printJson(); +$foo = new Test(['x'=>1, 1, [11, 22, 33], 3]); +var_dump($foo->asJson()); +$foo->printJson(); + +var_dump('---------------------'); + +// now end with some nice data dumping tests on the final, large data object... +// and let's use the newly unserialized object instance for fun.. +var_dump($new->asJson()); +var_dump($new->asJson(JSON_PRETTY_PRINT)); // manually controlling JSON output options +$new->printPropertyDescriptions(); +echo str_repeat(PHP_EOL, 5); +$new->printJson(false); // automatic printing but without pretty-print enabled +echo str_repeat(PHP_EOL, 5); +$new->getSelfObject()->printJson(); // printing a sub-object (only safe if obj is non-NULL) +echo str_repeat(PHP_EOL, 5); +// $new->getSelfArray()->printJson(); // would not work on PHP arrays, obviously +$new->getSelfArray()[0]->printJson(); // works, since that array entry is an obj +echo str_repeat(PHP_EOL, 5); +$new->printJson(); // <-- Debug heaven! ;-) diff --git a/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/devtools/junk/testPropertyDefinitionNamespaces.php b/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/devtools/junk/testPropertyDefinitionNamespaces.php new file mode 100755 index 0000000..7fc1a39 --- /dev/null +++ b/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/devtools/junk/testPropertyDefinitionNamespaces.php @@ -0,0 +1,173 @@ + 'string', + ]; + } +} + +namespace Foo\Deeper { + require __DIR__.'/../../vendor/autoload.php'; + + use LazyJsonMapper\LazyJsonMapper; + + class NoExtendsClass + { + } + + class MyClass extends LazyJsonMapper + { + const JSON_PROPERTY_MAP = ['foo' => 'string']; + } +} + +namespace Other\Space\VerySpace { + require __DIR__.'/../../vendor/autoload.php'; + + use LazyJsonMapper\LazyJsonMapper; + + class VeryDeepInSpace extends LazyJsonMapper + { + const JSON_PROPERTY_MAP = [ + 'deepspacenine' => 'string', + ]; + } +} + +namespace Other\Space { + require __DIR__.'/../../vendor/autoload.php'; + + use LazyJsonMapper\LazyJsonMapper; + use LazyJsonMapper\Property\PropertyDefinition; + + class OtherClass extends LazyJsonMapper + { + const JSON_PROPERTY_MAP = [ + 'from_other_class' => 'string', + ]; + } + + class MyClass extends LazyJsonMapper + { + const JSON_PROPERTY_MAP = [ + 'foo' => 'int', + // tests handling of missing relative classes (the warning will + // display the namespace of this class which defined the property): + // 'missing_relative' => 'NoSuchClass', // uncomment to test + // tests support for relative classes within same namespace: + 'relative_class_path' => 'OtherClass', + 'relative_sub_class_path' => 'VerySpace\VeryDeepInSpace', + // and global overrides (the "\" prefix makes PropertyDefinition + // use the global namespace instead): + 'global_class_path' => '\Foo\Deeper\MyClass', + // just for fun, let's import a class map too, via relative: + // (can be done via global or relative paths) + VerySpace\VeryDeepInSpace::class, + ]; + } + + $resolved = new MyClass(); + var_dump($resolved); +} + +namespace Foo\Other\Space { + require __DIR__.'/../../vendor/autoload.php'; + + use LazyJsonMapper\LazyJsonMapper; + + // This class is here to show that new $x->propType() construction technique + // is a bad idea since it may lead to relative resolving like this one, if + // the global path cannot be found. + class MyClass extends LazyJsonMapper + { + } +} + +namespace Foo { + require __DIR__.'/../../vendor/autoload.php'; + + use LazyJsonMapper\LazyJsonMapper; + use LazyJsonMapper\Property\PropertyDefinition; + + var_dump(\Other\Space\MyClass::class); + var_dump(class_exists('\Other\Space\MyClass')); + var_dump(class_exists('\Other\Space\\\MyClass')); + var_dump(Deeper\MyClass::class); + var_dump(__NAMESPACE__); + + echo "-----\n"; + + // test various combinations of namespaces and class prefixes: + // $x = new PropertyDefinition('\MyClass', __NAMESPACE__); + // $x = new PropertyDefinition('\MyClass\Deeper', __NAMESPACE__); + // $x = new PropertyDefinition('MyClass', __NAMESPACE__); + // $x = new PropertyDefinition('MyClass\Deeper', __NAMESPACE__); + // $x = new PropertyDefinition('MyClass'); + // $x = new PropertyDefinition('\MyClass'); + // $x = new PropertyDefinition('MyClass\Deeper'); + // var_dump($x); + + // test a valid relative path (and the cleanup/normalization of a bad name). + $x = new PropertyDefinition('deePER\MYClass[][]', __NAMESPACE__); + var_dump($x); + var_dump($x->asString()); + $y = new $x->propType(); // BAD! WE ALWAYS THE GLOBAL PATH, DO NOT USE THIS + // always use getStrictClassPath() instead! + var_dump($y); // \Foo\Deeper\MyClass instance + + // test a valid path in other space (via global path) + $x = new PropertyDefinition('\Other\SPACe\MYCLASS[][]', __NAMESPACE__); + var_dump($x); + var_dump($x->asString()); + + $type = "Deeper\MyClass"; // PHP would resolve this locally due to no \ + $y = new $type(); + var_dump($y); + + $type = "\Deeper\MyClass"; + $y = new $type(); + var_dump($y); + + echo "------\n"; + var_dump($x); + $y = new $x->propType(); // BAD IDEA! This field has no "\" prefix and may not + // resolve to the intended class in all situations + // correct way for extra safety is always: + $strictClassPath = $x->getStrictClassPath(); + var_dump($strictClassPath); + $y = new $strictClassPath(); + + var_dump($y); // \Other\Space\MyClass instance + + // test bad class warning (no extends) + // $x = new PropertyDefinition('deePER\noextendsCLASS', __NAMESPACE__); + + // test bad class warning via mistyped basic typename: + // $x = new PropertyDefinition('ints', __NAMESPACE__); +} diff --git a/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/devtools/junk/testUserFeatureToggling.php b/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/devtools/junk/testUserFeatureToggling.php new file mode 100755 index 0000000..e011523 --- /dev/null +++ b/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/devtools/junk/testUserFeatureToggling.php @@ -0,0 +1,205 @@ + 'string', + 'bar' => 'string', + ]; +} + +// -------------------------------------------------------- + +// Test class that disallows functions but allows properties. + +class DisallowsFunctions extends CoreMap +{ + const ALLOW_VIRTUAL_FUNCTIONS = false; + + public function getBar() + { + return $this->_getProperty('bar'); + } +} + +$jsonData = ['foo' => 'hello', 'bar' => 'world']; + +$x = new DisallowsFunctions($jsonData); + +echo str_repeat(PHP_EOL, 5); +$x->printPropertyDescriptions(); +$x->printJson(); + +// works since we have overridden that function manually +printf("getBar(): \"%s\"\n", $x->getBar()); + +// works since we allow direct property access in the class above +$x->bar = 'changed via direct virtual property access'; + +// look at the new value +printf("getBar(): \"%s\"\n", $x->getBar()); + +// does not work since we have no setter "setBar()" in the class above +try { + $x->setBar('xyzzy'); +} catch (\Exception $e) { + printf("setBar(): %s\n", $e->getMessage()); +} + +// try all function variations of the "foo" property. none should work. +try { + $x->hasFoo(); +} catch (\Exception $e) { + printf("hasFoo(): %s\n", $e->getMessage()); +} + +try { + $x->isFoo(); +} catch (\Exception $e) { + printf("isFoo(): %s\n", $e->getMessage()); +} + +try { + $x->getFoo(); +} catch (\Exception $e) { + printf("getFoo(): %s\n", $e->getMessage()); +} + +try { + $x->setFoo(); +} catch (\Exception $e) { + printf("setFoo(): %s\n", $e->getMessage()); +} + +try { + $x->unsetFoo(); +} catch (\Exception $e) { + printf("unsetFoo(): %s\n", $e->getMessage()); +} + +// -------------------------------------------------------- + +// Test class that disallows properties but allows functions. + +class DisallowsProperties extends CoreMap +{ + const ALLOW_VIRTUAL_PROPERTIES = false; +} + +$jsonData = ['foo' => 'hello', 'bar' => 'world']; + +$x = new DisallowsProperties($jsonData); + +echo str_repeat(PHP_EOL, 5); +$x->printPropertyDescriptions(); +$x->printJson(); + +// works since we allow functions +printf("getBar(): \"%s\"\n", $x->getBar()); +$x->setBar('changed via virtual setBar() function acccess'); + +// look at the new value +printf("getBar(): \"%s\"\n", $x->getBar()); + +// try all property acccess variations of the "foo" property. none should work. +try { + $test = $x->foo; +} catch (\Exception $e) { + printf("__get() via x->foo: %s\n", $e->getMessage()); +} + +try { + $x->foo[] = 'test'; // this __get()-trigger will fail too +} catch (\Exception $e) { + printf("__get() via x->foo[]: %s\n", $e->getMessage()); +} + +try { + $x->foo = 'xyz'; +} catch (\Exception $e) { + printf("__set() via x->foo = ...: %s\n", $e->getMessage()); +} + +try { + isset($x->foo); +} catch (\Exception $e) { + printf("__isset() via isset(x->foo): %s\n", $e->getMessage()); +} + +try { + empty($x->foo); +} catch (\Exception $e) { + printf("__isset() via empty(x->foo): %s\n", $e->getMessage()); +} + +try { + unset($x->foo); +} catch (\Exception $e) { + printf("__unset() via unset(x->foo): %s\n", $e->getMessage()); +} + +// -------------------------------------------------------- + +// Test class that disallows both. + +class DisallowsBoth extends CoreMap +{ + const ALLOW_VIRTUAL_PROPERTIES = false; + const ALLOW_VIRTUAL_FUNCTIONS = false; +} + +$x = new DisallowsBoth($jsonData); + +echo str_repeat(PHP_EOL, 5); +$x->printPropertyDescriptions(); + +try { + $test = $x->foo; +} catch (\Exception $e) { + printf("__get() via x->foo: %s\n", $e->getMessage()); +} + +try { + $x->getFoo(); +} catch (\Exception $e) { + printf("getFoo(): %s\n", $e->getMessage()); +} + +// -------------------------------------------------------- + +// Test class that extends "DisallowsBoth" and re-allows both. + +class ReallowsBoth extends DisallowsBoth +{ + const ALLOW_VIRTUAL_PROPERTIES = true; + const ALLOW_VIRTUAL_FUNCTIONS = true; +} + +$x = new ReallowsBoth($jsonData); + +echo str_repeat(PHP_EOL, 5); +$x->printPropertyDescriptions(); + +printf("getFoo(): \"%s\"\n", $x->getFoo()); +printf("x->bar: \"%s\"\n", $x->bar); diff --git a/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/devtools/nonRecursiveArrays.php b/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/devtools/nonRecursiveArrays.php new file mode 100755 index 0000000..0b07092 --- /dev/null +++ b/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/devtools/nonRecursiveArrays.php @@ -0,0 +1,402 @@ +current()); + } +} + +// Algorithm v1: The initial idea I had... +function array_flat_topdown_traverse( + array &$input) +{ + // Traverse top-down, processing level by level (going deeper and deeper). + $workStack = [&$input]; // The stack processes one array level at a time. + $nextStack = []; // Next stack with all deeper arrays found on this level. + $currentDepth = 1; // First level of input array should count from 1. + while (!empty($workStack)) { + // Pop a direct reference off the start of our FIFO stack. + reset($workStack); + $firstKey = key($workStack); + $pointer = &$workStack[$firstKey]; + unset($workStack[$firstKey]); + + // Now we're ready to act on the popped stack element... + foreach ($pointer as $k => &$v) { + // printf( + // "[D] %d %s\"%s\":%s\n", + // $currentDepth, + // str_repeat('-', $currentDepth), + // $k, + // is_array($v) ? '[]' : var_export($v, true) + // ); + + // Analyze the current array-child... + if (is_array($v)) { + // Add the discovered child-array to the end of the next-stack. + $nextStack[] = &$v; + } else { + // The child is a non-array element... Send it to the callback! + // TODO: Give callback key + value ref + array depth + } + } + + // If the work-stack is finished, switch to the next (deeper) stack. + if (empty($workStack)) { + $workStack = $nextStack; + $nextStack = []; + $currentDepth++; + } + } +} + +// Algorithm v2: Avoids two count() calls per stack-element iteration. +function array_flat_topdown_traverse2( + array &$input) +{ + // Traverse top-down, processing level by level (going deeper and deeper). + $workStack = [&$input]; // The stack processes one array level at a time. + $workStackSize = 1; // Hardcoded result of count($workStack). + $nextStack = []; // Next stack with all deeper arrays found on this level. + $currentDepth = 1; // First level of input array should count from 1. + while ($workStackSize > 0) { + // Pop a direct reference off the start of our FIFO stack. + reset($workStack); + $firstKey = key($workStack); + $pointer = &$workStack[$firstKey]; + unset($workStack[$firstKey]); + $workStackSize--; + + // Now we're ready to act on the popped stack element... + foreach ($pointer as $k => &$v) { + // printf( + // "[D] %d %s\"%s\":%s\n", + // $currentDepth, + // str_repeat('-', $currentDepth), + // $k, + // is_array($v) ? '[]' : var_export($v, true) + // ); + + // Analyze the current array-child... + if (is_array($v)) { + // Add the discovered child-array to the end of the next-stack. + $nextStack[] = &$v; + } else { + // The child is a non-array element... Send it to the callback! + // TODO: Give callback key + value ref + array depth + } + } + + // If the work-stack is finished, switch to the next (deeper) stack. + if ($workStackSize <= 0) { + // NOTE: There's no need to assign to workStack by reference to + // avoid copy-on-write. Because when we set nextStack to a new + // value, PHP will realize that workStack is the only instance. + // In fact, by-ref is slower it also needs an unset($nextStack) + // call to break its own reference before doing $nextStack = []. + $workStack = $nextStack; + $workStackSize = count($workStack); + $nextStack = []; + $currentDepth++; + } + } +} + +// Regular, old-school recursive function calls. +function array_recursive_traverse( + array &$input, + $currentDepth = 1) +{ + // Recursion adds 1 level to the function call stack + // per depth-level of the array: + // debug_print_backtrace(); + + $nextDepth = $currentDepth + 1; + foreach ($input as $k => &$v) { + // printf( + // "[D] %d %s\"%s\":%s\n", + // $currentDepth, + // str_repeat('-', $currentDepth), + // $k, + // is_array($v) ? '[]' : var_export($v, true) + // ); + + if (is_array($v)) { + array_recursive_traverse($v, $nextDepth); + } + } +} + +// Build an array data tree. +function generateData( + $depth) +{ + $data = []; + $pointer = &$data; + for ($d = 0; $d < $depth; ++$d) { + // $subArr = ['x', 'y', ['z'], ['foo'], [['xxyyzzyy']]]; // Harder data. + $subArr = ['x', 'y', 'z', ['foo'], 'xxyyzzyy']; + $pointer[] = &$subArr; + $pointer = &$subArr; + unset($subArr); // Unlink, otherwise next assignment overwrites pointer. + } + + return $data; +} + +// Run a single test. +function runTest( + $description, + $data, + $algorithm, + $iterations) +{ + $start = microtime(true); + + switch ($algorithm) { + case 'array_flat_topdown_traverse': + for ($i = 0; $i < $iterations; ++$i) { + array_flat_topdown_traverse($data); + } + break; + case 'array_flat_topdown_traverse2': + for ($i = 0; $i < $iterations; ++$i) { + array_flat_topdown_traverse2($data); + } + break; + case 'array_recursive_traverse': + for ($i = 0; $i < $iterations; ++$i) { + array_recursive_traverse($data); + } + break; + case 'RecursiveIteratorIterator': + for ($i = 0; $i < $iterations; ++$i) { + $iterator = new \RecursiveIteratorIterator( + new RecursiveArrayOnlyIterator($data), + \RecursiveIteratorIterator::SELF_FIRST + ); + // foreach ($iterator as $key => $value) { + // // echo "$key => $value\n"; + // } + // This iteration method takes 15% longer than foreach, + // but it's the only way to get the depth, which we + // absolutely need to know in this project. + for (; $iterator->valid(); $iterator->next()) { + $key = $iterator->key(); + $value = $iterator->current(); + $depth = $iterator->getDepth(); + } + } + break; + } + + printf( + "%dx %s %s: %.0f milliseconds.\n", + $iterations, $description, $algorithm, + 1000 * (microtime(true) - $start) + ); +} + +// Run all algorithm tests at once. +function runTestMulti( + $description, + $data, + $iterations, + $iteratorTestMode) // Time-saver: -1 off, 0 divide by ten, 1 normal +{ + if ($iteratorTestMode > -1) { + runTest($description, $data, 'RecursiveIteratorIterator', + $iteratorTestMode > 0 ? $iterations : (int) floor($iterations / 10)); + } + runTest($description, $data, 'array_flat_topdown_traverse', $iterations); + runTest($description, $data, 'array_flat_topdown_traverse2', $iterations); + runTest($description, $data, 'array_recursive_traverse', $iterations); +} + +// Special data test-tree for use together with debug-output (uncomment it in +// the algorithms), to verify that each algorithm detects the current depth. +$data = [ + '1one' => [ + '1two' => [ + '1three-nonarr1' => '1', + '1three' => [ + '1four-nonarr1' => '2', + '1four' => [ + '1five' => '3', + ], + ], + ], + ], + '2one-nonarr1' => null, + '3one' => [ + '3two-1' => [ + '3three-nonarr1' => '4', + '3three-1' => [ + '3four-1' => [ + '3five-1' => [ + '3six-nonarr1' => '5', + ], + ], + ], + '3three-nonarr2' => '6', + ], + '3two-nonarr1' => '7', + '3two-2' => [ + '3three-nonarr3' => '8', + ], + '3two-nonarr2' => '9', + ], +]; + +// The "RecursiveIteratorIterator" is ~10x slower, so this setting saves time. +// Values: -1 off, 0 divide by ten, 1 normal. +$iteratorTestMode = -1; + +// Globally extend/shorten the amount of test iterations, or "1" for no scaling. +$testScale = 1; + +// Output PHP version details. +printf("[Running %dx tests on PHP version %s]\n", $testScale, PHP_VERSION); +printf("[RecursiveIteratorIterator Tests: %s]\n", ['Disabled', 'Shortened by /10', 'Enabled'][$iteratorTestMode + 1]); + +// Test with normal data (6 levels deep). +runTestMulti('normal-6', generateData(6), $testScale * 500000, $iteratorTestMode); + +// Test unusual data (50 levels deep). +runTestMulti('rare-50', generateData(50), $testScale * 100000, $iteratorTestMode); + +// Now test with insanely deeply nested data. +runTestMulti('insane-500', generateData(500), $testScale * 10000, $iteratorTestMode); + +// Let's do one final test with even more disgustingly deep arrays. +runTestMulti('hellish-5000', generateData(5000), $testScale * 100, $iteratorTestMode); diff --git a/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/devtools/prefixSplitAlgorithms.php b/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/devtools/prefixSplitAlgorithms.php new file mode 100755 index 0000000..8c0acfc --- /dev/null +++ b/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/devtools/prefixSplitAlgorithms.php @@ -0,0 +1,270 @@ + 'string', + 'users' => 'User[]', + ]; +} + +class User extends LazyJsonMapper +{ + const JSON_PROPERTY_MAP = [ + 'name' => 'string', + 'details' => 'UserDetails', + ]; +} + +class UserDetails extends LazyJsonMapper +{ + const JSON_PROPERTY_MAP = [ + 'age' => 'int', + 'hired_date' => 'string', + ]; +} + +/* + * Now simply create the root object (a Section), and give it the section JSON: + */ + +$section = new Section(json_decode($section_json, true)); + +/* + * Here's how you would look at the available properties and their functions: + */ + +$section->printPropertyDescriptions(); + +/* + * Now let's access some data, via the functions shown by the previous command. + */ + +printf("\n\nData Output Example:\n\nSection Title: %s\n", $section->getSectionTitle()); +foreach ($section->getUsers() as $user) { + // $user->printPropertyDescriptions(); // Uncomment to see User-functions. + // $user->printJson(); // Uncomment to look at the JSON data for that user. + printf( + "- User: %s\n Age: %s\n Hired Date: %s\n", + $user->getName(), + $user->getDetails()->getAge(), + $user->getDetails()->getHiredDate() + ); +} +echo "\n\n"; + +/* + * Lastly, let's demonstrate looking at the actual internal JSON data: + */ + +// var_dump($section->asJson()); // Uncomment to get a JSON data string instead. +// var_dump($section->getUsers()[0]->asJson()); // Property sub-object encoding. +// var_dump(json_encode($section->getUsers())); // Property non-object values +// // solvable via `json_encode()`. +$section->printJson(); + +/* + * There are a million other functions and features. Have fun exploring! + * Simply read the main src/LazyJsonMapper.php file for all documentation! + */ diff --git a/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/examples/import_example.php b/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/examples/import_example.php new file mode 100755 index 0000000..50d7014 --- /dev/null +++ b/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/examples/import_example.php @@ -0,0 +1,42 @@ + 'string', + 'age' => 'int', + ]; +} + +class AdvancedUser extends User +{ + const JSON_PROPERTY_MAP = [ + 'advanced' => 'string', + ]; +} + +class SomethingElse extends LazyJsonMapper +{ + const JSON_PROPERTY_MAP = [ + AdvancedUser::class, // This is an "import class map"-command. + 'otherprop' => 'float[][]', + ]; +} + +/* + * This demonstrates that SomethingElse contains all fields from AdvancedUser + * (which in turn inherited User's map), as well as having its own "otherprop". + */ + +$somethingelse = new SomethingElse(); +$somethingelse->printPropertyDescriptions(); diff --git a/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/examples/inheritance_example.php b/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/examples/inheritance_example.php new file mode 100755 index 0000000..899c187 --- /dev/null +++ b/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/examples/inheritance_example.php @@ -0,0 +1,51 @@ + 'string', + 'age' => 'int', + ]; +} + +class AdvancedUser extends User +{ + const JSON_PROPERTY_MAP = [ + 'advanced' => 'string', + ]; +} + +/* + * This demonstrates that AdvancedUser contains all fields from User. + */ + +$advanceduser = new AdvancedUser(json_decode($advanceduser_json, true)); + +$advanceduser->printPropertyDescriptions(); +printf( + "\n\nName: %s\nAge: %s\nAdvanced: %s\n", + $advanceduser->getName(), + $advanceduser->getAge(), + $advanceduser->getAdvanced() +); +$advanceduser->printJson(); diff --git a/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/examples/unpredictable_data.php b/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/examples/unpredictable_data.php new file mode 100755 index 0000000..2d366a5 --- /dev/null +++ b/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/examples/unpredictable_data.php @@ -0,0 +1,705 @@ + 'string', + ]; +} + +/** + * This container supports unpredictable keys. + * + * Note that it doesn't define any properties in its JSON_PROPERTY_MAP. Instead, + * we use a custom getter which reads _all_ JSON properties and processes them! + * + * In other words, this _entire_ container will hold nothing but unpredictable + * data keys. (If you have a mixture, there are other examples further down.) + */ +class UnpredictableContainer extends LazyJsonMapper +{ + /** + * Cached key-value object translations. + * + * This is optional, but speeds up repeated calls to `getUserList()`, since + * it will store all previously converted objects for future re-use. + * + * @var array + */ + protected $_userList; + + /** + * Get the list of users. + * + * @throws LazyJsonMapperException + * + * @return array Associative array of key-value pairs, keyed by userID. + */ + public function getUserList() + { + // Tell LazyJsonMapper to give us all of our internal data as an array. + // NOTE: This creates a COPY of the array. It's not attached to the main + // storage, which means that any changes we make to the new array aren't + // going to affect the actual LazyJsonMapper object's own data. That + // won't matter for pure getter-based objects like this one (which is + // all you'll need in 99.9999999% of cases where you'll want to read + // unpredictable data). So if the user serializes this LazyJsonMapper + // object, or gets it "asJson()", etc, then they'd still be serializing + // the original, untouched internal data, which is exactly what we are + // retrieving here. So everything works out perfectly as long as we + // don't have any setters! (But setters will be demonstrated later.) + if ($this->_userList === null) { + // Get a copy of the internal data as an array, and cache it. + // NOTE: This only throws if there is unmappable data internally. + $this->_userList = $this->asArray(); // Throws. + } + + // Loop through the list of JSON properties and convert every "array" + // value to a User object instead. That's because all of JSON's nested, + // not-yet-converted "JSON object values" are associative sub-arrays. + foreach ($this->_userList as &$value) { + if (is_array($value)) { + // NOTE: The User constructor can only throw if a custom _init() + // fails or if its class property map can't be compiled. + $value = new User($value); // Throws. + } + } + + // Now just return our key-value array. The inner array values have + // been converted to User objects, which makes them easy to work with. + return $this->_userList; + } +} + +/* + * Let's try it out with two sets of data that use unpredictable (numeric) keys! + */ + +/* + * This JSON data consists of various numeric keys pointing at "User"-objects. + */ +$unpredictable_json1 = <<printJson(); + +/* + * Now let's call our custom getter which retrieves all values and gives them to + * us as User objects. As you can see, their contents perfectly match the + * printJson() results, since our custom getter doesn't manipulate any data. + */ +foreach ($unpredictable1->getUserList() as $userId => $userInfo) { + printf("User ID: %s\n Name: %s\n", $userId, $userInfo->getName()); +} + +/* + * Let's do the same for another set of data with other keys... + */ +$unpredictable_json2 = <<printJson(); + +foreach ($unpredictable2->getUserList() as $userId => $userInfo) { + printf("User ID: %s\n Name: %s\n", $userId, $userInfo->getName()); +} + +/* + * Alright, that's all great... but what if we want to manipulate the data? + * + * Well, the loop above still has $userId and $userInfo variables that point at + * the last element it looped to... so let's try using those to set data! What + * can possibly go wrong!? ;-) + */ +printf("*** Changing the name of user #%s to 'FOO'.\n", $userId); +$userInfo->setName('FOO'); + +/* + * Now let's look at the contents of the "getUserList()" call... + * + * Because the user-list is cached internally in our custom object, it already + * refers to the exact same object instances... So it will indeed have updated. + */ +foreach ($unpredictable2->getUserList() as $userId => $userInfo) { + printf("User ID: %s\n Name: %s\n", $userId, $userInfo->getName()); +} + +/* + * But wait... what about the actual internal object data? Let's look at that! + * + * The name... is not updated... + */ +$unpredictable2->printJson(); + +/* + * Uh oh... since we're using a custom getter which fetches the data totally + * detached from the core LazyJsonMapper storage, we will need another solution! + * + * These extra steps are ONLY needed if you want setters that actually work... + */ + +/** + * Extends UnpredictableContainer with a custom setter. + * + * We could have put these functions on the main UnpredictableContainer, but + * this example is clearer by only explaining it here as a separate step for + * those who need setters... + */ +class UnpredictableContainerWithSetter extends UnpredictableContainer +{ + /** + * Syncs the user list cache with the LazyJsonMapper core storage. + * + * Note that we could build these steps into `setUserList()`. But by having + * it as a separate function, you are able to just update specific User + * objects and then call `syncUserList()` to write them back to the core. + * + * @throws LazyJsonMapperException + */ + public function syncUserList() + { + // If no internal cache exists yet, get its value from LazyJsonMapper. + if ($this->_userList === null) { + $this->getUserList(); // Builds our "_userList" variable. Throws. + } + + // Now, we need to create a new, internal LazyJsonMapper data array for + // our object. In undefined (unmapped) properties, you are ONLY allowed + // to store basic data types. Not objects. So we will need to convert + // all User objects to real array data. Otherwise OTHER calls would fail + // due to invalid internal data when we try things like `printJson()`. + $newObjectData = []; + foreach ($this->_userList as $k => $v) { + $newObjectData[$k] = is_object($v) && $v instanceof LazyJsonMapper + ? $v->asArray() // Throws. + : $v; // Is already a valid value. + } + + // Now give the new object data to LazyJsonMapper, which ensures that it + // contains all of the same values as our updated cache! This replaces + // the ENTIRE internal JSON property storage, so be aware of that! + $this->assignObjectData($newObjectData); // Throws. + } + + /** + * Replace the entire user list with another list. + * + * @param array $userList Associative array of User objects keyed by userID. + * + * @throws LazyJsonMapperException + * + * @return $this + */ + public function setUserList( + array $userList) + { + // First of all, let's instantly save their new value to our own + // internal cache, since our cache supports User objects and doesn't + // have to do any special transformations... + $this->_userList = $userList; + + // Now sync our internal cache with the LazyJsonMapper object...! :-) + $this->syncUserList(); // Throws. + + // Setters should always return "$this" to make them chainable! + return $this; + } +} + +/* + * Alright, let's try the example again, with the new container that has proper + * support for setters! + */ +echo "\n\nUnpredictable data #2, with setter:\n"; +$unpredictable2 = new UnpredictableContainerWithSetter(json_decode($unpredictable_json2, true)); + +$unpredictable2->printJson(); + +foreach ($unpredictable2->getUserList() as $userId => $userInfo) { + printf("User ID: %s\n Name: %s\n", $userId, $userInfo->getName()); +} + +/* + * Now let's try manipulating the data again! + */ +printf("*** Changing the name of user #%s to 'FOO'.\n", $userId); +$userInfo->setName('FOO'); + +/* + * Now let's look at the contents of the "getUserList()" call... + * + * Just as before, it has been updated since we use an internal cache which + * already refers to the exact same object instances... so our update is there! + */ +foreach ($unpredictable2->getUserList() as $userId => $userInfo) { + printf("User ID: %s\n Name: %s\n", $userId, $userInfo->getName()); +} + +/* + * Now let's look at the internal LazyJsonMapper data storage... + * + * It is NOT updated. As expected... + */ +$unpredictable2->printJson(); + +/* + * Now let's SYNC our cache back to the internal LazyJsonMapper data storage! + * + * And voila...! + */ +$unpredictable2->syncUserList(); +$unpredictable2->printJson(); + +/* + * Let's also try our custom setter, which takes a whole array of User objects + * and does the syncing automatically! + */ +$users = $unpredictable2->getUserList(); +foreach ($users as $userId => $userInfo) { + $userInfo->setName('Updated...'.$userId.'!'); +} +$unpredictable2->setUserList($users); // Replaces entire cache and syncs! + +/* + * Now let's look at the contents of our cache AND the LazyJsonMapper data! + * + * Everything has been updated, since our "setUserList()" call replaced the + * entire cache contents AND synced the new cache contents to the core storage. + */ +foreach ($unpredictable2->getUserList() as $userId => $userInfo) { + printf("User ID: %s\n Name: %s\n", $userId, $userInfo->getName()); +} +$unpredictable2->printJson(); + +/** + * This class handles a mixture of knowable and unknowable data. + * + * Let's end this with one more example... What if you DON'T want to define a + * whole custom container? What if you only want a SPECIFIC value within your + * map to be handling unpredictable keys? You could achieve that as follows! + * + * This class will contain less comments, for brevity. You hopefully understand + * all of the major workflow concepts by now! We will only explain new concepts. + * And this class won't show any `syncEmployees()` function, since you should + * understand how to do that now if you want that feature. + */ +class UnpredictableMixtureContainer extends LazyJsonMapper +{ + protected $_employees; + + const JSON_PROPERTY_MAP = [ + // This is a statically named "manager" property, which is a User. + 'manager' => 'User', + // This is a statically named "employees" property, which consists of + // unpredictable key-value pairs, keyed by userID. + 'employees' => 'mixed', // Must be 'mixed' (aka '') to allow sub-arrays. + ]; + + /** + * Get the list of employees. + * + * NOTE: This overrides the normal, automatic LazyJsonMapper getter! By + * naming our function identically, we override its behavior! + * + * @throws LazyJsonMapperException + * + * @return array + */ + public function getEmployees() + { + if ($this->_employees === null) { + // Use the internal _getProperty() API to read the actual property. + // NOTE: This function only throws if "employees" contains any + // invalid non-basic data. Only basic PHP types are accepted. + $this->_employees = $this->_getProperty('employees'); // Throws. + } + + foreach ($this->_employees as &$value) { + if (is_array($value)) { + $value = new User($value); // Throws. + } + } + + return $this->_employees; + } + + /** + * Set the list of employees. + * + * NOTE: This overrides the normal, automatic LazyJsonMapper setter! By + * naming our function identically, we override its behavior! + * + * @param array $employees + * + * @throws LazyJsonMapperException + * + * @return $this + */ + public function setEmployees( + array $employees) + { + $this->_employees = $employees; + + // We now need to construct a new, inner value for the property. Since + // it's a "mixed" property, it only accepts basic PHP types. + $newInnerValue = []; + foreach ($this->_employees as $k => $v) { + $newInnerValue[$k] = is_object($v) && $v instanceof LazyJsonMapper + ? $v->asArray() // Throws. + : $v; // Is already a valid value. + } + + // Now use the internal _setProperty() API to set the new inner value. + // NOTE: This function only throws if the new value contains any + // invalid non-basic data. Only basic PHP types are accepted. + $this->_setProperty('employees', $newInnerValue); + + return $this; + } +} + +/* + * Let's try it out! + */ +$unpredictable_json3 = <<printJson(); +printf("The manager's name is: %s\n", $unpredictable3->getManager()->getName()); +foreach ($unpredictable3->getEmployees() as $employeeId => $employeeInfo) { + printf("- Employee #%s, Name: %s\n", $employeeId, $employeeInfo->getName()); +} + +/* + * Now let's set some new employee data... This time, just for fun, let's give + * it a virtual JSON array which we construct manually. + */ +$unpredictable3->setEmployees([ + // Let's provide one value as a User object, since our setter supports + // the conversion of User objects back into plain arrays. + '10' => new User(['name' => 'Employee Ten']), + // However, we could also just provide the data as an array, since that's + // the goal that our setter performs... this is for really advanced users. + // Don't blame us if you break things by using this shortcut! ;-) + '11' => ['name' => 'Employee Eleven'], +]); + +/* + * Let's also update the manager, which is done via a normal, automatic + * LazyJsonMapper setter, since that property is completely defined. And since + * the "manager" object is owned by the LazyJsonMapper core, we're able to chain + * its getters and setters to select the manager's User object and set its name! + */ +$unpredictable3->getManager()->setName('New Manager!'); + +/* + * Now let's look at all current data again! + */ +$unpredictable3->printJson(); +printf("The manager's name is: %s\n", $unpredictable3->getManager()->getName()); +foreach ($unpredictable3->getEmployees() as $employeeId => $employeeInfo) { + printf("- Employee #%s, Name: %s\n", $employeeId, $employeeInfo->getName()); +} + +/* + * But wait... there's yet another way to solve this! + * + * Since we know that our "mixture container"'s `employees` value is a key-value + * storage of User objects, in other words it's "an unpredictable key-value + * container", then we CAN just tell LazyJsonMapper to map that property to a + * `UnpredictableContainer[WithSetter]` which we defined earlier. That way, the + * "employees" values are handled automatically by a neat sub-container. + * + * In fact, we could do something even better! We could define a basic + * "unpredictable keys" container, and then define subclasses of it for various + * types of unpredictable containers. Let's do that instead! + */ + +/** + * This class defines a core "untyped" container of unpredictable data-keys. + * + * Unpredictable data is data with keys that cannot be known ahead of time, such + * as objects whose values are keyed by things like user IDs. + * + * Here's an example of such unpredictable data: `{"9323":{"name":"foo"}}` + * + * The `getData()` function retrieves all key-value pairs, converted to the + * optional `$_type` (if one is set via a subclass). And `setData()` writes + * the new data back into the core `LazyJsonMapper` container. Most people will + * not need to use the setter. It's just provided as an extra feature. + * + * @author SteveJobzniak (https://github.com/SteveJobzniak) + */ +class CoreUnpredictableContainer extends LazyJsonMapper +{ + // Let's disable direct access to this container via anything other than + // the functions that WE define ourselves! That way, people cannot use + // virtual properties/functions to manipulate the core data storage. + const ALLOW_VIRTUAL_PROPERTIES = false; + const ALLOW_VIRTUAL_FUNCTIONS = false; + + /** + * Data cache to avoid constant processing every time the getter is used. + * + * @var array + */ + protected $_cache; + + /** + * What class-type to convert all sub-object values into. + * + * Defaults to no conversion. Override this value via a subclass! + * + * Always use the FULL path to the target class, with a leading backslash! + * The leading backslash ensures that it's found via a strict, global path. + * + * Example: `\Foo\BarClass`. + * + * @var string + */ + protected $_type; + + /** + * Get the data array of this unpredictable container. + * + * @throws LazyJsonMapperException + * + * @return array + */ + public function getData() + { + if ($this->_cache === null) { + $this->_cache = $this->asArray(); // Throws. + } + + if ($this->_type !== null) { + foreach ($this->_cache as &$value) { + if (is_array($value)) { + $value = new $this->_type($value); // Throws. + } + } + } + + return $this->_cache; + } + + /** + * Set the data array of this unpredictable container. + * + * @param array $value The new data array. + * + * @throws LazyJsonMapperException + * + * @return $this + */ + public function setData( + array $value) + { + $this->_cache = $value; + + $newObjectData = []; + foreach ($this->_cache as $k => $v) { + $newObjectData[$k] = is_object($v) && $v instanceof LazyJsonMapper + ? $v->asArray() // Throws. + : $v; // Is already a valid value. + } + + $this->assignObjectData($newObjectData); // Throws. + + return $this; + } +} + +/** + * This class defines an "unpredictable container of User objects". + * + * It's very easy to define other containers. Simply create them like this and + * override their `$_type` property to any other valid LazyJsonMapper class. + */ +class UserUnpredictableContainer extends CoreUnpredictableContainer +{ + // The FULL path to the target class, with leading backslash! + // NOTE: The leading backslash ensures it's found via a strict, global path. + protected $_type = '\User'; +} + +/** + * This is our new and improved, final class! + * + * Here is our final object for mapping our unpredictable "employees" data... + * As you can see, it is much easier to create this class now that we have + * defined a core, re-usable "unpredictable container" above. + */ +class UnpredictableMixtureContainerTwo extends LazyJsonMapper +{ + const JSON_PROPERTY_MAP = [ + // This holds a regular User object. + 'manager' => 'User', + // This property is an unpredictable container of User objets. + 'employees' => 'UserUnpredictableContainer', + ]; +} + +/* + * Let's try it out! + */ +$unpredictable_json4 = <<printJson(); +printf("The manager's name is: %s\n", $unpredictable4->getManager()->getName()); +foreach ($unpredictable4->getEmployees()->getData() as $employeeId => $employeeInfo) { + printf("- Employee #%s, Name: %s\n", $employeeId, $employeeInfo->getName()); +} + +/* + * And let's update the value of the inner, unpredictable container! + * + * The container itself takes care of updating the LazyJsonMapper data storage! + */ +$unpredictable4->getEmployees()->setData([ + '123' => ['name' => 'Final Employee 123'], + '456' => ['name' => 'Final Employee 456'], +]); + +/* + * Now finish by looking at all of the data again, via the LazyJsonMapper core + * object and via the `getEmployees()` object's cache... They are identical! + */ +$unpredictable4->printJson(); +printf("The manager's name is: %s\n", $unpredictable4->getManager()->getName()); +foreach ($unpredictable4->getEmployees()->getData() as $employeeId => $employeeInfo) { + printf("- Employee #%s, Name: %s\n", $employeeId, $employeeInfo->getName()); +} + +/* + * And that's it! Hopefully you NEVER have to work with nasty, unpredictable + * data like this. If you're able to control the JSON format, you should always + * design it properly with known keys instead. But at least you now know about + * multiple great methods for working with objects that have unpredictable keys! + * + * There are other methods too, such as if your class contains a blend of known + * (defined) keys and unpredictable keys, in which case you'd need to fetch via + * `asArray()` as in the `UnpredictableContainer` example, and then you'd simply + * filter out all known keys from your cache, to get _just_ the unpredictable + * keys. And if you need setters in that scenario, your setter functions would + * be a bit more complex since they would need to use `assignObjectData()` AND + * would have to provide BOTH the known data AND the unknown data. One way of + * doing that would be to use `_getProperty()` to merge in the CURRENT values of + * each core property into your NEW object-data array BEFORE you assign it. But + * I leave that extremely rare scenario as an excercise for you, dear reader! + * + * You should go out and adapt all of this code to fit your own needs! ;-) + * + * For example, if you don't need the unpredictable values to be converted + * to/from a specific object type, then simply skip the conversion code. + * + * If you don't need caching for performance, then skip the caching code. + * + * If you don't need setters/syncing, then skip all of that code. + * + * You may also want to disable the user-options `ALLOW_VIRTUAL_PROPERTIES` and + * `ALLOW_VIRTUAL_FUNCTIONS` on your unpredictable containers, so users cannot + * manipulate the unpredictable data via LazyJsonMapper's automatic functions! + * That's what we did for CoreUnpredictableContainer, to ensure that nobody can + * destroy its internal data by touching it directly. They can only manipulate + * the data via its safe, public `getData()` and `setData()` functions! + * + * And you may perhaps prefer to write a custom base-class which has a few other + * helper-functions for doing these kinds of data translations, caching and + * syncing, to make your own work easier (such as the CoreUnpredictableContainer + * example above). That way, your various sub-classes could just call your + * internal helper functions to do the required processing automatically! :-) + * + * The possibilities are endless! Have fun! + */ +echo "\n\nHave fun!\n"; diff --git a/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/phpdoc.dist.xml b/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/phpdoc.dist.xml new file mode 100755 index 0000000..acd326c --- /dev/null +++ b/instafeed/vendor/lazyjsonmapper/lazyjsonmapper/phpdoc.dist.xml @@ -0,0 +1,24 @@ + + + LazyJsonMapper + + docs/cache + utf8 + + TODO + FIXME + + + php + + + + docs/output + + +