File manager - Edit - /home/linknsbh/sabel-eltaqwa.com/assets/lfm/files/shares/events/thumbs/jeroendesloovere.tar
Back
vcard/CHANGELOG.md 0000644 00000003032 15213363772 0007463 0 ustar 00 1.2.2 (2015-11-30) -- Improvements: * Probably a fix for UTF-8 in outlook 2010 1.2.1 (2015-07-23) -- Improvements: * You can now set the charset by using $vcard->setCharset('ISO-8859-1'); 1.2.0 (2015-06-01) -- Improvements: * You can now add some properties multiple times: email, address, phone number and url 1.1.11 (2015-05-21) -- Improvements: * addMedia updated to check if correct file type. 1.1.10 (2015-05-20) -- Improvements: * Multiple mailaddresses allowed. * Chaining integrated to add functions. 1.1.9 (2015-04-21) -- Improvements: * getHeaders() is now separate function. So frameworks can use this. * Fix for iOS 8 to return vcard without calendar wrapper. 1.1.8 (2015-03-09) -- Bugfixes: * Fixes $include/$exclude, #27 * Fixes special characters by using external transliterator class. 1.1.7 (2015-03-05) -- Improvements: * Images should per default be included in our vcard. Bugfixes: * Fix for the ->get() which didn't return anything. 1.1.6 (2015-02-24) -- Improvements: * Add line folding, check #16 * Refactored some functions. * PSR-2-code-styling applied. * PHPCS applied. Bugfixes: * Fix fetching PHOTO elements. 1.1.5 (2015-01-30) -- Bugfixes: * Updated the deprecated MIME detection, check #16 1.1.4 (2015-01-22) -- Improvements: * PHPUnit Tests added * Exception is now a separate class. * Renamed the variables $firstName and $lastName Bugfixes: * Filename: Fixed double underscores when no "additional" field was given. 1.1.3 (2015-01-22) -- Bugfixes: * Name: Double space when no "additional" field is given. Fixes #8 vcard/README.md 0000644 00000011071 15213363772 0007133 0 ustar 00 # VCard PHP library [](https://packagist.org/packages/jeroendesloovere/vcard) [](https://github.com/jeroendesloovere/vcard/blob/master/LICENSE) [](https://travis-ci.org/jeroendesloovere/vcard) [](https://scrutinizer-ci.com/g/jeroendesloovere/vcard/?branch=master) This VCard PHP library can generate a vCard with some data. When using an iOS device < iOS 8 it will export as a .ics file because iOS devices don't support the default .vcf files. **NOTE**: We are working on a complete new version to work with vCard version 4.0, with extreme good code quality. [Check out the new version](https://github.com/jeroendesloovere/vcard/tree/2.0.0-dev) ## Usage ### Installation ```bash composer require jeroendesloovere/vcard ``` > This will install the latest version of vcard with [Composer](https://getcomposer.org) ### Example ``` php use JeroenDesloovere\VCard\VCard; // define vcard $vcard = new VCard(); // define variables $lastname = 'Desloovere'; $firstname = 'Jeroen'; $additional = ''; $prefix = ''; $suffix = ''; // add personal data $vcard->addName($lastname, $firstname, $additional, $prefix, $suffix); // add work data $vcard->addCompany('Siesqo'); $vcard->addJobtitle('Web Developer'); $vcard->addRole('Data Protection Officer'); $vcard->addEmail('info@jeroendesloovere.be'); $vcard->addPhoneNumber(1234121212, 'PREF;WORK'); $vcard->addPhoneNumber(123456789, 'WORK'); $vcard->addAddress(null, null, 'street', 'worktown', null, 'workpostcode', 'Belgium'); $vcard->addLabel('street, worktown, workpostcode Belgium'); $vcard->addURL('http://www.jeroendesloovere.be'); $vcard->addPhoto(__DIR__ . '/landscape.jpeg'); // return vcard as a string //return $vcard->getOutput(); // return vcard as a download return $vcard->download(); // save vcard on disk //$vcard->setSavePath('/path/to/directory'); //$vcard->save(); ``` > [View all examples](/examples/example.php) or check [the VCard class](/src/VCard.php). ### Parsing examples The parser can either get passed a VCard string, like so: ```php // load VCardParser classes use JeroenDesloovere\VCard\VCardParser; $parser = new VCardParser($vcardString); echo $parser->getCardAtIndex(0)->fullname; // Prints the full name. ``` Or by using a factory method with a file name: ```php $parser = VCardParser::parseFromFile('path/to/file.vcf'); echo $parser->getCardAtIndex(0)->fullname; // Prints the full name. ``` > [View the parsing example](/examples/example_parsing.php) or check the [the VCardParser class](/src/VCardParser.php) class. **Support for frameworks** I've created a Symfony Bundle: [VCard Bundle](https://github.com/jeroendesloovere/vcard-bundle) Usage in for example: Laravel ```php return Response::make( $this->vcard->getOutput(), 200, $this->vcard->getHeaders(true) ); ``` ## Tests ```bash vendor/bin/phpunit tests ``` ## Documentation The class is well documented inline. If you use a decent IDE you'll see that each method is documented with PHPDoc. ## Contributing Contributions are **welcome** and will be fully **credited**. ### Pull Requests > To add or update code - **Coding Syntax** - Please keep the code syntax consistent with the rest of the package. - **Add unit tests!** - Your patch won't be accepted if it doesn't have tests. - **Document any change in behavior** - Make sure the README and any other relevant documentation are kept up-to-date. - **Consider our release cycle** - We try to follow [semver](http://semver.org/). Randomly breaking public APIs is not an option. - **Create topic branches** - Don't ask us to pull from your master branch. - **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. - **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please squash them before submitting. ### Issues > For bug reporting or code discussions. More info on how to work with GitHub on help.github.com. ## Credits - [Jeroen Desloovere](https://github.com/jeroendesloovere) - [All Contributors](https://github.com/jeroendesloovere/vcard/contributors) ## License The module is licensed under [MIT](./LICENSE.md). In short, this license allows you to do everything as long as the copyright statement stays present. vcard/LICENSE 0000644 00000002074 15213363772 0006664 0 ustar 00 The MIT License (MIT) Copyright (c) 2014 Jeroen Desloovere 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. vcard/tests/empty.jpg 0000644 00000000000 15213363772 0010644 0 ustar 00 vcard/tests/image.jpg 0000644 00000001033 15213363772 0010577 0 ustar 00 ��� JFIF H H �� Created with GIMP�� C �� C �� �� �� �� T��� �� �� �� ?�� �� ?�� �� ?�� �� ?!�� ��� �� ?�� �� ?�� �� ?�� vcard/tests/VCardParserTest.php 0000644 00000026043 15213363772 0012550 0 ustar 00 <?php namespace JeroenDesloovere\VCard\tests; use JeroenDesloovere\VCard\VCard; use JeroenDesloovere\VCard\VCardParser; use OutOfBoundsException; use PHPUnit\Framework\TestCase; /** * Unit tests for our VCard parser. */ final class VCardParserTest extends TestCase { public function testOutOfRangeException() { $this->expectException(OutOfBoundsException::class); $parser = new VCardParser(''); $parser->getCardAtIndex(2); } public function testSimpleVcard() { $vcard = new VCard(); $vcard->addName("Desloovere", "Jeroen"); $parser = new VCardParser($vcard->buildVCard()); $this->assertEquals($parser->getCardAtIndex(0)->firstname, "Jeroen"); $this->assertEquals($parser->getCardAtIndex(0)->lastname, "Desloovere"); $this->assertEquals($parser->getCardAtIndex(0)->fullname, "Jeroen Desloovere"); } public function testBDay() { $vcard = new VCard(); $vcard->addBirthday('31-12-2015'); $parser = new VCardParser($vcard->buildVCard()); $this->assertEquals($parser->getCardAtIndex(0)->birthday->format('Y-m-d'), '2015-12-31'); } public function testAddress() { $vcard = new VCard(); $vcard->addAddress( "Lorem Corp.", "(extended info)", "54th Ipsum Street", "PHPsville", "Guacamole", "01158", "Gitland", 'WORK;POSTAL' ); $vcard->addAddress( "Jeroen Desloovere", "(extended info, again)", "25th Some Address", "Townsville", "Area 51", "045784", "Europe (is a country, right?)", 'WORK;PERSONAL' ); $vcard->addAddress( "Georges Desloovere", "(extended info, again, again)", "26th Some Address", "Townsville-South", "Area 51B", "04554", "Europe (no, it isn't)", 'WORK;PERSONAL' ); $parser = new VCardParser($vcard->buildVCard()); $this->assertEquals($parser->getCardAtIndex(0)->address['WORK;POSTAL'][0], (object) array( 'name' => "Lorem Corp.", 'extended' => "(extended info)", 'street' => "54th Ipsum Street", 'city' => "PHPsville", 'region' => "Guacamole", 'zip' => "01158", 'country' => "Gitland", )); $this->assertEquals($parser->getCardAtIndex(0)->address['WORK;PERSONAL'][0], (object) array( 'name' => "Jeroen Desloovere", 'extended' => "(extended info, again)", 'street' => "25th Some Address", 'city' => "Townsville", 'region' => "Area 51", 'zip' => "045784", 'country' => "Europe (is a country, right?)", )); $this->assertEquals($parser->getCardAtIndex(0)->address['WORK;PERSONAL'][1], (object) array( 'name' => "Georges Desloovere", 'extended' => "(extended info, again, again)", 'street' => "26th Some Address", 'city' => "Townsville-South", 'region' => "Area 51B", 'zip' => "04554", 'country' => "Europe (no, it isn't)", )); } public function testPhone() { $vcard = new VCard(); $vcard->addPhoneNumber('0984456123'); $vcard->addPhoneNumber('2015123487', 'WORK'); $vcard->addPhoneNumber('4875446578', 'WORK'); $vcard->addPhoneNumber('9875445464', 'PREF;WORK;VOICE'); $parser = new VCardParser($vcard->buildVCard()); $this->assertEquals($parser->getCardAtIndex(0)->phone['default'][0], '0984456123'); $this->assertEquals($parser->getCardAtIndex(0)->phone['WORK'][0], '2015123487'); $this->assertEquals($parser->getCardAtIndex(0)->phone['WORK'][1], '4875446578'); $this->assertEquals($parser->getCardAtIndex(0)->phone['PREF;WORK;VOICE'][0], '9875445464'); } public function testEmail() { $vcard = new VCard(); $vcard->addEmail('some@email.com'); $vcard->addEmail('site@corp.net', 'WORK'); $vcard->addEmail('site.corp@corp.net', 'WORK'); $vcard->addEmail('support@info.info', 'PREF;WORK'); $parser = new VCardParser($vcard->buildVCard()); // The VCard class uses a default type of "INTERNET", so we do not test // against the "default" key. $this->assertEquals($parser->getCardAtIndex(0)->email['INTERNET'][0], 'some@email.com'); $this->assertEquals($parser->getCardAtIndex(0)->email['INTERNET;WORK'][0], 'site@corp.net'); $this->assertEquals($parser->getCardAtIndex(0)->email['INTERNET;WORK'][1], 'site.corp@corp.net'); $this->assertEquals($parser->getCardAtIndex(0)->email['INTERNET;PREF;WORK'][0], 'support@info.info'); } public function testOrganization() { $vcard = new VCard(); $vcard->addCompany('Lorem Corp.'); $parser = new VCardParser($vcard->buildVCard()); $this->assertEquals($parser->getCardAtIndex(0)->organization, 'Lorem Corp.'); } public function testUrl() { $vcard = new VCard(); $vcard->addUrl('http://www.jeroendesloovere.be'); $vcard->addUrl('http://home.example.com', 'HOME'); $vcard->addUrl('http://work1.example.com', 'PREF;WORK'); $vcard->addUrl('http://work2.example.com', 'PREF;WORK'); $parser = new VCardParser($vcard->buildVCard()); $this->assertEquals($parser->getCardAtIndex(0)->url['default'][0], 'http://www.jeroendesloovere.be'); $this->assertEquals($parser->getCardAtIndex(0)->url['HOME'][0], 'http://home.example.com'); $this->assertEquals($parser->getCardAtIndex(0)->url['PREF;WORK'][0], 'http://work1.example.com'); $this->assertEquals($parser->getCardAtIndex(0)->url['PREF;WORK'][1], 'http://work2.example.com'); } public function testNote() { $vcard = new VCard(); $vcard->addNote('This is a testnote'); $parser = new VCardParser($vcard->buildVCard()); $vcardMultiline = new VCard(); $vcardMultiline->addNote("This is a multiline note\nNew line content!\r\nLine 2"); $parserMultiline = new VCardParser($vcardMultiline->buildVCard()); $this->assertEquals($parser->getCardAtIndex(0)->note, 'This is a testnote'); $this->assertEquals(nl2br($parserMultiline->getCardAtIndex(0)->note), nl2br("This is a multiline note" . PHP_EOL . "New line content!" . PHP_EOL . "Line 2")); } public function testCategories() { $vcard = new VCard(); $vcard->addCategories([ 'Category 1', 'cat-2', 'another long category!' ]); $parser = new VCardParser($vcard->buildVCard()); $this->assertEquals($parser->getCardAtIndex(0)->categories[0], 'Category 1'); $this->assertEquals($parser->getCardAtIndex(0)->categories[1], 'cat-2'); $this->assertEquals($parser->getCardAtIndex(0)->categories[2], 'another long category!'); } public function testTitle() { $vcard = new VCard(); $vcard->addJobtitle('Ninja'); $parser = new VCardParser($vcard->buildVCard()); $this->assertEquals($parser->getCardAtIndex(0)->title, 'Ninja'); } public function testLogo() { $image = __DIR__ . '/image.jpg'; $imageUrl = 'https://raw.githubusercontent.com/jeroendesloovere/vcard/master/tests/image.jpg'; $vcard = new VCard(); $vcard->addLogo($image, true); $parser = new VCardParser($vcard->buildVCard()); $this->assertEquals($parser->getCardAtIndex(0)->rawLogo, file_get_contents($image)); $vcard = new VCard(); $vcard->addLogo($image, false); $parser = new VCardParser($vcard->buildVCard()); $this->assertEquals($parser->getCardAtIndex(0)->logo, __DIR__ . '/image.jpg'); $vcard = new VCard(); $vcard->addLogo($imageUrl, false); $parser = new VCardParser($vcard->buildVCard()); $this->assertEquals($parser->getCardAtIndex(0)->logo, $imageUrl); } public function testPhoto() { $image = __DIR__ . '/image.jpg'; $imageUrl = 'https://raw.githubusercontent.com/jeroendesloovere/vcard/master/tests/image.jpg'; $vcard = new VCard(); $vcard->addPhoto($image, true); $parser = new VCardParser($vcard->buildVCard()); $this->assertEquals($parser->getCardAtIndex(0)->rawPhoto, file_get_contents($image)); $vcard = new VCard(); $vcard->addPhoto($image, false); $parser = new VCardParser($vcard->buildVCard()); $this->assertEquals($parser->getCardAtIndex(0)->photo, __DIR__ . '/image.jpg'); $vcard = new VCard(); $vcard->addPhoto($imageUrl, false); $parser = new VCardParser($vcard->buildVCard()); $this->assertEquals($parser->getCardAtIndex(0)->photo, $imageUrl); } public function testVcardDB() { $db = ''; $vcard = new VCard(); $vcard->addName("Desloovere", "Jeroen"); $db .= $vcard->buildVCard(); $vcard = new VCard(); $vcard->addName("Lorem", "Ipsum"); $db .= $vcard->buildVCard(); $parser = new VCardParser($db); $this->assertEquals($parser->getCardAtIndex(0)->fullname, "Jeroen Desloovere"); $this->assertEquals($parser->getCardAtIndex(1)->fullname, "Ipsum Lorem"); } public function testIteration() { // Prepare a VCard DB. $db = ''; $vcard = new VCard(); $vcard->addName("Desloovere", "Jeroen"); $db .= $vcard->buildVCard(); $vcard = new VCard(); $vcard->addName("Lorem", "Ipsum"); $db .= $vcard->buildVCard(); $parser = new VCardParser($db); foreach ($parser as $i => $card) { $this->assertEquals($card->fullname, $i == 0 ? "Jeroen Desloovere" : "Ipsum Lorem"); } } public function testFromFile() { $parser = VCardParser::parseFromFile(__DIR__ . '/example.vcf'); // Use this opportunity to test fetching all cards directly. $cards = $parser->getCards(); $this->assertEquals($cards[0]->firstname, "Jeroen"); $this->assertEquals($cards[0]->lastname, "Desloovere"); $this->assertEquals($cards[0]->fullname, "Jeroen Desloovere"); // Check the parsing of grouped items as well, which are present in the // example file. $this->assertEquals($cards[0]->url['default'][0], 'http://www.jeroendesloovere.be'); $this->assertEquals($cards[0]->email['INTERNET'][0], 'site@example.com'); } public function testFileNotFound() { $this->expectException(\RuntimeException::class); $parser = VCardParser::parseFromFile(__DIR__ . '/does-not-exist.vcf'); } public function testLabel() { $label = 'street, worktown, workpostcode Belgium'; $vcard = new VCard(); $vcard->addLabel($label, 'work'); $parser = new VCardParser($vcard->buildVCard()); $this->assertEquals($parser->getCardAtIndex(0)->label, $label); } } vcard/tests/VCardTest.php 0000644 00000032622 15213363772 0011373 0 ustar 00 <?php namespace JeroenDesloovere\VCard\tests; // required to load require_once __DIR__ . '/../vendor/autoload.php'; /* * This file is part of the VCard PHP Class from Jeroen Desloovere. * * For the full copyright and license information, please view the license * file that was distributed with this source code. */ use Exception; use JeroenDesloovere\VCard\VCard; use PHPUnit\Framework\TestCase; /** * This class will test our VCard PHP Class which can generate VCards. */ class VCardTest extends TestCase { /** * @var VCard */ protected $vcard = null; /** * Data provider for testEmail() * * @return array */ public function emailDataProvider() { return [ [['john@doe.com']], [['john@doe.com', 'WORK' => 'john@work.com']], [['WORK' => 'john@work.com', 'HOME' => 'john@home.com']], [['PREF;WORK' => 'john@work.com', 'HOME' => 'john@home.com']], ]; } /** * Set up before class * * @return void */ protected function setUp(): void { // set timezone date_default_timezone_set('Europe/Brussels'); $this->vcard = new VCard(); $this->firstName = 'Jeroen'; $this->lastName = 'Desloovere'; $this->additional = '&'; $this->prefix = 'Mister'; $this->suffix = 'Junior'; $this->emailAddress1 = ''; $this->emailAddress2 = ''; $this->firstName2 = 'Ali'; $this->lastName2 = 'ÖZSÜT'; $this->firstName3 = 'Garçon'; $this->lastName3 = 'Jéroèn'; } /** * Tear down after class */ protected function tearDown(): void { $this->vcard = null; } public function testAddAddress() { $this->assertEquals($this->vcard, $this->vcard->addAddress( '', '88th Floor', '555 East Flours Street', 'Los Angeles', 'CA', '55555', 'USA' )); $this->assertStringContainsString('ADR;WORK;POSTAL;CHARSET=utf-8:;88th Floor;555 East Flours Street;Los Angele', $this->vcard->getOutput()); // Should fold on row 75, so we should not see the full address. $this->assertStringNotContainsString('ADR;WORK;POSTAL;CHARSET=utf-8:;88th Floor;555 East Flours Street;Los Angeles;CA;55555;', $this->vcard->getOutput()); } public function testAddBirthday() { $this->assertEquals($this->vcard, $this->vcard->addBirthday('')); } public function testAddCompany() { $this->assertEquals($this->vcard, $this->vcard->addCompany('')); } public function testAddCategories() { $this->assertEquals($this->vcard, $this->vcard->addCategories([])); } public function testAddEmail() { $this->assertEquals($this->vcard, $this->vcard->addEmail($this->emailAddress1)); $this->assertEquals($this->vcard, $this->vcard->addEmail($this->emailAddress2)); $this->assertEquals(2, count($this->vcard->getProperties())); } public function testAddJobTitle() { $this->assertEquals($this->vcard, $this->vcard->addJobtitle('')); } public function testAddRole() { $this->assertEquals($this->vcard, $this->vcard->addRole('')); } public function testAddName() { $this->assertEquals($this->vcard, $this->vcard->addName('')); } public function testAddNote() { $this->assertEquals($this->vcard, $this->vcard->addNote('')); } public function testAddPhoneNumber() { $this->assertEquals($this->vcard, $this->vcard->addPhoneNumber('')); $this->assertEquals($this->vcard, $this->vcard->addPhoneNumber('')); $this->assertCount(2, $this->vcard->getProperties()); } public function testAddPhotoWithJpgPhoto() { $return = $this->vcard->addPhoto(__DIR__ . '/image.jpg', true); $this->assertEquals($this->vcard, $return); } public function testAddPhotoWithRemoteJpgPhoto() { $return = $this->vcard->addPhoto( 'https://raw.githubusercontent.com/jeroendesloovere/vcard/master/tests/image.jpg', true ); $this->assertEquals($this->vcard, $return); } /** * Test adding remote empty photo */ public function testAddPhotoWithRemoteEmptyJpgPhoto() { $this->expectException(Exception::class); $this->expectExceptionMessage('Returned data is not an image.'); $this->vcard->addPhoto( 'https://raw.githubusercontent.com/jeroendesloovere/vcard/master/tests/empty.jpg', true ); } public function testAddPhotoContentWithJpgPhoto() { $return = $this->vcard->addPhotoContent(file_get_contents(__DIR__ . '/image.jpg')); $this->assertEquals($this->vcard, $return); } /** * Test adding empty photo */ public function testAddPhotoContentWithEmptyContent() { $this->expectException(Exception::class); $this->expectExceptionMessage('Returned data is not an image.'); $this->vcard->addPhotoContent(''); } public function testAddLogoWithJpgImage() { $return = $this->vcard->addLogo(__DIR__ . '/image.jpg', true); $this->assertEquals($this->vcard, $return); } public function testAddLogoWithJpgImageNoInclude() { $return = $this->vcard->addLogo(__DIR__ . '/image.jpg', false); $this->assertEquals($this->vcard, $return); } public function testAddLogoContentWithJpgImage() { $return = $this->vcard->addLogoContent(file_get_contents(__DIR__ . '/image.jpg')); $this->assertEquals($this->vcard, $return); } /** * Test adding empty photo */ public function testAddLogoContentWithEmptyContent() { $this->expectException(Exception::class); $this->expectExceptionMessage('Returned data is not an image.'); $this->vcard->addLogoContent(''); } public function testAddUrl() { $this->assertEquals($this->vcard, $this->vcard->addUrl('1')); $this->assertEquals($this->vcard, $this->vcard->addUrl('2')); $this->assertCount(2, $this->vcard->getProperties()); } /** * Test adding local photo using an empty file */ public function testAddPhotoWithEmptyFile() { $this->expectException(Exception::class); $this->expectExceptionMessage('Returned data is not an image.'); $this->vcard->addPhoto(__DIR__ . '/emptyfile', true); } /** * Test adding logo with no value */ public function testAddLogoWithNoValue() { $this->expectException(Exception::class); $this->expectExceptionMessage('Returned data is not an image.'); $this->vcard->addLogo(__DIR__ . '/emptyfile', true); } /** * Test adding photo with no photo */ public function testAddPhotoWithNoPhoto() { $this->expectException(Exception::class); $this->expectExceptionMessage('Returned data is not an image.'); $this->vcard->addPhoto(__DIR__ . '/wrongfile', true); } /** * Test adding logo with no image */ public function testAddLogoWithNoImage() { $this->expectException(Exception::class); $this->expectExceptionMessage('Returned data is not an image.'); $this->vcard->addLogo(__DIR__ . '/wrongfile', true); } /** * Test charset */ public function testCharset() { $charset = 'ISO-8859-1'; $this->vcard->setCharset($charset); $this->assertEquals($charset, $this->vcard->getCharset()); } /** * Test Email * * @dataProvider emailDataProvider $emails */ public function testEmail($emails = []) { foreach ($emails as $key => $email) { if (is_string($key)) { $this->vcard->addEmail($email, $key); } else { $this->vcard->addEmail($email); } } foreach ($emails as $key => $email) { if (is_string($key)) { $this->assertStringContainsString('EMAIL;INTERNET;' . $key . ':' . $email, $this->vcard->getOutput()); } else { $this->assertStringContainsString('EMAIL;INTERNET:' . $email, $this->vcard->getOutput()); } } } /** * Test first name and last name */ public function testFirstNameAndLastName() { $this->vcard->addName( $this->lastName, $this->firstName ); $this->assertEquals('jeroen-desloovere', $this->vcard->getFilename()); } /** * Test full blown name */ public function testFullBlownName() { $this->vcard->addName( $this->lastName, $this->firstName, $this->additional, $this->prefix, $this->suffix ); $this->assertEquals('mister-jeroen-desloovere-junior', $this->vcard->getFilename()); } /** * Test multiple birthdays */ public function testMultipleBirthdays() { $this->expectException(\Exception::class); $this->assertEquals($this->vcard, $this->vcard->addBirthday('1')); $this->expectException(Exception::class); $this->assertEquals($this->vcard, $this->vcard->addBirthday('2')); } /** * Test multiple categories */ public function testMultipleCategories() { $this->expectException(\Exception::class); $this->assertEquals($this->vcard, $this->vcard->addCategories(['1'])); $this->expectException(Exception::class); $this->assertEquals($this->vcard, $this->vcard->addCategories(['2'])); } /** * Test multiple companies */ public function testMultipleCompanies() { $this->expectException(\Exception::class); $this->assertEquals($this->vcard, $this->vcard->addCompany('1')); $this->expectException(Exception::class); $this->assertEquals($this->vcard, $this->vcard->addCompany('2')); } /** * Test multiple job titles */ public function testMultipleJobtitles() { $this->expectException(\Exception::class); $this->assertEquals($this->vcard, $this->vcard->addJobtitle('1')); $this->expectException(Exception::class); $this->assertEquals($this->vcard, $this->vcard->addJobtitle('2')); } /** * Test multiple roles */ public function testMultipleRoles() { $this->expectException(\Exception::class); $this->assertEquals($this->vcard, $this->vcard->addRole('1')); $this->expectException(Exception::class); $this->assertEquals($this->vcard, $this->vcard->addRole('2')); } /** * Test multiple names */ public function testMultipleNames() { $this->expectException(\Exception::class); $this->assertEquals($this->vcard, $this->vcard->addName('1')); $this->expectException(Exception::class); $this->assertEquals($this->vcard, $this->vcard->addName('2')); } /** * Test multiple notes */ public function testMultipleNotes() { $this->expectException(\Exception::class); $this->assertEquals($this->vcard, $this->vcard->addNote('1')); $this->expectException(Exception::class); $this->assertEquals($this->vcard, $this->vcard->addNote('2')); } /** * Test special first name and last name */ public function testSpecialFirstNameAndLastName() { $this->vcard->addName( $this->lastName2, $this->firstName2 ); $this->assertEquals('ali-ozsut', $this->vcard->getFilename()); } /** * Test special first name and last name */ public function testSpecialFirstNameAndLastName2() { $this->vcard->addName( $this->lastName3, $this->firstName3 ); $this->assertEquals('garcon-jeroen', $this->vcard->getFilename()); } /** * Test multiple labels */ public function testMultipleLabels() { $this->assertSame($this->vcard, $this->vcard->addLabel('My label')); $this->assertSame($this->vcard, $this->vcard->addLabel('My work label', 'WORK')); $this->assertSame(2, count($this->vcard->getProperties())); $this->assertStringContainsString('LABEL;CHARSET=utf-8:My label', $this->vcard->getOutput()); $this->assertStringContainsString('LABEL;WORK;CHARSET=utf-8:My work label', $this->vcard->getOutput()); } public function testChunkSplitUnicode() { $class_handler = new \ReflectionClass('JeroenDesloovere\VCard\VCard'); $method_handler = $class_handler->getMethod('chunk_split_unicode'); $method_handler->setAccessible(true); $ascii_input="Lorem ipsum dolor sit amet,"; $ascii_output = $method_handler->invokeArgs(new VCard(), [$ascii_input,10,'|']); $unicode_input='Τη γλώσσα μου έδωσαν ελληνική το σπίτι φτωχικό στις αμμουδιές του Ομήρου.'; $unicode_output = $method_handler->invokeArgs(new VCard(), [$unicode_input,10,'|']); $this->assertEquals( "Lorem ipsu|m dolor si|t amet,|", $ascii_output); $this->assertEquals( "Τη γλώσσα |μου έδωσαν| ελληνική |το σπίτι φ|τωχικό στι|ς αμμουδιέ|ς του Ομήρ|ου.|", $unicode_output); } } vcard/tests/wrongfile 0000644 00000000007 15213363772 0010732 0 ustar 00 foobar vcard/tests/VCardExceptionTest.php 0000644 00000001053 15213363772 0013244 0 ustar 00 <?php namespace JeroenDesloovere\VCard; use PHPUnit\Framework\TestCase; /* * This file is part of the VCard PHP Class from Jeroen Desloovere. * * For the full copyright and license information, please view the license * file that was distributed with this source code. */ /** * VCard Exception Test. */ final class VCardExceptionTest extends TestCase { public function testException() { $this->expectException(\JeroenDesloovere\VCard\VCardException::class); throw new VCardException('Testing the VCard error.'); } } vcard/tests/example.vcf 0000644 00000000416 15213363772 0011152 0 ustar 00 BEGIN:VCARD VERSION:3.0 REV:2016-05-30T10:36:13Z N;CHARSET=utf-8:Desloovere;Jeroen;;; FN;CHARSET=utf-8:Jeroen Desloovere item1.EMAIL;type=INTERNET:site@example.com item1.X-ABLabel:$!<Email>!$ item2.URL:http://www.jeroendesloovere.be item2.X-ABLabel:$!<Home>!$ END:VCARD vcard/tests/emptyfile 0000644 00000000000 15213363773 0010726 0 ustar 00 vcard/phpunit.xml.dist 0000644 00000000750 15213363773 0011032 0 ustar 00 <?xml version="1.0" encoding="UTF-8"?> <phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" bootstrap="./vendor/autoload.php" colors="true" verbose="true" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"> <coverage> <include> <directory suffix=".php">src</directory> </include> </coverage> <testsuites> <testsuite name="JeroenDesloovere Test Suite"> <directory>tests</directory> </testsuite> </testsuites> </phpunit> vcard/.travis.yml 0000644 00000000426 15213363773 0007770 0 ustar 00 language: php dist: trusty php: - 7.3 - 8.0 - hhvm sudo: false before_script: - composer self-update - composer install --prefer-source --no-interaction script: - vendor/bin/phpunit --coverage-text matrix: allow_failures: - php: hhvm fast_finish: true vcard/composer.json 0000644 00000001650 15213363773 0010401 0 ustar 00 { "name": "jeroendesloovere/vcard", "type": "library", "description": "This VCard PHP class can generate a vCard with some data. When using an iOS device it will export as a .ics file because iOS devices don't support the default .vcf files.", "keywords": ["vcard", "generator", ".vcf", "php"], "homepage": "https://github.com/jeroendesloovere/vcard", "license": "MIT", "authors": [ { "name": "Jeroen Desloovere", "email": "info@jeroendesloovere.be", "homepage": "http://jeroendesloovere.be", "role": "Developer" } ], "require": { "php": ">=7.3.0", "behat/transliterator": "~1.0" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "autoload": { "psr-4": { "JeroenDesloovere\\VCard\\": "src/" } }, "autoload-dev": { "psr-4": { "JeroenDesloovere\\VCard\\": "tests/" } } } vcard/src/VCardParser.php 0000644 00000024746 15213363773 0011346 0 ustar 00 <?php namespace JeroenDesloovere\VCard; /* * This file is part of the VCard PHP Class from Jeroen Desloovere. * * For the full copyright and license information, please view the license * file that was distributed with this source code. */ use Iterator; use OutOfBoundsException; use RuntimeException; use stdClass; /** * VCard PHP Class to parse .vcard files. * * This class is heavily based on the Zendvcard project (seemingly abandoned), * which is licensed under the Apache 2.0 license. * More information can be found at https://code.google.com/archive/p/zendvcard/ */ class VCardParser implements Iterator { /** * The raw VCard content. * * @var string */ protected $content; /** * The VCard data objects. * * @var array */ protected $vcardObjects; /** * The iterator position. * * @var int */ protected $position; /** * Helper function to parse a file directly. * * @param string $filename * @return self */ public static function parseFromFile($filename) { if (file_exists($filename) && is_readable($filename)) { return new self(file_get_contents($filename)); } else { throw new \RuntimeException(sprintf("File %s is not readable, or doesn't exist.", $filename)); } } public function __construct($content) { $this->content = $content; $this->vcardObjects = []; $this->rewind(); $this->parse(); } public function rewind(): void { $this->position = 0; } public function current(): \stdClass { if (! $this->valid()) { throw new RuntimeException('invalid'); } return $this->getCardAtIndex($this->position); } public function key(): int { return $this->position; } public function next(): void { $this->position++; } public function valid(): bool { return !empty($this->vcardObjects[$this->position]); } /** * Fetch all the imported VCards. * * @return array * A list of VCard card data objects. */ public function getCards(): array { return $this->vcardObjects; } /** * Fetch the imported VCard at the specified index. * * @throws OutOfBoundsException * * @param int $i * * @return stdClass * The card data object. */ public function getCardAtIndex($i): stdClass { if (isset($this->vcardObjects[$i])) { return $this->vcardObjects[$i]; } throw new \OutOfBoundsException(); } /** * Start the parsing process. * * This method will populate the data object. */ protected function parse() { // Normalize new lines. $this->content = str_replace(["\r\n", "\r"], "\n", $this->content); // RFC2425 5.8.1. Line delimiting and folding // Unfolding is accomplished by regarding CRLF immediately followed by // a white space character (namely HTAB ASCII decimal 9 or. SPACE ASCII // decimal 32) as equivalent to no characters at all (i.e., the CRLF // and single white space character are removed). $this->content = preg_replace("/\n(?:[ \t])/", "", $this->content); $lines = explode("\n", $this->content); // Parse the VCard, line by line. foreach ($lines as $line) { $line = trim($line); if (strtoupper($line) == "BEGIN:VCARD") { $cardData = new \stdClass(); } elseif (strtoupper($line) == "END:VCARD") { $this->vcardObjects[] = $cardData; } elseif (!empty($line)) { // Strip grouping information. We don't use the group names. We // simply use a list for entries that have multiple values. // As per RFC, group names are alphanumerical, and end with a // period (.). $line = preg_replace('/^\w+\./', '', $line); $type = ''; $value = ''; @list($type, $value) = explode(':', $line, 2); $types = explode(';', $type); $element = strtoupper($types[0]); array_shift($types); // Normalize types. A type can either be a type-param directly, // or can be prefixed with "type=". E.g.: "INTERNET" or // "type=INTERNET". if (!empty($types)) { $types = array_map(function($type) { return preg_replace('/^type=/i', '', $type); }, $types); } $i = 0; $rawValue = false; foreach ($types as $type) { if (preg_match('/base64/', strtolower($type))) { $value = base64_decode($value); unset($types[$i]); $rawValue = true; } elseif (preg_match('/encoding=b/', strtolower($type))) { $value = base64_decode($value); unset($types[$i]); $rawValue = true; } elseif (preg_match('/quoted-printable/', strtolower($type))) { $value = quoted_printable_decode($value); unset($types[$i]); $rawValue = true; } elseif (strpos(strtolower($type), 'charset=') === 0) { try { $value = mb_convert_encoding($value, "UTF-8", substr($type, 8)); } catch (\Exception $e) { } unset($types[$i]); } $i++; } switch (strtoupper($element)) { case 'FN': $cardData->fullname = $value; break; case 'N': foreach ($this->parseName($value) as $key => $val) { $cardData->{$key} = $val; } break; case 'BDAY': $cardData->birthday = $this->parseBirthday($value); break; case 'ADR': if (!isset($cardData->address)) { $cardData->address = []; } $key = !empty($types) ? implode(';', $types) : 'WORK;POSTAL'; $cardData->address[$key][] = $this->parseAddress($value); break; case 'TEL': if (!isset($cardData->phone)) { $cardData->phone = []; } $key = !empty($types) ? implode(';', $types) : 'default'; $cardData->phone[$key][] = $value; break; case 'EMAIL': if (!isset($cardData->email)) { $cardData->email = []; } $key = !empty($types) ? implode(';', $types) : 'default'; $cardData->email[$key][] = $value; break; case 'REV': $cardData->revision = $value; break; case 'VERSION': $cardData->version = $value; break; case 'ORG': $cardData->organization = $value; break; case 'URL': if (!isset($cardData->url)) { $cardData->url = []; } $key = !empty($types) ? implode(';', $types) : 'default'; $cardData->url[$key][] = $value; break; case 'TITLE': $cardData->title = $value; break; case 'PHOTO': if ($rawValue) { $cardData->rawPhoto = $value; } else { $cardData->photo = $value; } break; case 'LOGO': if ($rawValue) { $cardData->rawLogo = $value; } else { $cardData->logo = $value; } break; case 'NOTE': $cardData->note = $this->unescape($value); break; case 'CATEGORIES': $cardData->categories = array_map('trim', explode(',', $value)); break; case 'LABEL': $cardData->label = $value; break; } } } } protected function parseName($value) { @list( $lastname, $firstname, $additional, $prefix, $suffix ) = explode(';', $value); return (object) [ 'lastname' => $lastname, 'firstname' => $firstname, 'additional' => $additional, 'prefix' => $prefix, 'suffix' => $suffix, ]; } protected function parseBirthday($value) { return new \DateTime($value); } protected function parseAddress($value) { @list( $name, $extended, $street, $city, $region, $zip, $country, ) = explode(';', $value); return (object) [ 'name' => $name, 'extended' => $extended, 'street' => $street, 'city' => $city, 'region' => $region, 'zip' => $zip, 'country' => $country, ]; } /** * Unescape newline characters according to RFC2425 section 5.8.4. * This function will replace escaped line breaks with PHP_EOL. * * @link http://tools.ietf.org/html/rfc2425#section-5.8.4 * @param string $text * @return string */ protected function unescape($text) { return str_replace("\\n", PHP_EOL, $text); } } vcard/src/VCardException.php 0000644 00000001462 15213363773 0012036 0 ustar 00 <?php namespace JeroenDesloovere\VCard; /* * This file is part of the VCard PHP Class from Jeroen Desloovere. * * For the full copyright and license information, please view the license * file that was distributed with this source code. */ /** * VCard Exception PHP Class. */ class VCardException extends \Exception { public static function elementAlreadyExists($element) { return new self('You can only set "' . $element . '" once.'); } public static function emptyURL() { return new self('Nothing returned from URL.'); } public static function invalidImage() { return new self('Returned data is not an image.'); } public static function outputDirectoryNotExists() { return new self('Output directory does not exist.'); } } vcard/src/VCard.php 0000644 00000060260 15213363773 0010160 0 ustar 00 <?php namespace JeroenDesloovere\VCard; /* * This file is part of the VCard PHP Class from Jeroen Desloovere. * * For the full copyright and license information, please view the license * file that was distributed with this source code. */ use Behat\Transliterator\Transliterator; /** * VCard PHP Class to generate .vcard files and save them to a file or output as a download. */ class VCard { /** * definedElements * * @var array */ private $definedElements; /** * Filename * * @var string */ private $filename; /** * Save Path * * @var string */ private $savePath = null; /** * Multiple properties for element allowed * * @var array */ private $multiplePropertiesForElementAllowed = [ 'email', 'address', 'phoneNumber', 'url', 'label' ]; /** * Properties * * @var array */ private $properties; /** * Default Charset * * @var string */ public $charset = 'utf-8'; /** * Add address * * @param string [optional] $name * @param string [optional] $extended * @param string [optional] $street * @param string [optional] $city * @param string [optional] $region * @param string [optional] $zip * @param string [optional] $country * @param string [optional] $type * $type may be DOM | INTL | POSTAL | PARCEL | HOME | WORK * or any combination of these: e.g. "WORK;PARCEL;POSTAL" * @return $this */ public function addAddress( $name = '', $extended = '', $street = '', $city = '', $region = '', $zip = '', $country = '', $type = 'WORK;POSTAL' ) { // init value $value = $name . ';' . $extended . ';' . $street . ';' . $city . ';' . $region . ';' . $zip . ';' . $country; // set property $this->setProperty( 'address', 'ADR' . (($type != '') ? ';' . $type : '') . $this->getCharsetString(), $value ); return $this; } /** * Add birthday * * @param string $date Format is YYYY-MM-DD * @return $this */ public function addBirthday($date) { $this->setProperty( 'birthday', 'BDAY', $date ); return $this; } /** * Add company * * @param string $company * @param string $department * @return $this */ public function addCompany($company, $department = '') { $this->setProperty( 'company', 'ORG' . $this->getCharsetString(), $company . ($department != '' ? ';' . $department : '') ); // if filename is empty, add to filename if ($this->filename === null) { $this->setFilename($company); } return $this; } /** * Add email * * @param string $address The e-mail address * @param string [optional] $type The type of the email address * $type may be PREF | WORK | HOME * or any combination of these: e.g. "PREF;WORK" * @return $this */ public function addEmail($address, $type = '') { $this->setProperty( 'email', 'EMAIL;INTERNET' . (($type != '') ? ';' . $type : ''), $address ); return $this; } /** * Add jobtitle * * @param string $jobtitle The jobtitle for the person. * @return $this */ public function addJobtitle($jobtitle) { $this->setProperty( 'jobtitle', 'TITLE' . $this->getCharsetString(), $jobtitle ); return $this; } /** * Add a label * * @param string $label * @param string $type * * @return $this */ public function addLabel($label, $type = '') { $this->setProperty( 'label', 'LABEL' . ($type !== '' ? ';' . $type : '') . $this->getCharsetString(), $label ); return $this; } /** * Add role * * @param string $role The role for the person. * @return $this */ public function addRole($role) { $this->setProperty( 'role', 'ROLE' . $this->getCharsetString(), $role ); return $this; } /** * Add a photo or logo (depending on property name) * * @param string $property LOGO|PHOTO * @param string $url image url or filename * @param bool $include Do we include the image in our vcard or not? * @param string $element The name of the element to set * @throws VCardException */ private function addMedia($property, $url, $element, $include = true) { $mimeType = null; //Is this URL for a remote resource? if (filter_var($url, FILTER_VALIDATE_URL) !== false) { $headers = get_headers($url, 1); if (array_key_exists('Content-Type', $headers)) { $mimeType = $headers['Content-Type']; if (is_array($mimeType)) { $mimeType = end($mimeType); } } } else { //Local file, so inspect it directly $mimeType = mime_content_type($url); } if (strpos($mimeType, ';') !== false) { $mimeType = strstr($mimeType, ';', true); } if (!is_string($mimeType) || substr($mimeType, 0, 6) !== 'image/') { throw VCardException::invalidImage(); } $fileType = strtoupper(substr($mimeType, 6)); if ($include) { if ((bool) ini_get('allow_url_fopen') === true) { $value = file_get_contents($url); } else { $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, $url); curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); $value = curl_exec($curl); curl_close($curl); } if (!$value) { throw VCardException::emptyURL(); } $value = base64_encode($value); $property .= ";ENCODING=b;TYPE=" . $fileType; } else { if (filter_var($url, FILTER_VALIDATE_URL) !== false) { $propertySuffix = ';VALUE=URL'; $propertySuffix .= ';TYPE=' . strtoupper($fileType); $property = $property . $propertySuffix; $value = $url; } else { $value = $url; } } $this->setProperty( $element, $property, $value ); } /** * Add a photo or logo (depending on property name) * * @param string $property LOGO|PHOTO * @param string $content image content * @param string $element The name of the element to set */ private function addMediaContent($property, $content, $element) { $finfo = new \finfo(); $mimeType = $finfo->buffer($content, FILEINFO_MIME_TYPE); if (strpos($mimeType, ';') !== false) { $mimeType = strstr($mimeType, ';', true); } if (!is_string($mimeType) || substr($mimeType, 0, 6) !== 'image/') { throw VCardException::invalidImage(); } $fileType = strtoupper(substr($mimeType, 6)); $content = base64_encode($content); $property .= ";ENCODING=b;TYPE=" . $fileType; $this->setProperty( $element, $property, $content ); } /** * Add name * * @param string [optional] $lastName * @param string [optional] $firstName * @param string [optional] $additional * @param string [optional] $prefix * @param string [optional] $suffix * @return $this */ public function addName( $lastName = '', $firstName = '', $additional = '', $prefix = '', $suffix = '' ) { // define values with non-empty values $values = array_filter([ $prefix, $firstName, $additional, $lastName, $suffix, ]); // define filename $this->setFilename($values); // set property $property = $lastName . ';' . $firstName . ';' . $additional . ';' . $prefix . ';' . $suffix; $this->setProperty( 'name', 'N' . $this->getCharsetString(), $property ); // is property FN set? if (!$this->hasProperty('FN')) { // set property $this->setProperty( 'fullname', 'FN' . $this->getCharsetString(), trim(implode(' ', $values)) ); } return $this; } /** * Add note * * @param string $note * @return $this */ public function addNote($note) { $this->setProperty( 'note', 'NOTE' . $this->getCharsetString(), $note ); return $this; } /** * Add categories * * @param array $categories * @return $this */ public function addCategories($categories) { $this->setProperty( 'categories', 'CATEGORIES' . $this->getCharsetString(), trim(implode(',', $categories)) ); return $this; } /** * Add phone number * * @param string $number * @param string [optional] $type * Type may be PREF | WORK | HOME | VOICE | FAX | MSG | * CELL | PAGER | BBS | CAR | MODEM | ISDN | VIDEO * or any senseful combination, e.g. "PREF;WORK;VOICE" * @return $this */ public function addPhoneNumber($number, $type = '') { $this->setProperty( 'phoneNumber', 'TEL' . (($type != '') ? ';' . $type : ''), $number ); return $this; } /** * Add Logo * * @param string $url image url or filename * @param bool $include Include the image in our vcard? * @return $this */ public function addLogo($url, $include = true) { $this->addMedia( 'LOGO', $url, 'logo', $include ); return $this; } /** * Add Logo content * * @param string $content image content * @return $this */ public function addLogoContent($content) { $this->addMediaContent( 'LOGO', $content, 'logo' ); return $this; } /** * Add Photo * * @param string $url image url or filename * @param bool $include Include the image in our vcard? * @return $this */ public function addPhoto($url, $include = true) { $this->addMedia( 'PHOTO', $url, 'photo', $include ); return $this; } /** * Add Photo content * * @param string $content image content * @return $this */ public function addPhotoContent($content) { $this->addMediaContent( 'PHOTO', $content, 'photo' ); return $this; } /** * Add URL * * @param string $url * @param string [optional] $type Type may be WORK | HOME * @return $this */ public function addURL($url, $type = '') { $this->setProperty( 'url', 'URL' . (($type != '') ? ';' . $type : ''), $url ); return $this; } /** * Build VCard (.vcf) * * @return string */ public function buildVCard() { // init string $string = "BEGIN:VCARD\r\n"; $string .= "VERSION:3.0\r\n"; $string .= "REV:" . date("Y-m-d") . "T" . date("H:i:s") . "Z\r\n"; // loop all properties $properties = $this->getProperties(); foreach ($properties as $property) { // add to string $string .= $this->fold($property['key'] . ':' . $this->escape($property['value']) . "\r\n"); } // add to string $string .= "END:VCARD\r\n"; // return return $string; } /** * Build VCalender (.ics) - Safari (< iOS 8) can not open .vcf files, so we have build a workaround. * * @return string */ public function buildVCalendar() { // init dates $dtstart = date("Ymd") . "T" . date("Hi") . "00"; $dtend = date("Ymd") . "T" . date("Hi") . "01"; // init string $string = "BEGIN:VCALENDAR\n"; $string .= "VERSION:2.0\n"; $string .= "BEGIN:VEVENT\n"; $string .= "DTSTART;TZID=Europe/London:" . $dtstart . "\n"; $string .= "DTEND;TZID=Europe/London:" . $dtend . "\n"; $string .= "SUMMARY:Click attached contact below to save to your contacts\n"; $string .= "DTSTAMP:" . $dtstart . "Z\n"; $string .= "ATTACH;VALUE=BINARY;ENCODING=BASE64;FMTTYPE=text/directory;\n"; $string .= " X-APPLE-FILENAME=" . $this->getFilename() . "." . $this->getFileExtension() . ":\n"; // base64 encode it so that it can be used as an attachemnt to the "dummy" calendar appointment $b64vcard = base64_encode($this->buildVCard()); // chunk the single long line of b64 text in accordance with RFC2045 // (and the exact line length determined from the original .ics file exported from Apple calendar $b64mline = chunk_split($b64vcard, 74, "\n"); // need to indent all the lines by 1 space for the iphone (yes really?!!) $b64final = preg_replace('/(.+)/', ' $1', $b64mline); $string .= $b64final; // output the correctly formatted encoded text $string .= "END:VEVENT\n"; $string .= "END:VCALENDAR\n"; // return return $string; } /** * Returns the browser user agent string. * * @return string */ protected function getUserAgent() { if (array_key_exists('HTTP_USER_AGENT', $_SERVER)) { $browser = strtolower($_SERVER['HTTP_USER_AGENT']); } else { $browser = 'unknown'; } return $browser; } /** * Decode * * @param string $value The value to decode * @return string decoded */ private function decode($value) { // convert cyrlic, greek or other caracters to ASCII characters return Transliterator::transliterate($value); } /** * Download a vcard or vcal file to the browser. */ public function download() { // define output $output = $this->getOutput(); foreach ($this->getHeaders(false) as $header) { header($header); } // echo the output and it will be a download echo $output; } /** * Fold a line according to RFC2425 section 5.8.1. * * @link http://tools.ietf.org/html/rfc2425#section-5.8.1 * @param string $text * @return mixed */ protected function fold($text) { if (strlen($text) <= 75) { return $text; } // The chunk_split_unicode creates a huge memory footprint when used on long strings (EG photos are base64 10MB results in > 1GB memory usage) // So check if the string is ASCII (7 bit) and if it is use the built in way RE: https://github.com/jeroendesloovere/vcard/issues/153 if ($this->is_ascii($text)) { return substr(chunk_split($text, 75, "\r\n "), 0, -3); } // split, wrap and trim trailing separator return substr($this->chunk_split_unicode($text, 75, "\r\n "), 0, -3); } /** * Determine if string is pure 7bit ascii * @link https://pageconfig.com/post/how-to-validate-ascii-text-in-php * * @param string $string * @return bool */ protected function is_ascii($string = '' ) { $num = 0; while( isset( $string[$num] ) ) { if( ord( $string[$num] ) & 0x80 ) { return false; } $num++; } return true; } /** * multibyte word chunk split * @link http://php.net/manual/en/function.chunk-split.php#107711 * * @param string $body The string to be chunked. * @param integer $chunklen The chunk length. * @param string $end The line ending sequence. * @return string Chunked string */ protected function chunk_split_unicode($body, $chunklen = 76, $end = "\r\n") { $array = array_chunk( preg_split("//u", $body, -1, PREG_SPLIT_NO_EMPTY), $chunklen); $body = ""; foreach ($array as $item) { $body .= join("", $item) . $end; } return $body; } /** * Escape newline characters according to RFC2425 section 5.8.4. * * @link http://tools.ietf.org/html/rfc2425#section-5.8.4 * @param string $text * @return string */ protected function escape($text) { if ($text === null) { return null; } $text = str_replace("\r\n", "\\n", $text); $text = str_replace("\n", "\\n", $text); return $text; } /** * Get output as string * @deprecated in the future * * @return string */ public function get() { return $this->getOutput(); } /** * Get charset * * @return string */ public function getCharset() { return $this->charset; } /** * Get charset string * * @return string */ public function getCharsetString() { return ';CHARSET=' . $this->charset; } /** * Get content type * * @return string */ public function getContentType() { return ($this->isIOS7()) ? 'text/x-vcalendar' : 'text/x-vcard'; } /** * Get filename * * @return string */ public function getFilename() { if (!$this->filename) { return 'unknown'; } return $this->filename; } /** * Get file extension * * @return string */ public function getFileExtension() { return ($this->isIOS7()) ? 'ics' : 'vcf'; } /** * Get headers * * @param bool $asAssociative * @return array */ public function getHeaders($asAssociative) { $contentType = $this->getContentType() . '; charset=' . $this->getCharset(); $contentDisposition = 'attachment; filename=' . $this->getFilename() . '.' . $this->getFileExtension(); $contentLength = mb_strlen($this->getOutput(), '8bit'); $connection = 'close'; if ((bool)$asAssociative) { return [ 'Content-type' => $contentType, 'Content-Disposition' => $contentDisposition, 'Content-Length' => $contentLength, 'Connection' => $connection, ]; } return [ 'Content-type: ' . $contentType, 'Content-Disposition: ' . $contentDisposition, 'Content-Length: ' . $contentLength, 'Connection: ' . $connection, ]; } /** * Get output as string * iOS devices (and safari < iOS 8 in particular) can not read .vcf (= vcard) files. * So I build a workaround to build a .ics (= vcalender) file. * * @return string */ public function getOutput() { $output = ($this->isIOS7()) ? $this->buildVCalendar() : $this->buildVCard(); return $output; } /** * Get properties * * @return array */ public function getProperties() { return $this->properties; } /** * Has property * * @param string $key * @return bool */ public function hasProperty($key) { $properties = $this->getProperties(); foreach ($properties as $property) { if ($property['key'] === $key && $property['value'] !== '') { return true; } } return false; } /** * Is iOS - Check if the user is using an iOS-device * * @return bool */ public function isIOS() { // get user agent $browser = $this->getUserAgent(); return (strpos($browser, 'iphone') || strpos($browser, 'ipod') || strpos($browser, 'ipad')); } /** * Is iOS less than 7 (should cal wrapper be returned) * * @return bool */ public function isIOS7() { return ($this->isIOS() && $this->shouldAttachmentBeCal()); } /** * Save to a file * * @return void */ public function save() { $file = $this->getFilename() . '.' . $this->getFileExtension(); // Add save path if given if (null !== $this->savePath) { $file = $this->savePath . $file; } file_put_contents( $file, $this->getOutput() ); } /** * Set charset * * @param mixed $charset * @return void */ public function setCharset($charset) { $this->charset = $charset; } /** * Set filename * * @param mixed $value * @param bool $overwrite [optional] Default overwrite is true * @param string $separator [optional] Default separator is an underscore '_' * @return void */ public function setFilename($value, $overwrite = true, $separator = '_') { // recast to string if $value is array if (is_array($value)) { $value = implode($separator, $value); } // trim unneeded values $value = trim($value, $separator); // remove all spaces $value = preg_replace('/\s+/', $separator, $value); // if value is empty, stop here if (empty($value)) { return; } // decode value + lowercase the string $value = strtolower($this->decode($value)); // urlize this part $value = Transliterator::urlize($value); // overwrite filename or add to filename using a prefix in between $this->filename = ($overwrite) ? $value : $this->filename . $separator . $value; } /** * Set the save path directory * * @param string $savePath Save Path * @throws VCardException */ public function setSavePath($savePath) { if (!is_dir($savePath)) { throw VCardException::outputDirectoryNotExists(); } // Add trailing directory separator the save path if (substr($savePath, -1) != DIRECTORY_SEPARATOR) { $savePath .= DIRECTORY_SEPARATOR; } $this->savePath = $savePath; } /** * Set property * * @param string $element The element name you want to set, f.e.: name, email, phoneNumber, ... * @param string $key * @param string $value * @throws VCardException */ private function setProperty($element, $key, $value) { if (!in_array($element, $this->multiplePropertiesForElementAllowed) && isset($this->definedElements[$element]) ) { throw VCardException::elementAlreadyExists($element); } // we define that we set this element $this->definedElements[$element] = true; // adding property $this->properties[] = [ 'key' => $key, 'value' => $value ]; } /** * Checks if we should return vcard in cal wrapper * * @return bool */ protected function shouldAttachmentBeCal() { $browser = $this->getUserAgent(); $matches = []; preg_match('/os (\d+)_(\d+)\s+/', $browser, $matches); $version = isset($matches[1]) ? ((int)$matches[1]) : 999; return ($version < 8); } } vcard/examples/example_parsing.php 0000644 00000001142 15213363773 0013360 0 ustar 00 <?php /** * VCardParser test - can parse bundled VCF file into CSV */ require_once __DIR__ . '/../vendor/autoload.php'; require_once __DIR__ . '/../src/VCardParser.php'; // load VCardParser classes use JeroenDesloovere\VCard\VCardParser; $pathToVCardExample = __DIR__ . '/assets/contacts.vcf'; $parser = VCardParser::parseFromFile($pathToVCardExample); foreach($parser as $vcard) { $lastname = $vcard->lastname; $firstname = $vcard->firstname; $birthday = $vcard->birthday->format('Y-m-d'); printf("\"%s\",\"%s\",\"%s\"", $lastname, $firstname, $birthday); echo PHP_EOL; } vcard/examples/assets/contacts.vcf 0000644 00000002462 15213363773 0013317 0 ustar 00 BEGIN:VCARD VERSION:3.0 FN:Thies-Tillman Jacobsen N:Jacobsen;Thies-Tillman;;; BDAY:1908-10-25 CATEGORIES:Sportverein TZ:+0100 NOTE:Lieblingsfarbe: Violett END:VCARD BEGIN:VCARD VERSION:3.0 FN:Lenn Biernoth N:Biernoth;Lenn;;; X-MAIDENNAME:Iser BDAY:1951-11-22 BIRTHPLACE:Hörstel TITLE:Strassenreiniger CATEGORIES:Familie TZ:+0100 EMAIL;TYPE=INTERNET;TYPE=HOME;TYPE=PREF:lenn@lbi.net NOTE:Interessen: Internet trollen, Kekse backen, Videos schneiden\nLieblingsessen: Chillibrot mit Kartoffelpuffer END:VCARD BEGIN:VCARD VERSION:3.0 FN:Ludwig-Götz Graßl N:Graßl;Ludwig-Götz;;; X-MAIDENNAME:Schoen BDAY:1943-02-07 BIRTHPLACE:Kaltennordheim TITLE:Rentner CATEGORIES:Piratenpartei TZ:+0100 EMAIL;TYPE=INTERNET;TYPE=HOME;TYPE=PREF:ludwig-goetz@ludwig-goetz-grassl.org NOTE:Interessen: Stofftiere, Basketball\nLieblingsessen: Reiberouladen END:VCARD BEGIN:VCARD VERSION:3.0 FN:Marita Kreutzer N:Kreutzer;Marita;;; NICKNAME:mkr X-MAIDENNAME:Sievers BDAY:1943-07-26 TITLE:Rentnerin CATEGORIES:CCC TZ:+0100 NOTE:Interessen: Handarbeiten\nLieblingsfarbe: Weiß\nLieblingsessen: Orangen END:VCARD BEGIN:VCARD VERSION:3.0 FN:Kathi Hoelzl N:Hoelzl;Kathi;;; BDAY:2002-07-11 BIRTHPLACE:Grevenbroich CATEGORIES:Piratenpartei TZ:+0100 EMAIL;TYPE=INTERNET;TYPE=HOME;TYPE=PREF:kathi.hoelzl@alhilal.net URL;TYPE=HOME:http://kho.me/ END:VCARD vcard/examples/assets/landscape.jpeg 0000644 00000021072 15213363773 0013600 0 ustar 00 ��� JFIF �� � ( '"1!%)+... 383,7(-.+ ,% $,,,44,,,,/,4,,,,,4,,,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,�� �,"