From 5dc84ef088cdcc6916d5c30bfaace1e1159cae51 Mon Sep 17 00:00:00 2001 From: Francis Besset Date: Sun, 4 Sep 2011 00:29:19 +0200 Subject: [PATCH 01/48] Initial commit --- phpunit.xml.dist | 31 +++++ src/BeSimple/SoapCommon/Cache.php | 107 ++++++++++++++++++ tests/BeSimple/Tests/SoapCommon/CacheTest.php | 72 ++++++++++++ tests/bootstrap.php | 22 ++++ 4 files changed, 232 insertions(+) create mode 100644 phpunit.xml.dist create mode 100644 src/BeSimple/SoapCommon/Cache.php create mode 100644 tests/BeSimple/Tests/SoapCommon/CacheTest.php create mode 100644 tests/bootstrap.php diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..e719cf2 --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + ./tests/BeSimple/ + + + + + + benchmark + + + + + + ./src/BeSimple/ + + + diff --git a/src/BeSimple/SoapCommon/Cache.php b/src/BeSimple/SoapCommon/Cache.php new file mode 100644 index 0000000..c4213a8 --- /dev/null +++ b/src/BeSimple/SoapCommon/Cache.php @@ -0,0 +1,107 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\SoapCommon; + +/** + * @author Francis Besset + */ +class Cache +{ + const DISABLED = 0; + const ENABLED = 1; + + const TYPE_NONE = WSDL_CACHE_NONE; + const TYPE_DISK = WSDL_CACHE_DISK; + const TYPE_MEMORY = WSDL_CACHE_MEMORY; + const TYPE_DISK_MEMORY = WSDL_CACHE_BOTH; + + static protected $types = array( + self::TYPE_NONE, + self::TYPE_DISK, + self::TYPE_MEMORY, + self::TYPE_DISK_MEMORY, + ); + + static public function getTypes() + { + return self::$types; + } + + static public function isEnabled() + { + return self::iniGet('soap.wsdl_cache_enabled'); + } + + static public function setEnabled($enabled) + { + if (!in_array($enabled, array(self::ENABLED, self::DISABLED))) { + throw new \InvalidArgumentException(); + } + + self::iniSet('soap.wsdl_cache_enabled', $enabled); + } + + static public function getType() + { + return self::iniGet('soap.wsdl_cache'); + } + + static public function setType($type) + { + if (!in_array($type, self::getTypes())) { + throw new \InvalidArgumentException(); + } + + self::iniSet('soap.wsdl_cache', $type); + } + + static public function getDirectory() + { + return self::iniGet('soap.wsdl_cache_dir'); + } + + static public function setDirectory($directory) + { + self::iniSet('soap.wsdl_cache_dir', $directory); + } + + static public function getLifetime() + { + return self::iniGet('soap.wsdl_cache_ttl'); + } + + static public function setLifetime($lifetime) + { + self::iniSet('soap.wsdl_cache_ttl', $lifetime); + } + + static public function getLimit() + { + return self::iniGet('soap.wsdl_cache_limit'); + } + + static public function setLimit($limit) + { + self::iniSet('soap.wsdl_cache_limit', $limit); + } + + static protected function iniGet($key) + { + return ini_get($key); + } + + static protected function iniSet($key, $value) + { + ini_set($key, $value); + } +} \ No newline at end of file diff --git a/tests/BeSimple/Tests/SoapCommon/CacheTest.php b/tests/BeSimple/Tests/SoapCommon/CacheTest.php new file mode 100644 index 0000000..aebe217 --- /dev/null +++ b/tests/BeSimple/Tests/SoapCommon/CacheTest.php @@ -0,0 +1,72 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\Tests\SoapCommon\Soap; + +use BeSimple\SoapCommon\Cache; + +class SoapRequestTest extends \PHPUnit_Framework_TestCase +{ + public function testSetEnabled() + { + Cache::setEnabled(Cache::ENABLED); + $this->assertEquals(Cache::ENABLED, Cache::isEnabled()); + + Cache::setEnabled(Cache::DISABLED); + $this->assertEquals(Cache::DISABLED, Cache::isEnabled()); + } + + public function testSetType() + { + Cache::setType(Cache::TYPE_DISK); + $this->assertEquals(Cache::TYPE_DISK, Cache::getType()); + + Cache::setType(Cache::TYPE_NONE); + $this->assertEquals(Cache::TYPE_NONE, Cache::getType()); + } + + public function testSetDirectory() + { + Cache::setDirectory('/foo'); + $this->assertEquals('/foo', Cache::getDirectory()); + + Cache::setDirectory('/bar'); + $this->assertEquals('/bar', Cache::getDirectory()); + } + + public function testSetLifetime() + { + Cache::setLifetime(1234); + $this->assertEquals(1234, Cache::getLifetime()); + + Cache::setLifetime(4321); + $this->assertEquals(4321, Cache::getLifetime()); + } + + public function testSetLimit() + { + Cache::setLimit(10); + $this->assertEquals(10, Cache::getLimit()); + + Cache::setLimit(1); + $this->assertEquals(1, Cache::getLimit()); + } + + public function setUp() + { + ini_restore('soap.wsdl_cache_enabled'); + ini_restore('soap.wsdl_cache'); + ini_restore('soap.wsdl_cache_dir'); + ini_restore('soap.wsdl_cache_ttl'); + ini_restore('soap.wsdl_cache_limit'); + } +} \ No newline at end of file diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000..96aebb6 --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,22 @@ + Date: Sat, 10 Sep 2011 18:51:04 +0200 Subject: [PATCH 02/48] Check that the directory cache exists --- .gitignore | 1 + src/BeSimple/SoapCommon/Cache.php | 4 ++ tests/BeSimple/Tests/SoapCommon/CacheTest.php | 16 ++++++-- tests/bootstrap.php | 9 +++- vendors.php | 41 +++++++++++++++++++ 5 files changed, 66 insertions(+), 5 deletions(-) create mode 100644 .gitignore create mode 100755 vendors.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..22d0d82 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +vendor diff --git a/src/BeSimple/SoapCommon/Cache.php b/src/BeSimple/SoapCommon/Cache.php index c4213a8..7deecc7 100644 --- a/src/BeSimple/SoapCommon/Cache.php +++ b/src/BeSimple/SoapCommon/Cache.php @@ -72,6 +72,10 @@ class Cache static public function setDirectory($directory) { + if (!is_dir($directory)) { + mkdir($directory, 0777, true); + } + self::iniSet('soap.wsdl_cache_dir', $directory); } diff --git a/tests/BeSimple/Tests/SoapCommon/CacheTest.php b/tests/BeSimple/Tests/SoapCommon/CacheTest.php index aebe217..f56f192 100644 --- a/tests/BeSimple/Tests/SoapCommon/CacheTest.php +++ b/tests/BeSimple/Tests/SoapCommon/CacheTest.php @@ -36,11 +36,19 @@ class SoapRequestTest extends \PHPUnit_Framework_TestCase public function testSetDirectory() { - Cache::setDirectory('/foo'); - $this->assertEquals('/foo', Cache::getDirectory()); + \vfsStream::setup('Fixtures'); - Cache::setDirectory('/bar'); - $this->assertEquals('/bar', Cache::getDirectory()); + $this->assertFalse(\vfsStreamWrapper::getRoot()->hasChild('foo')); + $dir = \vfsStream::url('Fixtures/foo'); + Cache::setDirectory($dir); + $this->assertEquals($dir, Cache::getDirectory()); + $this->assertTrue(\vfsStreamWrapper::getRoot()->hasChild('foo')); + + $this->assertFalse(\vfsStreamWrapper::getRoot()->hasChild('bar')); + $dir = \vfsStream::url('Fixtures/bar'); + Cache::setDirectory($dir); + $this->assertEquals($dir, Cache::getDirectory()); + $this->assertTrue(\vfsStreamWrapper::getRoot()->hasChild('bar')); } public function testSetLifetime() diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 96aebb6..f92b0dd 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -11,11 +11,18 @@ spl_autoload_register(function($class) { return true; } - } else if (0 === strpos($class, 'BeSimple\SoapCommon\\')) { + } elseif (0 === strpos($class, 'BeSimple\SoapCommon\\')) { $path = __DIR__.'/../src/'.($class = strtr($class, '\\', '/')).'.php'; if (file_exists($path) && is_readable($path)) { require_once $path; + return true; + } + } elseif (0 === strpos($class, 'vfsStream')) { + $path = __DIR__.'/../vendor/vfsStream/src/main/php/org/bovigo/vfs/'.$class.'.php'; + if (file_exists($path) && is_readable($path)) { + require_once $path; + return true; } } diff --git a/vendors.php b/vendors.php new file mode 100755 index 0000000..7f127b0 --- /dev/null +++ b/vendors.php @@ -0,0 +1,41 @@ +#!/usr/bin/env php + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/* + +CAUTION: This file installs the dependencies needed to run the BeSimpleSoapCommon test suite. + +https://github.com/BeSimple/BeSimpleSoapCommon + +*/ + +if (!is_dir($vendorDir = dirname(__FILE__).'/vendor')) { + mkdir($vendorDir, 0777, true); +} + +$deps = array( + array('vfsStream', 'https://github.com/mikey179/vfsStream.git', 'RELEASE-0.10.1'), +); + +foreach ($deps as $dep) { + list($name, $url, $rev) = $dep; + + echo "> Installing/Updating $name\n"; + + $installDir = $vendorDir.'/'.$name; + if (!is_dir($installDir)) { + system(sprintf('git clone %s %s', escapeshellarg($url), escapeshellarg($installDir))); + } + + system(sprintf('cd %s && git fetch origin && git reset --hard %s', escapeshellarg($installDir), escapeshellarg($rev))); +} From 69d1e59edfe240b8e1ddb4380c25c19fbb73b9eb Mon Sep 17 00:00:00 2001 From: Francis Besset Date: Sat, 10 Sep 2011 19:14:38 +0200 Subject: [PATCH 03/48] Added TypeConverterCollection --- .../Converter/TypeConverterCollection.php | 31 ++++++++++++++ .../Converter/TypeConverterInterface.php | 27 +++++++++++++ .../Converter/TypeConverterCollectionTest.php | 40 +++++++++++++++++++ 3 files changed, 98 insertions(+) create mode 100644 src/BeSimple/SoapCommon/Converter/TypeConverterCollection.php create mode 100644 src/BeSimple/SoapCommon/Converter/TypeConverterInterface.php create mode 100644 tests/BeSimple/Tests/SoapCommon/Converter/TypeConverterCollectionTest.php diff --git a/src/BeSimple/SoapCommon/Converter/TypeConverterCollection.php b/src/BeSimple/SoapCommon/Converter/TypeConverterCollection.php new file mode 100644 index 0000000..fbdd79b --- /dev/null +++ b/src/BeSimple/SoapCommon/Converter/TypeConverterCollection.php @@ -0,0 +1,31 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\SoapCommon\Converter; + +/** + * @author Christian Kerl + */ +class TypeConverterCollection +{ + private $typeConverters = array(); + + public function add(TypeConverterInterface $converter) + { + $this->typeConverters[] = $converter; + } + + public function all() + { + return $this->typeConverters; + } +} diff --git a/src/BeSimple/SoapCommon/Converter/TypeConverterInterface.php b/src/BeSimple/SoapCommon/Converter/TypeConverterInterface.php new file mode 100644 index 0000000..4de7d7f --- /dev/null +++ b/src/BeSimple/SoapCommon/Converter/TypeConverterInterface.php @@ -0,0 +1,27 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\SoapCommon\Converter; + +/** + * @author Christian Kerl + */ +interface TypeConverterInterface +{ + function getTypeNamespace(); + + function getTypeName(); + + function convertXmlToPhp($data); + + function convertPhpToXml($data); +} \ No newline at end of file diff --git a/tests/BeSimple/Tests/SoapCommon/Converter/TypeConverterCollectionTest.php b/tests/BeSimple/Tests/SoapCommon/Converter/TypeConverterCollectionTest.php new file mode 100644 index 0000000..4c9feef --- /dev/null +++ b/tests/BeSimple/Tests/SoapCommon/Converter/TypeConverterCollectionTest.php @@ -0,0 +1,40 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\SoapCommon\Tests\Converter; + +use BeSimple\SoapCommon\Converter\TypeConverterCollection; +use BeSimple\SoapCommon\Converter\DateTimeTypeConverter; +use BeSimple\SoapCommon\Converter\DateTypeConverter; + +/** + * UnitTest for \BeSimple\SoapCommon\Converter\TypeConverterCollection. + * + * @author Francis Besset + */ +class TypeConverterCollectionTest extends \PHPUnit_Framework_TestCase +{ + public function testAdd() + { + $converters = new TypeConverterCollection(); + + $dateTimeTypeConverter = new DateTimeTypeConverter(); + $converters->add($dateTimeTypeConverter); + + $this->assertSame(array($dateTimeTypeConverter), $converters->all()); + + $dateTypeConverter = new DateTypeConverter(); + $converters->add($dateTypeConverter); + + $this->assertSame(array($dateTimeTypeConverter, $dateTypeConverter), $converters->all()); + } +} \ No newline at end of file From 5c3357b879067011d9a2fd356a7cba4e98878c96 Mon Sep 17 00:00:00 2001 From: Francis Besset Date: Sat, 10 Sep 2011 19:16:27 +0200 Subject: [PATCH 04/48] Added DateTimeTypeConverter --- .../Converter/DateTimeTypeConverter.php | 42 ++++++++++++++++++ .../Converter/DateTimeTypeConverterTest.php | 43 +++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 src/BeSimple/SoapCommon/Converter/DateTimeTypeConverter.php create mode 100644 tests/BeSimple/Tests/SoapCommon/Converter/DateTimeTypeConverterTest.php diff --git a/src/BeSimple/SoapCommon/Converter/DateTimeTypeConverter.php b/src/BeSimple/SoapCommon/Converter/DateTimeTypeConverter.php new file mode 100644 index 0000000..6446e35 --- /dev/null +++ b/src/BeSimple/SoapCommon/Converter/DateTimeTypeConverter.php @@ -0,0 +1,42 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\SoapCommon\Converter; + +/** + * @author Christian Kerl + */ +class DateTimeTypeConverter implements TypeConverterInterface +{ + public function getTypeNamespace() + { + return 'http://www.w3.org/2001/XMLSchema'; + } + + public function getTypeName() + { + return 'dateTime'; + } + + public function convertXmlToPhp($data) + { + $doc = new \DOMDocument(); + $doc->loadXML($data); + + return new \DateTime($doc->textContent); + } + + public function convertPhpToXml($data) + { + return sprintf('<%1$s>%2$s', $this->getTypeName(), $data->format('Y-m-d\TH:i:sP')); + } +} \ No newline at end of file diff --git a/tests/BeSimple/Tests/SoapCommon/Converter/DateTimeTypeConverterTest.php b/tests/BeSimple/Tests/SoapCommon/Converter/DateTimeTypeConverterTest.php new file mode 100644 index 0000000..d7913f5 --- /dev/null +++ b/tests/BeSimple/Tests/SoapCommon/Converter/DateTimeTypeConverterTest.php @@ -0,0 +1,43 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\SoapCommon\Tests\Converter; + +use BeSimple\SoapCommon\Converter\DateTimeTypeConverter; + +/** + * UnitTest for \BeSimple\SoapCommon\Converter\DateTimeTypeConverter. + * + * @author Christian Kerl + */ +class DateTimeTypeConverterTest extends \PHPUnit_Framework_TestCase +{ + public function testConvertXmlToPhp() + { + $converter = new DateTimeTypeConverter(); + + $dateXml = '2002-10-10T12:00:00-05:00'; + $date = $converter->convertXmlToPhp($dateXml); + + $this->assertEquals(new \DateTime('2002-10-10T12:00:00-05:00'), $date); + } + + public function testConvertPhpToXml() + { + $converter = new DateTimeTypeConverter(); + + $date = new \DateTime('2002-10-10T12:00:00-05:00'); + $dateXml = $converter->convertPhpToXml($date); + + $this->assertEquals('2002-10-10T12:00:00-05:00', $dateXml); + } +} \ No newline at end of file From 9b0ec8816deed7315c494fc42809d431e2642d6b Mon Sep 17 00:00:00 2001 From: Francis Besset Date: Sat, 10 Sep 2011 19:17:31 +0200 Subject: [PATCH 05/48] Added DateTypeConverter --- .../Converter/DateTypeConverter.php | 42 +++++++++++++++++++ .../Converter/DateTypeConverterTest.php | 41 ++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 src/BeSimple/SoapCommon/Converter/DateTypeConverter.php create mode 100644 tests/BeSimple/Tests/SoapCommon/Converter/DateTypeConverterTest.php diff --git a/src/BeSimple/SoapCommon/Converter/DateTypeConverter.php b/src/BeSimple/SoapCommon/Converter/DateTypeConverter.php new file mode 100644 index 0000000..4e21425 --- /dev/null +++ b/src/BeSimple/SoapCommon/Converter/DateTypeConverter.php @@ -0,0 +1,42 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\SoapCommon\Converter; + +/** + * @author Francis Besset + */ +class DateTypeConverter implements TypeConverterInterface +{ + public function getTypeNamespace() + { + return 'http://www.w3.org/2001/XMLSchema'; + } + + public function getTypeName() + { + return 'date'; + } + + public function convertXmlToPhp($data) + { + $doc = new \DOMDocument(); + $doc->loadXML($data); + + return new \DateTime($doc->textContent); + } + + public function convertPhpToXml($data) + { + return sprintf('<%1$s>%2$s', $this->getTypeName(), $data->format('Y-m-d')); + } +} diff --git a/tests/BeSimple/Tests/SoapCommon/Converter/DateTypeConverterTest.php b/tests/BeSimple/Tests/SoapCommon/Converter/DateTypeConverterTest.php new file mode 100644 index 0000000..6e164cb --- /dev/null +++ b/tests/BeSimple/Tests/SoapCommon/Converter/DateTypeConverterTest.php @@ -0,0 +1,41 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\SoapCommon\Tests\Converter; + +use BeSimple\SoapCommon\Converter\DateTypeConverter; + +/** + * UnitTest for \BeSimple\SoapCommon\Converter\DateTypeConverter. + */ +class DateTypeConverterTest extends \PHPUnit_Framework_TestCase +{ + public function testConvertXmlToPhp() + { + $converter = new DateTypeConverter(); + + $dateXml = '2002-10-10'; + $date = $converter->convertXmlToPhp($dateXml); + + $this->assertEquals(new \DateTime('2002-10-10'), $date); + } + + public function testConvertPhpToXml() + { + $converter = new DateTypeConverter(); + + $date = new \DateTime('2002-10-10'); + $dateXml = $converter->convertPhpToXml($date); + + $this->assertEquals('2002-10-10', $dateXml); + } +} \ No newline at end of file From c2a2413330d0ba47e40029cc089c72467cc52abf Mon Sep 17 00:00:00 2001 From: Francis Besset Date: Tue, 13 Sep 2011 20:58:46 +0200 Subject: [PATCH 06/48] Added TypeConverterCollection::getTypemap() function --- .../Converter/TypeConverterCollection.php | 23 +++++++++++++++++ .../Converter/TypeConverterCollectionTest.php | 25 +++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/src/BeSimple/SoapCommon/Converter/TypeConverterCollection.php b/src/BeSimple/SoapCommon/Converter/TypeConverterCollection.php index fbdd79b..099d0eb 100644 --- a/src/BeSimple/SoapCommon/Converter/TypeConverterCollection.php +++ b/src/BeSimple/SoapCommon/Converter/TypeConverterCollection.php @@ -28,4 +28,27 @@ class TypeConverterCollection { return $this->typeConverters; } + + /** + * @return array + */ + public function getTypemap() + { + $typemap = array(); + + foreach ($this->all() as $typeConverter) { + $typemap[] = array( + 'type_name' => $typeConverter->getTypeName(), + 'type_ns' => $typeConverter->getTypeNamespace(), + 'from_xml' => function($input) use ($typeConverter) { + return $typeConverter->convertXmlToPhp($input); + }, + 'to_xml' => function($input) use ($typeConverter) { + return $typeConverter->convertPhpToXml($input); + }, + ); + } + + return $typemap; + } } diff --git a/tests/BeSimple/Tests/SoapCommon/Converter/TypeConverterCollectionTest.php b/tests/BeSimple/Tests/SoapCommon/Converter/TypeConverterCollectionTest.php index 4c9feef..19865f1 100644 --- a/tests/BeSimple/Tests/SoapCommon/Converter/TypeConverterCollectionTest.php +++ b/tests/BeSimple/Tests/SoapCommon/Converter/TypeConverterCollectionTest.php @@ -37,4 +37,29 @@ class TypeConverterCollectionTest extends \PHPUnit_Framework_TestCase $this->assertSame(array($dateTimeTypeConverter, $dateTypeConverter), $converters->all()); } + + public function testGetTypemap() + { + $converters = new TypeConverterCollection(); + + $this->assertEquals(array(), $converters->getTypemap()); + + $dateTimeTypeConverter = new DateTimeTypeConverter(); + $converters->add($dateTimeTypeConverter); + + $dateTypeConverter = new DateTypeConverter(); + $converters->add($dateTypeConverter); + + $typemap = $converters->getTypemap(); + + $this->assertEquals('http://www.w3.org/2001/XMLSchema', $typemap[0]['type_ns']); + $this->assertEquals('dateTime', $typemap[0]['type_name']); + $this->assertInstanceOf('Closure', $typemap[0]['from_xml']); + $this->assertInstanceOf('Closure', $typemap[0]['to_xml']); + + $this->assertEquals('http://www.w3.org/2001/XMLSchema', $typemap[1]['type_ns']); + $this->assertEquals('date', $typemap[1]['type_name']); + $this->assertInstanceOf('Closure', $typemap[1]['from_xml']); + $this->assertInstanceOf('Closure', $typemap[1]['to_xml']); + } } \ No newline at end of file From 402f229971e62976e185ab0e7532e77ba8160764 Mon Sep 17 00:00:00 2001 From: Francis Besset Date: Sat, 8 Oct 2011 14:58:36 +0200 Subject: [PATCH 07/48] Added Classmap --- src/BeSimple/SoapCommon/Classmap.php | 86 +++++++++++++++++++ .../Tests/SoapCommon/ClassmapTest.php | 67 +++++++++++++++ 2 files changed, 153 insertions(+) create mode 100644 src/BeSimple/SoapCommon/Classmap.php create mode 100644 tests/BeSimple/Tests/SoapCommon/ClassmapTest.php diff --git a/src/BeSimple/SoapCommon/Classmap.php b/src/BeSimple/SoapCommon/Classmap.php new file mode 100644 index 0000000..3f0ece6 --- /dev/null +++ b/src/BeSimple/SoapCommon/Classmap.php @@ -0,0 +1,86 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\SoapCommon; + +/** + * @author Francis Besset + */ +class Classmap +{ + /** + * @var array + */ + protected $classmap = array(); + + + /** + * @return array + */ + public function all() + { + return $this->classmap; + } + + /** + * @param string $type + * + * @return string + * + * @throws \InvalidArgumentException + */ + public function get($type) + { + if (!$this->has($type)) { + throw new \InvalidArgumentException(sprintf('The type "%s" does not exists', $type)); + } + + return $this->classmap[$type]; + } + + /** + * @param string $type + * @param string $classname + * + * @throws \InvalidArgumentException + */ + public function add($type, $classname) + { + if ($this->has($type)) { + throw new \InvalidArgumentException(sprintf('The type "%s" already exists', $type)); + } + + $this->classmap[$type] = $classname; + } + + /** + * @param array $classmap + */ + public function set(array $classmap) + { + $this->classmap = array(); + + foreach ($classmap as $type => $classname) { + $this->add($type, $classname); + } + } + + /** + * @param string $type + * + * @return boolean + */ + public function has($type) + { + return isset($this->classmap[$type]); + } +} \ No newline at end of file diff --git a/tests/BeSimple/Tests/SoapCommon/ClassmapTest.php b/tests/BeSimple/Tests/SoapCommon/ClassmapTest.php new file mode 100644 index 0000000..55f5998 --- /dev/null +++ b/tests/BeSimple/Tests/SoapCommon/ClassmapTest.php @@ -0,0 +1,67 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\SoapCommon\Tests; + +use BeSimple\SoapCommon\Classmap; + +/** + * UnitTest for \BeSimple\SoapCommon\Classmap. + * + * @author Francis Besset + */ +class ClassmapTest extends \PHPUnit_Framework_TestCase +{ + public function testAll() + { + $classmap = new Classmap(); + + $this->assertSame(array(), $classmap->all()); + } + + public function testAdd() + { + $classmap = new Classmap(); + + $classmap->add('foobar', 'BeSimple\SoapCommon\Classmap'); + + $this->setExpectedException('InvalidArgumentException'); + $classmap->add('foobar', 'BeSimple\SoapCommon\Classmap'); + } + + public function testGet() + { + $classmap = new Classmap(); + + $this->setExpectedException('InvalidArgumentException'); + $classmap->get('foobar'); + + $classmap->add('foobar', 'BeSimple\SoapCommon\Classmap'); + $this->assertSame(array('foobar' => 'BeSimple\SoapCommon\Classmap'), $classmap->get('foobar')); + } + + public function testSet() + { + $classmap = new Classmap(); + + $classmap->add('foobar', 'BeSimple\SoapCommon\Tests\ClassmapTest'); + $classmap->add('foo', 'BeSimple\SoapCommon\Tests\Classmap'); + + $map = array( + 'foobar' => 'BeSimple\SoapCommon\Classmap', + 'barfoo' => 'BeSimple\SoapCommon\Tests\ClassmapTest', + ); + $classmap->set($map); + + $this->assertSame($map, $classmap->all()); + } +} \ No newline at end of file From 15123e9719703524dc465902a59f049a60017e95 Mon Sep 17 00:00:00 2001 From: Francis Besset Date: Sun, 9 Oct 2011 19:13:12 +0200 Subject: [PATCH 08/48] Added AbstractSoapBuilder --- .../SoapCommon/AbstractSoapBuilder.php | 179 ++++++++++++++++++ .../SoapCommon/AbstractSoapBuilderTest.php | 150 +++++++++++++++ .../Tests/SoapCommon/Fixtures/SoapBuilder.php | 9 + tests/bootstrap.php | 2 +- 4 files changed, 339 insertions(+), 1 deletion(-) create mode 100644 src/BeSimple/SoapCommon/AbstractSoapBuilder.php create mode 100644 tests/BeSimple/Tests/SoapCommon/AbstractSoapBuilderTest.php create mode 100644 tests/BeSimple/Tests/SoapCommon/Fixtures/SoapBuilder.php diff --git a/src/BeSimple/SoapCommon/AbstractSoapBuilder.php b/src/BeSimple/SoapCommon/AbstractSoapBuilder.php new file mode 100644 index 0000000..effa9a5 --- /dev/null +++ b/src/BeSimple/SoapCommon/AbstractSoapBuilder.php @@ -0,0 +1,179 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\SoapCommon; + +use BeSimple\SoapCommon\Cache; + +/** + * @author Christian Kerl + * @author Francis Besset + */ +abstract class AbstractSoapBuilder +{ + protected $wsdl; + protected $options; + + /** + * @return AbstractSoapBuilder + */ + static public function createWithDefaults() + { + $builder = new static(); + + $builder + ->withSoapVersion12() + ->withEncoding('UTF-8') + ->withSingleElementArrays() + ; + + return $builder; + } + + public function __construct() + { + $this->options = array( + 'features' => 0, + ); + } + + public function getWsdl() + { + return $this->wsdl; + } + + public function getOptions() + { + return $this->options; + } + + /** + * @return AbstractSoapBuilder + */ + public function withWsdl($wsdl) + { + $this->wsdl = $wsdl; + + return $this; + } + + /** + * @return AbstractSoapBuilder + */ + public function withSoapVersion11() + { + $this->options['soap_version'] = SOAP_1_1; + + return $this; + } + + /** + * @return AbstractSoapBuilder + */ + public function withSoapVersion12() + { + $this->options['soap_version'] = SOAP_1_2; + + return $this; + } + + public function withEncoding($encoding) + { + $this->options['encoding'] = $encoding; + + return $this; + } + + /** + * @return AbstractSoapBuilder + */ + public function withWsdlCacheNone() + { + $this->options['cache_wsdl'] = Cache::TYPE_NONE; + + return $this; + } + + /** + * @return AbstractSoapBuilder + */ + public function withWsdlCacheDisk() + { + $this->options['cache_wsdl'] = Cache::TYPE_DISK; + + return $this; + } + + /** + * @return AbstractSoapBuilder + */ + public function withWsdlCacheMemory() + { + $this->options['cache_wsdl'] = Cache::TYPE_MEMORY; + + return $this; + } + + /** + * @return AbstractSoapBuilder + */ + public function withWsdlCacheDiskAndMemory() + { + $this->options['cache_wsdl'] = Cache::TYPE_DISK_MEMORY; + + return $this; + } + + /** + * Enables the SOAP_SINGLE_ELEMENT_ARRAYS feature. + * If enabled arrays containing only one element will be passed as arrays otherwise the single element is extracted and directly passed. + * + * @return AbstractSoapBuilder + */ + public function withSingleElementArrays() + { + $this->options['features'] |= SOAP_SINGLE_ELEMENT_ARRAYS; + + return $this; + } + + /** + * Enables the SOAP_WAIT_ONE_WAY_CALLS feature. + * + * @return AbstractSoapBuilder + */ + public function withWaitOneWayCalls() + { + $this->options['features'] |= SOAP_WAIT_ONE_WAY_CALLS; + + return $this; + } + + /** + * Enables the SOAP_USE_XSI_ARRAY_TYPE feature. + * + * @return AbstractSoapBuilder + */ + public function withUseXsiArrayType() + { + $this->options['features'] |= SOAP_USE_XSI_ARRAY_TYPE; + + return $this; + } + + protected function validateWsdl() + { + if (null === $this->wsdl) { + throw new \InvalidArgumentException('The WSDL has to be configured!'); + } + } +} \ No newline at end of file diff --git a/tests/BeSimple/Tests/SoapCommon/AbstractSoapBuilderTest.php b/tests/BeSimple/Tests/SoapCommon/AbstractSoapBuilderTest.php new file mode 100644 index 0000000..f0c3938 --- /dev/null +++ b/tests/BeSimple/Tests/SoapCommon/AbstractSoapBuilderTest.php @@ -0,0 +1,150 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\Tests\SoapCommon\Soap; + +use BeSimple\Tests\SoapCommon\Fixtures\SoapBuilder; +use BeSimple\SoapCommon\Cache; + +class AbstractSoapBuilderTest extends \PHPUnit_Framework_TestCase +{ + private $defaultOptions = array( + 'features' => 0, + ); + + public function testContruct() + { + $options = $this + ->getSoapBuilder() + ->getOptions() + ; + + $this->assertEquals($this->mergeOptions(array()), $options); + } + + public function testWithWsdl() + { + $builder = $this->getSoapBuilder(); + $this->assertNull($builder->getWsdl()); + + $builder->withWsdl('http://myWsdl/?wsdl'); + $this->assertEquals('http://myWsdl/?wsdl', $builder->getWsdl()); + } + + public function testWithSoapVersion() + { + $builder = $this->getSoapBuilder(); + + $builder->withSoapVersion11(); + $this->assertEquals($this->mergeOptions(array('soap_version' => SOAP_1_1)), $builder->getOptions()); + + $builder->withSoapVersion12(); + $this->assertEquals($this->mergeOptions(array('soap_version' => SOAP_1_2)), $builder->getOptions()); + } + + public function testWithEncoding() + { + $builder = $this + ->getSoapBuilder() + ->withEncoding('ISO 8859-15') + ; + + $this->assertEquals($this->mergeOptions(array('encoding' => 'ISO 8859-15')), $builder->getOptions()); + } + + public function testWithWsdlCache() + { + $builder = $this->getSoapBuilder(); + + $builder->withWsdlCacheNone(); + $this->assertEquals($this->mergeOptions(array('cache_wsdl' => Cache::TYPE_NONE)), $builder->getOptions()); + + $builder->withWsdlCacheDisk(); + $this->assertEquals($this->mergeOptions(array('cache_wsdl' => Cache::TYPE_DISK)), $builder->getOptions()); + + $builder->withWsdlCacheMemory(); + $this->assertEquals($this->mergeOptions(array('cache_wsdl' => Cache::TYPE_MEMORY)), $builder->getOptions()); + + $builder->withWsdlCacheDiskAndMemory(); + $this->assertEquals($this->mergeOptions(array('cache_wsdl' => Cache::TYPE_DISK_MEMORY)), $builder->getOptions()); + } + + public function testWithSingleElementArrays() + { + $options = $this + ->getSoapBuilder() + ->withSingleElementArrays() + ->getOptions() + ; + + $this->assertEquals($this->mergeOptions(array('features' => SOAP_SINGLE_ELEMENT_ARRAYS)), $options); + } + + public function testWithWaitOneWayCalls() + { + $options = $this + ->getSoapBuilder() + ->withWaitOneWayCalls() + ->getOptions() + ; + + $this->assertEquals($this->mergeOptions(array('features' => SOAP_WAIT_ONE_WAY_CALLS)), $options); + } + + public function testWithUseXsiArrayType() + { + $options = $this + ->getSoapBuilder() + ->withUseXsiArrayType() + ->getOptions() + ; + + $this->assertEquals($this->mergeOptions(array('features' => SOAP_USE_XSI_ARRAY_TYPE)), $options); + } + + public function testFeatures() + { + $builder = $this->getSoapBuilder(); + $features = 0; + + $builder->withSingleElementArrays(); + $features |= SOAP_SINGLE_ELEMENT_ARRAYS; + $this->assertEquals($this->mergeOptions(array('features' => $features)), $builder->getOptions()); + + $builder->withWaitOneWayCalls(); + $features |= SOAP_WAIT_ONE_WAY_CALLS; + $this->assertEquals($this->mergeOptions(array('features' => $features)), $builder->getOptions()); + + $builder->withUseXsiArrayType(); + $features |= SOAP_USE_XSI_ARRAY_TYPE; + $this->assertEquals($this->mergeOptions(array('features' => $features)), $builder->getOptions()); + } + + public function testCreateWithDefaults() + { + $builder = SoapBuilder::createWithDefaults(); + + $this->assertInstanceOf('BeSimple\Tests\SoapCommon\Fixtures\SoapBuilder', $builder); + + $this->assertEquals($this->mergeOptions(array('soap_version' => SOAP_1_2, 'encoding' => 'UTF-8', 'features' => SOAP_SINGLE_ELEMENT_ARRAYS)), $builder->getOptions()); + } + + private function getSoapBuilder() + { + return new SoapBuilder(); + } + + private function mergeOptions(array $options) + { + return array_merge($this->defaultOptions, $options); + } +} \ No newline at end of file diff --git a/tests/BeSimple/Tests/SoapCommon/Fixtures/SoapBuilder.php b/tests/BeSimple/Tests/SoapCommon/Fixtures/SoapBuilder.php new file mode 100644 index 0000000..c3c5d5b --- /dev/null +++ b/tests/BeSimple/Tests/SoapCommon/Fixtures/SoapBuilder.php @@ -0,0 +1,9 @@ + Date: Sun, 9 Oct 2011 22:12:54 +0200 Subject: [PATCH 09/48] Added Classmap::addClassmap() method --- src/BeSimple/SoapCommon/Classmap.php | 7 +++++++ tests/BeSimple/Tests/SoapCommon/ClassmapTest.php | 14 ++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/BeSimple/SoapCommon/Classmap.php b/src/BeSimple/SoapCommon/Classmap.php index 3f0ece6..6f5fc4c 100644 --- a/src/BeSimple/SoapCommon/Classmap.php +++ b/src/BeSimple/SoapCommon/Classmap.php @@ -83,4 +83,11 @@ class Classmap { return isset($this->classmap[$type]); } + + public function addClassmap(Classmap $classmap) + { + foreach ($classmap->all() as $type => $classname) { + $this->add($type, $classname); + } + } } \ No newline at end of file diff --git a/tests/BeSimple/Tests/SoapCommon/ClassmapTest.php b/tests/BeSimple/Tests/SoapCommon/ClassmapTest.php index 55f5998..a4a97de 100644 --- a/tests/BeSimple/Tests/SoapCommon/ClassmapTest.php +++ b/tests/BeSimple/Tests/SoapCommon/ClassmapTest.php @@ -64,4 +64,18 @@ class ClassmapTest extends \PHPUnit_Framework_TestCase $this->assertSame($map, $classmap->all()); } + + public function testAddClassmap() + { + $classmap1 = new Classmap(); + $classmap2 = new Classmap(); + + $classmap2->add('foobar', 'BeSimple\SoapCommon\Classmap'); + $classmap1->addClassmap($classmap2); + + $this->assertEquals(array('foobar' => 'BeSimple\SoapCommon\Classmap'), $classmap1->all()); + + $this->setExpectedException('InvalidArgumentException'); + $classmap1->addClassmap($classmap2); + } } \ No newline at end of file From 676d5cb108a06cb479f106bf50559e445b1112be Mon Sep 17 00:00:00 2001 From: Francis Besset Date: Sun, 9 Oct 2011 22:17:39 +0200 Subject: [PATCH 10/48] [TypeConverterCollection] Added get, set, has and addCollection methods --- .../Converter/TypeConverterCollection.php | 62 ++++++++++++++----- .../Converter/TypeConverterCollectionTest.php | 28 +++++++++ 2 files changed, 76 insertions(+), 14 deletions(-) diff --git a/src/BeSimple/SoapCommon/Converter/TypeConverterCollection.php b/src/BeSimple/SoapCommon/Converter/TypeConverterCollection.php index 099d0eb..7f06142 100644 --- a/src/BeSimple/SoapCommon/Converter/TypeConverterCollection.php +++ b/src/BeSimple/SoapCommon/Converter/TypeConverterCollection.php @@ -17,16 +17,50 @@ namespace BeSimple\SoapCommon\Converter; */ class TypeConverterCollection { - private $typeConverters = array(); - - public function add(TypeConverterInterface $converter) - { - $this->typeConverters[] = $converter; - } + private $converters = array(); public function all() { - return $this->typeConverters; + return array_values($this->converters); + } + + public function get($namespace, $name) + { + if (!$this->has($namespace, $name)) { + throw new \InvalidArgumentException(sprintf('The converter "%s %s" does not exists', $namespace, $name)); + } + + return $this->converters[$namespace.':'.$name]; + } + + public function add(TypeConverterInterface $converter) + { + if ($this->has($converter->getTypeNamespace(), $converter->getTypeName())) { + throw new \InvalidArgumentException(sprintf('The converter "%s %s" already exists', $converter->getTypeNamespace(), $converter->getTypeName())); + } + + $this->converters[$converter->getTypeNamespace().':'.$converter->getTypeName()] = $converter; + } + + public function set(array $converters) + { + $this->converters = array(); + + foreach ($converters as $converter) { + $this->add($converter); + } + } + + public function has($namespace, $name) + { + return isset($this->converters[$namespace.':'.$name]); + } + + public function addCollection(TypeConverterCollection $converterCollection) + { + foreach ($converterCollection->all() as $converter) { + $this->add($converter); + } } /** @@ -36,15 +70,15 @@ class TypeConverterCollection { $typemap = array(); - foreach ($this->all() as $typeConverter) { + foreach ($this->converters as $converter) { $typemap[] = array( - 'type_name' => $typeConverter->getTypeName(), - 'type_ns' => $typeConverter->getTypeNamespace(), - 'from_xml' => function($input) use ($typeConverter) { - return $typeConverter->convertXmlToPhp($input); + 'type_name' => $converter->getTypeName(), + 'type_ns' => $converter->getTypeNamespace(), + 'from_xml' => function($input) use ($converter) { + return $converter->convertXmlToPhp($input); }, - 'to_xml' => function($input) use ($typeConverter) { - return $typeConverter->convertPhpToXml($input); + 'to_xml' => function($input) use ($converter) { + return $converter->convertPhpToXml($input); }, ); } diff --git a/tests/BeSimple/Tests/SoapCommon/Converter/TypeConverterCollectionTest.php b/tests/BeSimple/Tests/SoapCommon/Converter/TypeConverterCollectionTest.php index 19865f1..0a955cd 100644 --- a/tests/BeSimple/Tests/SoapCommon/Converter/TypeConverterCollectionTest.php +++ b/tests/BeSimple/Tests/SoapCommon/Converter/TypeConverterCollectionTest.php @@ -62,4 +62,32 @@ class TypeConverterCollectionTest extends \PHPUnit_Framework_TestCase $this->assertInstanceOf('Closure', $typemap[1]['from_xml']); $this->assertInstanceOf('Closure', $typemap[1]['to_xml']); } + + public function testSet() + { + $converters = new TypeConverterCollection(); + + $dateTimeTypeConverter = new DateTimeTypeConverter(); + $converters->add($dateTimeTypeConverter); + + $converter = array(new DateTypeConverter); + $converters->set($converter); + + $this->assertSame($converter, $converters->all()); + } + + public function testAddCollection() + { + $converters1 = new TypeConverterCollection(); + $converters2 = new TypeConverterCollection(); + + $dateTimeTypeConverter = new DateTimeTypeConverter(); + $converters2->add($dateTimeTypeConverter); + $converters1->addCollection($converters2); + + $this->assertSame(array($dateTimeTypeConverter), $converters1->all()); + + $this->setExpectedException('InvalidArgumentException'); + $converters1->addCollection($converters2); + } } \ No newline at end of file From c243957bdfd761876faa120fecb2712e49421c13 Mon Sep 17 00:00:00 2001 From: Francis Besset Date: Sun, 9 Oct 2011 23:53:58 +0200 Subject: [PATCH 11/48] Added typemap and classmap in AbstractSoapBuilder --- .../SoapCommon/AbstractSoapBuilder.php | 94 +++++++++++++++---- .../SoapCommon/AbstractSoapBuilderTest.php | 32 ++++--- 2 files changed, 93 insertions(+), 33 deletions(-) diff --git a/src/BeSimple/SoapCommon/AbstractSoapBuilder.php b/src/BeSimple/SoapCommon/AbstractSoapBuilder.php index effa9a5..b3bf31d 100644 --- a/src/BeSimple/SoapCommon/AbstractSoapBuilder.php +++ b/src/BeSimple/SoapCommon/AbstractSoapBuilder.php @@ -12,7 +12,8 @@ namespace BeSimple\SoapCommon; -use BeSimple\SoapCommon\Cache; +use BeSimple\SoapCommon\Converter\TypeConverterCollection; +use BeSimple\SoapCommon\Converter\TypeConverterInterface; /** * @author Christian Kerl @@ -21,7 +22,7 @@ use BeSimple\SoapCommon\Cache; abstract class AbstractSoapBuilder { protected $wsdl; - protected $options; + protected $soapOptions; /** * @return AbstractSoapBuilder @@ -30,19 +31,19 @@ abstract class AbstractSoapBuilder { $builder = new static(); - $builder + return $builder ->withSoapVersion12() ->withEncoding('UTF-8') ->withSingleElementArrays() ; - - return $builder; } public function __construct() { - $this->options = array( + $this->soapOptions = array( 'features' => 0, + 'classmap' => new Classmap(), + 'typemap' => new TypeConverterCollection(), ); } @@ -51,9 +52,14 @@ abstract class AbstractSoapBuilder return $this->wsdl; } - public function getOptions() + public function getSoapOptions() { - return $this->options; + $options = $this->soapOptions; + + $options['classmap'] = $this->soapOptions['classmap']->all(); + $options['typemap'] = $this->soapOptions['typemap']->getTypemap(); + + return $options; } /** @@ -71,7 +77,7 @@ abstract class AbstractSoapBuilder */ public function withSoapVersion11() { - $this->options['soap_version'] = SOAP_1_1; + $this->soapOptions['soap_version'] = SOAP_1_1; return $this; } @@ -81,14 +87,14 @@ abstract class AbstractSoapBuilder */ public function withSoapVersion12() { - $this->options['soap_version'] = SOAP_1_2; + $this->soapOptions['soap_version'] = SOAP_1_2; return $this; } public function withEncoding($encoding) { - $this->options['encoding'] = $encoding; + $this->soapOptions['encoding'] = $encoding; return $this; } @@ -98,7 +104,7 @@ abstract class AbstractSoapBuilder */ public function withWsdlCacheNone() { - $this->options['cache_wsdl'] = Cache::TYPE_NONE; + $this->soapOptions['cache_wsdl'] = Cache::TYPE_NONE; return $this; } @@ -108,7 +114,7 @@ abstract class AbstractSoapBuilder */ public function withWsdlCacheDisk() { - $this->options['cache_wsdl'] = Cache::TYPE_DISK; + $this->soapOptions['cache_wsdl'] = Cache::TYPE_DISK; return $this; } @@ -118,7 +124,7 @@ abstract class AbstractSoapBuilder */ public function withWsdlCacheMemory() { - $this->options['cache_wsdl'] = Cache::TYPE_MEMORY; + $this->soapOptions['cache_wsdl'] = Cache::TYPE_MEMORY; return $this; } @@ -128,7 +134,7 @@ abstract class AbstractSoapBuilder */ public function withWsdlCacheDiskAndMemory() { - $this->options['cache_wsdl'] = Cache::TYPE_DISK_MEMORY; + $this->soapOptions['cache_wsdl'] = Cache::TYPE_DISK_MEMORY; return $this; } @@ -141,7 +147,7 @@ abstract class AbstractSoapBuilder */ public function withSingleElementArrays() { - $this->options['features'] |= SOAP_SINGLE_ELEMENT_ARRAYS; + $this->soapOptions['features'] |= SOAP_SINGLE_ELEMENT_ARRAYS; return $this; } @@ -153,7 +159,7 @@ abstract class AbstractSoapBuilder */ public function withWaitOneWayCalls() { - $this->options['features'] |= SOAP_WAIT_ONE_WAY_CALLS; + $this->soapOptions['features'] |= SOAP_WAIT_ONE_WAY_CALLS; return $this; } @@ -165,7 +171,59 @@ abstract class AbstractSoapBuilder */ public function withUseXsiArrayType() { - $this->options['features'] |= SOAP_USE_XSI_ARRAY_TYPE; + $this->soapOptions['features'] |= SOAP_USE_XSI_ARRAY_TYPE; + + return $this; + } + + public function withTypeConverter(TypeConverterInterface $converter) + { + $this->soapOptions['typemap']->add($converter); + + return $this; + } + + public function withTypeConverters(TypeConverterCollection $converters, $merge = true) + { + if ($merge) { + $this->soapOptions['typemap']->addCollection($converters); + } else { + $this->soapOptions['typemap']->set($converters->all()); + } + + return $this; + } + + /** + * Adds a class mapping to the classmap. + * + * @param string $xmlType + * @param string $phpType + * + * @return AbstractSoapBuilder + */ + public function withClassMapping($xmlType, $phpType) + { + $this->options['classmap']->add($xmlType, $phpType); + + return $this; + } + + /** + * Sets the classmap. + * + * @param array $classmap The classmap. + * @param boolean $merge If true the given classmap is merged into the existing one, otherwise the existing one is overwritten. + * + * @return AbstractSoapBuilder + */ + public function withClassmap(Classmap $classmap, $merge = true) + { + if ($merge) { + $this->options['classmap']->addClassmap($classmap); + } else { + $this->options['classmap']->set($classmap->all()); + } return $this; } diff --git a/tests/BeSimple/Tests/SoapCommon/AbstractSoapBuilderTest.php b/tests/BeSimple/Tests/SoapCommon/AbstractSoapBuilderTest.php index f0c3938..ff7dc43 100644 --- a/tests/BeSimple/Tests/SoapCommon/AbstractSoapBuilderTest.php +++ b/tests/BeSimple/Tests/SoapCommon/AbstractSoapBuilderTest.php @@ -19,13 +19,15 @@ class AbstractSoapBuilderTest extends \PHPUnit_Framework_TestCase { private $defaultOptions = array( 'features' => 0, + 'classmap' => array(), + 'typemap' => array(), ); public function testContruct() { $options = $this ->getSoapBuilder() - ->getOptions() + ->getSoapOptions() ; $this->assertEquals($this->mergeOptions(array()), $options); @@ -45,10 +47,10 @@ class AbstractSoapBuilderTest extends \PHPUnit_Framework_TestCase $builder = $this->getSoapBuilder(); $builder->withSoapVersion11(); - $this->assertEquals($this->mergeOptions(array('soap_version' => SOAP_1_1)), $builder->getOptions()); + $this->assertEquals($this->mergeOptions(array('soap_version' => SOAP_1_1)), $builder->getSoapOptions()); $builder->withSoapVersion12(); - $this->assertEquals($this->mergeOptions(array('soap_version' => SOAP_1_2)), $builder->getOptions()); + $this->assertEquals($this->mergeOptions(array('soap_version' => SOAP_1_2)), $builder->getSoapOptions()); } public function testWithEncoding() @@ -58,7 +60,7 @@ class AbstractSoapBuilderTest extends \PHPUnit_Framework_TestCase ->withEncoding('ISO 8859-15') ; - $this->assertEquals($this->mergeOptions(array('encoding' => 'ISO 8859-15')), $builder->getOptions()); + $this->assertEquals($this->mergeOptions(array('encoding' => 'ISO 8859-15')), $builder->getSoapOptions()); } public function testWithWsdlCache() @@ -66,16 +68,16 @@ class AbstractSoapBuilderTest extends \PHPUnit_Framework_TestCase $builder = $this->getSoapBuilder(); $builder->withWsdlCacheNone(); - $this->assertEquals($this->mergeOptions(array('cache_wsdl' => Cache::TYPE_NONE)), $builder->getOptions()); + $this->assertEquals($this->mergeOptions(array('cache_wsdl' => Cache::TYPE_NONE)), $builder->getSoapOptions()); $builder->withWsdlCacheDisk(); - $this->assertEquals($this->mergeOptions(array('cache_wsdl' => Cache::TYPE_DISK)), $builder->getOptions()); + $this->assertEquals($this->mergeOptions(array('cache_wsdl' => Cache::TYPE_DISK)), $builder->getSoapOptions()); $builder->withWsdlCacheMemory(); - $this->assertEquals($this->mergeOptions(array('cache_wsdl' => Cache::TYPE_MEMORY)), $builder->getOptions()); + $this->assertEquals($this->mergeOptions(array('cache_wsdl' => Cache::TYPE_MEMORY)), $builder->getSoapOptions()); $builder->withWsdlCacheDiskAndMemory(); - $this->assertEquals($this->mergeOptions(array('cache_wsdl' => Cache::TYPE_DISK_MEMORY)), $builder->getOptions()); + $this->assertEquals($this->mergeOptions(array('cache_wsdl' => Cache::TYPE_DISK_MEMORY)), $builder->getSoapOptions()); } public function testWithSingleElementArrays() @@ -83,7 +85,7 @@ class AbstractSoapBuilderTest extends \PHPUnit_Framework_TestCase $options = $this ->getSoapBuilder() ->withSingleElementArrays() - ->getOptions() + ->getSoapOptions() ; $this->assertEquals($this->mergeOptions(array('features' => SOAP_SINGLE_ELEMENT_ARRAYS)), $options); @@ -94,7 +96,7 @@ class AbstractSoapBuilderTest extends \PHPUnit_Framework_TestCase $options = $this ->getSoapBuilder() ->withWaitOneWayCalls() - ->getOptions() + ->getSoapOptions() ; $this->assertEquals($this->mergeOptions(array('features' => SOAP_WAIT_ONE_WAY_CALLS)), $options); @@ -105,7 +107,7 @@ class AbstractSoapBuilderTest extends \PHPUnit_Framework_TestCase $options = $this ->getSoapBuilder() ->withUseXsiArrayType() - ->getOptions() + ->getSoapOptions() ; $this->assertEquals($this->mergeOptions(array('features' => SOAP_USE_XSI_ARRAY_TYPE)), $options); @@ -118,15 +120,15 @@ class AbstractSoapBuilderTest extends \PHPUnit_Framework_TestCase $builder->withSingleElementArrays(); $features |= SOAP_SINGLE_ELEMENT_ARRAYS; - $this->assertEquals($this->mergeOptions(array('features' => $features)), $builder->getOptions()); + $this->assertEquals($this->mergeOptions(array('features' => $features)), $builder->getSoapOptions()); $builder->withWaitOneWayCalls(); $features |= SOAP_WAIT_ONE_WAY_CALLS; - $this->assertEquals($this->mergeOptions(array('features' => $features)), $builder->getOptions()); + $this->assertEquals($this->mergeOptions(array('features' => $features)), $builder->getSoapOptions()); $builder->withUseXsiArrayType(); $features |= SOAP_USE_XSI_ARRAY_TYPE; - $this->assertEquals($this->mergeOptions(array('features' => $features)), $builder->getOptions()); + $this->assertEquals($this->mergeOptions(array('features' => $features)), $builder->getSoapOptions()); } public function testCreateWithDefaults() @@ -135,7 +137,7 @@ class AbstractSoapBuilderTest extends \PHPUnit_Framework_TestCase $this->assertInstanceOf('BeSimple\Tests\SoapCommon\Fixtures\SoapBuilder', $builder); - $this->assertEquals($this->mergeOptions(array('soap_version' => SOAP_1_2, 'encoding' => 'UTF-8', 'features' => SOAP_SINGLE_ELEMENT_ARRAYS)), $builder->getOptions()); + $this->assertEquals($this->mergeOptions(array('soap_version' => SOAP_1_2, 'encoding' => 'UTF-8', 'features' => SOAP_SINGLE_ELEMENT_ARRAYS)), $builder->getSoapOptions()); } private function getSoapBuilder() From 40eacb99e5b595cc560128c5e2b0b215df3c18c3 Mon Sep 17 00:00:00 2001 From: Francis Besset Date: Mon, 10 Oct 2011 20:13:16 +0200 Subject: [PATCH 12/48] Fixed typo --- src/BeSimple/SoapCommon/AbstractSoapBuilder.php | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/BeSimple/SoapCommon/AbstractSoapBuilder.php b/src/BeSimple/SoapCommon/AbstractSoapBuilder.php index b3bf31d..a607d53 100644 --- a/src/BeSimple/SoapCommon/AbstractSoapBuilder.php +++ b/src/BeSimple/SoapCommon/AbstractSoapBuilder.php @@ -22,7 +22,7 @@ use BeSimple\SoapCommon\Converter\TypeConverterInterface; abstract class AbstractSoapBuilder { protected $wsdl; - protected $soapOptions; + protected $soapOptions = array(); /** * @return AbstractSoapBuilder @@ -40,11 +40,9 @@ abstract class AbstractSoapBuilder public function __construct() { - $this->soapOptions = array( - 'features' => 0, - 'classmap' => new Classmap(), - 'typemap' => new TypeConverterCollection(), - ); + $this->soapOptions['features'] = 0; + $this->soapOptions['classmap'] = new Classmap(); + $this->soapOptions['typemap'] = new TypeConverterCollection(); } public function getWsdl() @@ -204,7 +202,7 @@ abstract class AbstractSoapBuilder */ public function withClassMapping($xmlType, $phpType) { - $this->options['classmap']->add($xmlType, $phpType); + $this->soapOptions['classmap']->add($xmlType, $phpType); return $this; } @@ -220,9 +218,9 @@ abstract class AbstractSoapBuilder public function withClassmap(Classmap $classmap, $merge = true) { if ($merge) { - $this->options['classmap']->addClassmap($classmap); + $this->soapOptions['classmap']->addClassmap($classmap); } else { - $this->options['classmap']->set($classmap->all()); + $this->soapOptions['classmap']->set($classmap->all()); } return $this; From 0f30119294c0b80834e4104b3ccf1287f5548101 Mon Sep 17 00:00:00 2001 From: Francis Besset Date: Mon, 10 Oct 2011 22:18:20 +0200 Subject: [PATCH 13/48] Added AbstractSoapBuilder::withWsdlCache() method --- src/BeSimple/SoapCommon/AbstractSoapBuilder.php | 11 +++++++++++ src/BeSimple/SoapCommon/Cache.php | 4 ++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/BeSimple/SoapCommon/AbstractSoapBuilder.php b/src/BeSimple/SoapCommon/AbstractSoapBuilder.php index a607d53..d9e4a78 100644 --- a/src/BeSimple/SoapCommon/AbstractSoapBuilder.php +++ b/src/BeSimple/SoapCommon/AbstractSoapBuilder.php @@ -97,6 +97,17 @@ abstract class AbstractSoapBuilder return $this; } + public function withWsdlCache($cache) + { + if (!in_array($cache, Cache::getTypes())) { + throw new \InvalidArgument(); + } + + $this->soapOptions['cache_wsdl'] = $cache; + + return $this; + } + /** * @return AbstractSoapBuilder */ diff --git a/src/BeSimple/SoapCommon/Cache.php b/src/BeSimple/SoapCommon/Cache.php index 7deecc7..ce0ef9d 100644 --- a/src/BeSimple/SoapCommon/Cache.php +++ b/src/BeSimple/SoapCommon/Cache.php @@ -16,7 +16,7 @@ namespace BeSimple\SoapCommon; * @author Francis Besset */ class Cache -{ +{ const DISABLED = 0; const ENABLED = 1; @@ -59,7 +59,7 @@ class Cache static public function setType($type) { if (!in_array($type, self::getTypes())) { - throw new \InvalidArgumentException(); + throw new \InvalidArgumentException('The cache type has to be either Cache::TYPE_NONE, Cache::TYPE_DISK, Cache::TYPE_MEMORY or Cache::TYPE_DISK_MEMORY'); } self::iniSet('soap.wsdl_cache', $type); From f95a5177b13de5c4b0fee06b7fb49a3c0032f305 Mon Sep 17 00:00:00 2001 From: Francis Besset Date: Tue, 11 Oct 2011 21:24:32 +0200 Subject: [PATCH 14/48] Fixed typo and added unit tests --- .../SoapCommon/AbstractSoapBuilder.php | 4 +- src/BeSimple/SoapCommon/Cache.php | 4 +- .../SoapCommon/AbstractSoapBuilderTest.php | 61 ++++++++++++++++++- tests/BeSimple/Tests/SoapCommon/CacheTest.php | 12 ++++ .../Tests/SoapCommon/ClassmapTest.php | 8 +-- 5 files changed, 80 insertions(+), 9 deletions(-) diff --git a/src/BeSimple/SoapCommon/AbstractSoapBuilder.php b/src/BeSimple/SoapCommon/AbstractSoapBuilder.php index d9e4a78..207893f 100644 --- a/src/BeSimple/SoapCommon/AbstractSoapBuilder.php +++ b/src/BeSimple/SoapCommon/AbstractSoapBuilder.php @@ -99,8 +99,8 @@ abstract class AbstractSoapBuilder public function withWsdlCache($cache) { - if (!in_array($cache, Cache::getTypes())) { - throw new \InvalidArgument(); + if (!in_array($cache, Cache::getTypes(), true)) { + throw new \InvalidArgumentException(); } $this->soapOptions['cache_wsdl'] = $cache; diff --git a/src/BeSimple/SoapCommon/Cache.php b/src/BeSimple/SoapCommon/Cache.php index ce0ef9d..547879f 100644 --- a/src/BeSimple/SoapCommon/Cache.php +++ b/src/BeSimple/SoapCommon/Cache.php @@ -44,7 +44,7 @@ class Cache static public function setEnabled($enabled) { - if (!in_array($enabled, array(self::ENABLED, self::DISABLED))) { + if (!in_array($enabled, array(self::ENABLED, self::DISABLED), true)) { throw new \InvalidArgumentException(); } @@ -58,7 +58,7 @@ class Cache static public function setType($type) { - if (!in_array($type, self::getTypes())) { + if (!in_array($type, self::getTypes(), true)) { throw new \InvalidArgumentException('The cache type has to be either Cache::TYPE_NONE, Cache::TYPE_DISK, Cache::TYPE_MEMORY or Cache::TYPE_DISK_MEMORY'); } diff --git a/tests/BeSimple/Tests/SoapCommon/AbstractSoapBuilderTest.php b/tests/BeSimple/Tests/SoapCommon/AbstractSoapBuilderTest.php index ff7dc43..d61fd9e 100644 --- a/tests/BeSimple/Tests/SoapCommon/AbstractSoapBuilderTest.php +++ b/tests/BeSimple/Tests/SoapCommon/AbstractSoapBuilderTest.php @@ -12,8 +12,12 @@ namespace BeSimple\Tests\SoapCommon\Soap; -use BeSimple\Tests\SoapCommon\Fixtures\SoapBuilder; use BeSimple\SoapCommon\Cache; +use BeSimple\SoapCommon\Classmap; +use BeSimple\SoapCommon\Converter\DateTimeTypeConverter; +use BeSimple\SoapCommon\Converter\DateTypeConverter; +use BeSimple\SoapCommon\Converter\TypeConverterCollection; +use BeSimple\Tests\SoapCommon\Fixtures\SoapBuilder; class AbstractSoapBuilderTest extends \PHPUnit_Framework_TestCase { @@ -67,6 +71,9 @@ class AbstractSoapBuilderTest extends \PHPUnit_Framework_TestCase { $builder = $this->getSoapBuilder(); + $builder->withWsdlCache(Cache::TYPE_DISK_MEMORY); + $this->assertEquals($this->mergeOptions(array('cache_wsdl' => Cache::TYPE_DISK_MEMORY)), $builder->getSoapOptions()); + $builder->withWsdlCacheNone(); $this->assertEquals($this->mergeOptions(array('cache_wsdl' => Cache::TYPE_NONE)), $builder->getSoapOptions()); @@ -80,6 +87,14 @@ class AbstractSoapBuilderTest extends \PHPUnit_Framework_TestCase $this->assertEquals($this->mergeOptions(array('cache_wsdl' => Cache::TYPE_DISK_MEMORY)), $builder->getSoapOptions()); } + public function testWithWsdlCacheBadValue() + { + $builder = $this->getSoapBuilder(); + + $this->setExpectedException('InvalidArgumentException'); + $builder->withWsdlCache('foo'); + } + public function testWithSingleElementArrays() { $options = $this @@ -131,6 +146,50 @@ class AbstractSoapBuilderTest extends \PHPUnit_Framework_TestCase $this->assertEquals($this->mergeOptions(array('features' => $features)), $builder->getSoapOptions()); } + public function testWithTypeConverters() + { + $builder = $this->getSoapBuilder(); + + $builder->withTypeConverter(new DateTypeConverter()); + $options = $builder->getSoapOptions(); + + $this->assertEquals(1, count($options['typemap'])); + + $converters = new TypeConverterCollection(); + $converters->add(new DateTimeTypeConverter()); + $builder->withTypeConverters($converters); + $options = $builder->getSoapOptions(); + + $this->assertEquals(2, count($options['typemap'])); + + $builder->withTypeConverters($converters, false); + $options = $builder->getSoapOptions(); + + $this->assertEquals(1, count($options['typemap'])); + } + + public function testClassmap() + { + $builder = $this->getSoapBuilder(); + + $builder->withClassMapping('foo', __CLASS__); + $options = $builder->getSoapOptions(); + + $this->assertEquals(1, count($options['classmap'])); + + $classmap = new Classmap(); + $classmap->add('bar', __CLASS__); + $builder->withClassmap($classmap); + $options = $builder->getSoapOptions(); + + $this->assertEquals(2, count($options['classmap'])); + + $builder->withClassmap($classmap, false); + $options = $builder->getSoapOptions(); + + $this->assertEquals(1, count($options['classmap'])); + } + public function testCreateWithDefaults() { $builder = SoapBuilder::createWithDefaults(); diff --git a/tests/BeSimple/Tests/SoapCommon/CacheTest.php b/tests/BeSimple/Tests/SoapCommon/CacheTest.php index f56f192..602f894 100644 --- a/tests/BeSimple/Tests/SoapCommon/CacheTest.php +++ b/tests/BeSimple/Tests/SoapCommon/CacheTest.php @@ -25,6 +25,12 @@ class SoapRequestTest extends \PHPUnit_Framework_TestCase $this->assertEquals(Cache::DISABLED, Cache::isEnabled()); } + public function testSetEnabledBadValue() + { + $this->setExpectedException('InvalidArgumentException'); + Cache::setEnabled('foo'); + } + public function testSetType() { Cache::setType(Cache::TYPE_DISK); @@ -34,6 +40,12 @@ class SoapRequestTest extends \PHPUnit_Framework_TestCase $this->assertEquals(Cache::TYPE_NONE, Cache::getType()); } + public function testSetTypeBadValue() + { + $this->setExpectedException('InvalidArgumentException'); + Cache::setType('foo'); + } + public function testSetDirectory() { \vfsStream::setup('Fixtures'); diff --git a/tests/BeSimple/Tests/SoapCommon/ClassmapTest.php b/tests/BeSimple/Tests/SoapCommon/ClassmapTest.php index a4a97de..2358244 100644 --- a/tests/BeSimple/Tests/SoapCommon/ClassmapTest.php +++ b/tests/BeSimple/Tests/SoapCommon/ClassmapTest.php @@ -42,11 +42,11 @@ class ClassmapTest extends \PHPUnit_Framework_TestCase { $classmap = new Classmap(); - $this->setExpectedException('InvalidArgumentException'); - $classmap->get('foobar'); - $classmap->add('foobar', 'BeSimple\SoapCommon\Classmap'); - $this->assertSame(array('foobar' => 'BeSimple\SoapCommon\Classmap'), $classmap->get('foobar')); + $this->assertSame('BeSimple\SoapCommon\Classmap', $classmap->get('foobar')); + + $this->setExpectedException('InvalidArgumentException'); + $classmap->get('bar'); } public function testSet() From 574ad003967853e43eb1512173c04e6759537d96 Mon Sep 17 00:00:00 2001 From: Andreas Schamberger Date: Sat, 22 Oct 2011 11:25:26 +0200 Subject: [PATCH 15/48] mime parser --- src/BeSimple/SoapCommon/Mime/MultiPart.php | 180 +++++++++++++++++++ src/BeSimple/SoapCommon/Mime/Parser.php | 183 ++++++++++++++++++++ src/BeSimple/SoapCommon/Mime/Part.php | 165 ++++++++++++++++++ src/BeSimple/SoapCommon/Mime/PartHeader.php | 114 ++++++++++++ 4 files changed, 642 insertions(+) create mode 100644 src/BeSimple/SoapCommon/Mime/MultiPart.php create mode 100644 src/BeSimple/SoapCommon/Mime/Parser.php create mode 100644 src/BeSimple/SoapCommon/Mime/Part.php create mode 100644 src/BeSimple/SoapCommon/Mime/PartHeader.php diff --git a/src/BeSimple/SoapCommon/Mime/MultiPart.php b/src/BeSimple/SoapCommon/Mime/MultiPart.php new file mode 100644 index 0000000..6267b77 --- /dev/null +++ b/src/BeSimple/SoapCommon/Mime/MultiPart.php @@ -0,0 +1,180 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\SoapCommon; + +/** + * Mime multi part container. + * + * Headers: + * - MIME-Version + * - Content-Type + * - Content-ID + * - Content-Location + * - Content-Description + * + * @author Andreas Schamberger + */ +class MultiPart extends PartHeader +{ + /** + * Content-ID of main part. + * + * @var string + */ + protected $mainPartContentId; + + /** + * Mime parts. + * + * @var array(\BeSimple\SoapCommon\Mime\Part) + */ + protected $parts = array(); + + /** + * Construct new mime object. + * + * @param string $boundary Boundary string + * @return void + */ + public function __construct($boundary = null) + { + $this->setHeader('MIME-Version', '1.0'); + $this->setHeader('Content-Type', 'multipart/related'); + $this->setHeader('Content-Type', 'type', 'text/xml'); + $this->setHeader('Content-Type', 'charset', 'utf-8'); + if (is_null($boundary)) { + $boundary = $this->generateBoundary(); + } + $this->setHeader('Content-Type', 'boundary', $boundary); + } + + /** + * Get mime message of this object (without headers). + * + * @param boolean $withHeaders Returned mime message contains headers + * @return string + */ + public function getMimeMessage($withHeaders = false) + { + $message = ($withHeaders === true) ? $this->generateHeaders() : ""; + // add parts + foreach ($this->parts as $part) { + $message .= "\r\n" . '--' . $this->getHeader('Content-Type', 'boundary') . "\r\n"; + $message .= $part->getMessagePart(); + } + $message .= "\r\n" . '--' . $this->getHeader('Content-Type', 'boundary') . '--'; + return $message; + } + + /** + * Get string array with MIME headers for usage in HTTP header (with CURL). + * + * @return arrray(string) + */ + public function getHeadersForHttp() + { + $allowed = array( + 'Content-Type', + 'Content-Description', + ); + $headers = array(); + foreach ($this->headers as $fieldName => $value) { + if (in_array($fieldName, $allowed)) { + $fieldValue = ''; + if (is_array($value)) { + if (isset($value['@'])) { + $fieldValue .= $value['@']; + } + foreach ($value as $subName => $subValue) { + if ($subName != '@') { + $fieldValue .= '; ' . $subName . '="' . $subValue . '"'; + } + } + } else { + $fieldValue .= $value; + } + // for http only ISO-8859-1 + $headers[] = $fieldName . ': '. iconv('utf-8', 'ISO-8859-1//TRANSLIT', $fieldValue); + } + } + return $headers; + } + + /** + * Add new part to MIME message. + * + * @param \BeSimple\SoapCommon\Mime\Part $part Part that is added + * @param boolean $isMain Is the given part the main part of mime message + * @return void + */ + public function addPart(Part $part, $isMain = false) + { + $contentId = trim($part->getHeader('Content-ID'), '<>'); + if ($isMain === true) { + $this->mainPartContentId = $contentId; + $this->setHeader('Content-Type', 'start', $part->getHeader('Content-ID')); + } + $this->parts[$contentId] = $part; + } + + /** + * Get part with given content id. If there is no content id given it + * returns the main part that is defined through the content-id start + * parameter. + * + * @param string $contentId Content id of desired part + * @return \BeSimple\SoapCommon\Mime\Part|null + */ + public function getPart($contentId = null) + { + if (is_null($contentId)) { + $contentId = $this->mainPartContentId; + } + if (isset($this->parts[$contentId])) { + return $this->parts[$contentId]; + } + return null; + } + + /** + * Get all parts. + * + * @param boolean $includeMainPart Should main part be in result set + * @return array(\BeSimple\SoapCommon\Mime\Part) + */ + public function getParts($includeMainPart = false) + { + if ($includeMainPart === true) { + $parts = $this->parts; + } else { + $parts = array(); + foreach ($this->parts as $cid => $part) { + if ($cid != $this->mainPartContentId) { + $parts[$cid] = $part; + } + } + } + return $parts; + } + + /** + * Returns a unique boundary string. + * + * @return string + */ + protected function generateBoundary() + { + // TODO + return 'urn:uuid:' . \ass\Soap\Helper::generateUUID(); + } +} \ No newline at end of file diff --git a/src/BeSimple/SoapCommon/Mime/Parser.php b/src/BeSimple/SoapCommon/Mime/Parser.php new file mode 100644 index 0000000..c7b815f --- /dev/null +++ b/src/BeSimple/SoapCommon/Mime/Parser.php @@ -0,0 +1,183 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\SoapCommon; + +/** + * Simple Multipart-Mime parser. + * + * @author Andreas Schamberger + */ +class Parser +{ + /** + * Parse the given Mime-Message and return a \BeSimple\SoapCommon\Mime\MultiPart object. + * + * @param string $mimeMessage Mime message string + * @param array(string=>string) $headers Array of header elements (e.g. coming from http request) + * @return \BeSimple\SoapCommon\Mime\MultiPart + */ + public static function parseMimeMessage($mimeMessage, array $headers = array()) + { + $boundary = null; + $start = null; + $multipart = new MultiPart(); + // add given headers, e.g. coming from HTTP headers + if (count($headers) > 0) { + foreach ($headers as $name => $value) { + if ($name == 'Content-Type') { + self::parseContentTypeHeader($multipart, $name, $value); + $boundary = $multipart->getHeader('Content-Type', 'boundary'); + $start = $multipart->getHeader('Content-Type', 'start'); + } else { + $multipart->setHeader($name, $value); + } + } + } + $hitFirstBoundary = false; + $inHeader = true; + $content = ''; + $currentPart = $multipart; + $lines = preg_split("/\r\n|\n/", $mimeMessage); + foreach ($lines as $line) { + // ignore http status code + if (substr($line, 0, 5) == 'HTTP/') { + continue; + } + if (isset($currentHeader)) { + if (isset($line[0]) && ($line[0] === ' ' || $line[0] === "\t")) { + $currentHeader .= $line; + continue; + } + list($headerName, $headerValue) = explode(':', $currentHeader, 2); + $headerValue = iconv_mime_decode($headerValue, 0, 'utf-8'); + if (strpos($headerValue, ';') !== false) { + self::parseContentTypeHeader($currentPart, $headerName, $headerValue); + $boundary = $multipart->getHeader('Content-Type', 'boundary'); + $start = $multipart->getHeader('Content-Type', 'start'); + } else { + $currentPart->setHeader($headerName, trim($headerValue)); + } + unset($currentHeader); + } + if ($inHeader) { + if ($line == '') { + $inHeader = false; + continue; + } + $currentHeader = $line; + continue; + } else { + // check if we hit any of the boundaries + if (strlen($line) > 0 && $line[0] == "-") { + if (strcmp(trim($line), '--' . $boundary) === 0) { + if ($currentPart instanceof Part) { + $content = iconv_substr($content, 0, -2, 'utf-8'); + self::decodeContent($currentPart, $content); + // check if there is a start parameter given, if not set first part + $isMain = (is_null($start) || $start == $currentPart->getHeader('Content-ID')) ? true : false; + if ($isMain === true) { + $start = $currentPart->getHeader('Content-ID'); + } + $multipart->addPart($currentPart, $isMain); + } + $currentPart = new Part(); + $hitFirstBoundary = true; + $inHeader = true; + $content = ''; + } elseif (strcmp(trim($line), '--' . $boundary . '--') === 0) { + $content = iconv_substr($content, 0, -2, 'utf-8'); + self::decodeContent($currentPart, $content); + // check if there is a start parameter given, if not set first part + $isMain = (is_null($start) || $start == $currentPart->getHeader('Content-ID')) ? true : false; + if ($isMain === true) { + $start = $currentPart->getHeader('Content-ID'); + } + $multipart->addPart($currentPart, $isMain); + $content = ''; + } + } else { + if ($hitFirstBoundary === false) { + if ($line != '') { + $inHeader = true; + $currentHeader = $line; + continue; + } + } + $content .= $line . "\r\n"; + } + } + + } + return $multipart; + } + + /** + * Parse a "Content-Type" header with multiple sub values. + * e.g. Content-Type: Multipart/Related; boundary=boundary; type=text/xml; + * start="<123@abc>" + * + * @see https://labs.omniti.com/alexandria/trunk/OmniTI/Mail/Parser.php + * + * @param \BeSimple\SoapCommon\Mime\PartHeader $part Header part + * @param string $headerName Header name + * @param string $headerValue Header value + * @return null + */ + private static function parseContentTypeHeader(PartHeader $part, $headerName, $headerValue) + { + list($value, $remainder) = explode(';', $headerValue, 2); + $value = trim($value); + $part->setHeader($headerName, $value); + $remainder = trim($remainder); + while (strlen($remainder) > 0) { + if (!preg_match('/^([a-zA-Z0-9_-]+)=(.)/', $remainder, $matches)) { + break; + } + $name = $matches[1]; + $delimiter = $matches[2]; + $pattern = '/(\S+)(\s|$)/'; + $remainder = substr($remainder, strlen($name)+1); + if (!preg_match($pattern, $remainder, $matches)) { + break; + } + $value = rtrim($matches[1], ';'); + if ($delimiter == '\'' || $delimiter == '"') { + $value = trim($value, $delimiter); + } + $part->setHeader($headerName, $name, $value); + $remainder = substr($remainder, strlen($matches[0])); + } + } + + /** + * Decodes the content of a Mime part. + * + * @param \BeSimple\SoapCommon\Mime\Part $part Part to add content + * @param string $content Content to decode + * @return null + */ + private static function decodeContent(Part $part, $content) + { + $encoding = strtolower($part->getHeader('Content-Transfer-Encoding')); + $charset = strtolower($part->getHeader('Content-Type', 'charset')); + if ($encoding == Part::ENCODING_BASE64) { + $content = base64_decode($content); + } elseif ($encoding == Part::ENCODING_QUOTED_PRINTABLE) { + $content = quoted_printable_decode($content); + } + if ($charset != 'utf-8') { + $content = iconv($charset, 'utf-8', $content); + } + $part->setContent($content); + } +} \ No newline at end of file diff --git a/src/BeSimple/SoapCommon/Mime/Part.php b/src/BeSimple/SoapCommon/Mime/Part.php new file mode 100644 index 0000000..45065ab --- /dev/null +++ b/src/BeSimple/SoapCommon/Mime/Part.php @@ -0,0 +1,165 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\SoapCommon; + +/** + * Mime part. Everything must be UTF-8. Default charset for text is UTF-8. + * + * Headers: + * - Content-Type + * - Content-Transfer-Encoding + * - Content-ID + * - Content-Location + * - Content-Description + * + * @author Andreas Schamberger + */ +class Part extends PartHeader +{ + /** + * Encoding type base 64 + */ + const ENCODING_BASE64 = 'base64'; + + /** + * Encoding type binary + */ + const ENCODING_BINARY = 'binary'; + + /** + * Encoding type eight bit + */ + const ENCODING_EIGHT_BIT = '8bit'; + + /** + * Encoding type seven bit + */ + const ENCODING_SEVEN_BIT = '7bit'; + + /** + * Encoding type quoted printable + */ + const ENCODING_QUOTED_PRINTABLE = 'quoted-printable'; + + /** + * Content. + * + * @var mixed + */ + protected $content; + + /** + * Construct new mime object. + * + * @param mixed $content Content + * @param string $contentType Content type + * @param string $charset Charset + * @param string $encoding Encoding + * @param string $contentId Content id + * @return void + */ + public function __construct($content = null, $contentType = 'application/octet-stream', $charset = null, $encoding = self::ENCODING_BINARY, $contentId = null) + { + $this->content = $content; + $this->setHeader('Content-Type', $contentType); + if (!is_null($charset)) { + $this->setHeader('Content-Type', 'charset', $charset); + } else { // if (substr($contentType, 0, 4) == 'text') { + $this->setHeader('Content-Type', 'charset', 'utf-8'); + } + $this->setHeader('Content-Transfer-Encoding', $encoding); + if (is_null($contentId)) { + $contentId = $this->generateContentId(); + } + $this->setHeader('Content-ID', '<' . $contentId . '>'); + } + + /** + * __toString. + * + * @return mixed + */ + public function __toString() + { + return $this->content; + } + + /** + * Get mime content. + * + * @return mixed + */ + public function getContent() + { + return $this->content; + } + + /** + * Set mime content. + * + * @param mixed $content Content to set + * @return void + */ + public function setContent($content) + { + $this->content = $content; + } + + /** + * Get complete mime message of this object. + * + * @return string + */ + public function getMessagePart() + { + return $this->generateHeaders() . "\r\n" . $this->generateBody(); + } + + /** + * Generate body. + * + * @return string + */ + protected function generateBody() + { + $encoding = strtolower($this->getHeader('Content-Transfer-Encoding')); + $charset = strtolower($this->getHeader('Content-Type', 'charset')); + if ($charset != 'utf-8') { + $content = iconv('utf-8', $charset . '//TRANSLIT', $this->content); + } else { + $content = $this->content; + } + switch ($encoding) { + case self::ENCODING_BASE64: + return substr(chunk_split(base64_encode($content), 76, "\r\n"), -2); + case self::ENCODING_QUOTED_PRINTABLE: + return quoted_printable_encode($content); + case self::ENCODING_BINARY: + case self::ENCODING_SEVEN_BIT: + case self::ENCODING_EIGHT_BIT: + default: + return preg_replace("/\r\n|\r|\n/", "\r\n", $content); + } + } + + /** + * Returns a unique ID to be used for the Content-ID header. + * + * @return string + */ + protected function generateContentId() + { + // TODO + return 'urn:uuid:' . \ass\Soap\Helper::generateUUID(); + } +} \ No newline at end of file diff --git a/src/BeSimple/SoapCommon/Mime/PartHeader.php b/src/BeSimple/SoapCommon/Mime/PartHeader.php new file mode 100644 index 0000000..4dc3d06 --- /dev/null +++ b/src/BeSimple/SoapCommon/Mime/PartHeader.php @@ -0,0 +1,114 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\SoapCommon; + +/** + * Mime part base class. + * + * @author Andreas Schamberger + */ +abstract class PartHeader +{ + /** + * Mime headers. + * + * @var array(string=>mixed|array(mixed)) + */ + protected $headers = array(); + + /** + * Add a new header to the mime part. + * + * @param string $name Header name + * @param string $value Header value + * @param string $subValue Is sub value? + * @return void + */ + public function setHeader($name, $value, $subValue = null) + { + if (isset($this->headers[$name]) && !is_null($subValue)) { + if (!is_array($this->headers[$name])) { + $this->headers[$name] = array( + '@' => $this->headers[$name], + $value => $subValue, + ); + } else { + $this->headers[$name][$value] = $subValue; + } + } elseif (isset($this->headers[$name]) && is_array($this->headers[$name]) && isset($this->headers[$name]['@'])) { + $this->headers[$name]['@'] = $value; + } else { + $this->headers[$name] = $value; + } + } + + /** + * Get given mime header. + * + * @param string $name Header name + * @param string $subValue Sub value name + * @return mixed|array(mixed) + */ + public function getHeader($name, $subValue = null) + { + if (isset($this->headers[$name])) { + if (!is_null($subValue)) { + if (is_array($this->headers[$name]) && isset($this->headers[$name][$subValue])) { + return $this->headers[$name][$subValue]; + } else { + return null; + } + } elseif (is_array($this->headers[$name]) && isset($this->headers[$name]['@'])) { + return $this->headers[$name]['@']; + } else { + return $this->headers[$name]; + } + } + return null; + } + + /** + * Generate headers. + * + * @return string + */ + protected function generateHeaders() + { + $charset = strtolower($this->getHeader('Content-Type', 'charset')); + $preferences = array( + 'scheme' => 'Q', + 'input-charset' => 'utf-8', + 'output-charset' => $charset, + ); + $headers = ''; + foreach ($this->headers as $fieldName => $value) { + $fieldValue = ''; + if (is_array($value)) { + if (isset($value['@'])) { + $fieldValue .= $value['@']; + } + foreach ($value as $subName => $subValue) { + if ($subName != '@') { + $fieldValue .= '; ' . $subName . '=' . $subValue; + } + } + } else { + $fieldValue .= $value; + } + // do not use proper encoding as Apache Axis does not understand this + // $headers .= iconv_mime_encode($field_name, $field_value, $preferences) . "\r\n"; + $headers .= $fieldName . ': ' . $fieldValue . "\r\n"; + } + return $headers; + } +} \ No newline at end of file From a1972a209cecff3d478a499dca9ca69d26c442b6 Mon Sep 17 00:00:00 2001 From: Andreas Schamberger Date: Sat, 22 Oct 2011 11:32:26 +0200 Subject: [PATCH 16/48] add helper class fix namespace --- src/BeSimple/SoapCommon/Helper.php | 194 ++++++++++++++++++++ src/BeSimple/SoapCommon/Mime/MultiPart.php | 7 +- src/BeSimple/SoapCommon/Mime/Parser.php | 2 +- src/BeSimple/SoapCommon/Mime/Part.php | 7 +- src/BeSimple/SoapCommon/Mime/PartHeader.php | 2 +- 5 files changed, 204 insertions(+), 8 deletions(-) create mode 100644 src/BeSimple/SoapCommon/Helper.php diff --git a/src/BeSimple/SoapCommon/Helper.php b/src/BeSimple/SoapCommon/Helper.php new file mode 100644 index 0000000..6d11b0f --- /dev/null +++ b/src/BeSimple/SoapCommon/Helper.php @@ -0,0 +1,194 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\SoapCommon; + +/** + * Soap helper class with static functions that are used in the client and + * server implementations. It also provides namespace and configuration + * constants. + * + * @author Andreas Schamberger + */ +class Helper +{ + /** + * Attachment type: xsd:base64Binary (native in ext/soap). + */ + const ATTACHMENTS_TYPE_BASE64 = 1; + + /** + * Attachment type: MTOM (SOAP Message Transmission Optimization Mechanism). + */ + const ATTACHMENTS_TYPE_MTOM = 2; + + /** + * Attachment type: SWA (SOAP Messages with Attachments). + */ + const ATTACHMENTS_TYPE_SWA = 4; + + /** + * Web Services Security: SOAP Message Security 1.0 (WS-Security 2004) + */ + const NAME_WSS_SMS = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0'; + + /** + * Web Services Security: SOAP Message Security 1.1 (WS-Security 2004) + */ + const NAME_WSS_SMS_1_1 = 'http://docs.oasis-open.org/wss/oasis-wss-soap-message-security-1.1'; + + /** + * Web Services Security UsernameToken Profile 1.0 + */ + const NAME_WSS_UTP = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0'; + + /** + * Web Services Security X.509 Certificate Token Profile + */ + const NAME_WSS_X509 = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0'; + + /** + * Soap 1.1 namespace. + */ + const NS_SOAP_1_1 = 'http://schemas.xmlsoap.org/soap/envelope/'; + + /** + * Soap 1.1 namespace. + */ + const NS_SOAP_1_2 = 'http://www.w3.org/2003/05/soap-envelope/'; + + /** + * Web Services Addressing 1.0 namespace. + */ + const NS_WSA = 'http://www.w3.org/2005/08/addressing'; + + /** + * Web Services Security Extension namespace. + */ + const NS_WSS = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd'; + + /** + * Web Services Security Utility namespace. + */ + const NS_WSU = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd'; + + /** + * Describing Media Content of Binary Data in XML namespace. + */ + const NS_XMLMIME = 'http://www.w3.org/2004/11/xmlmime'; + + /** + * XML Schema namespace. + */ + const NS_XML_SCHEMA = 'http://www.w3.org/2001/XMLSchema'; + + /** + * XML Schema instance namespace. + */ + const NS_XML_SCHEMA_INSTANCE = 'http://www.w3.org/2001/XMLSchema-instance'; + + /** + * XML-binary Optimized Packaging namespace. + */ + const NS_XOP = 'http://www.w3.org/2004/08/xop/include'; + + /** + * Web Services Addressing 1.0 prefix. + */ + const PFX_WSA = 'wsa'; + + /** + * Web Services Security Extension namespace. + */ + const PFX_WSS = 'wsse'; + + /** + * Web Services Security Utility namespace prefix. + */ + const PFX_WSU = 'wsu'; + + /** + * Describing Media Content of Binary Data in XML namespace prefix. + */ + const PFX_XMLMIME = 'xmlmime'; + + /** + * XML Schema namespace prefix. + */ + const PFX_XML_SCHEMA = 'xsd'; + + /** + * XML Schema instance namespace prefix. + */ + const PFX_XML_SCHEMA_INSTANCE = 'xsi'; + + /** + * XML-binary Optimized Packaging namespace prefix. + */ + const PFX_XOP = 'xop'; + + /** + * Generate a pseudo-random version 4 UUID. + * + * @see http://de.php.net/manual/en/function.uniqid.php#94959 + * @return string + */ + public static function generateUUID() + { + return sprintf( + '%04x%04x-%04x-%04x-%04x-%04x%04x%04x', + // 32 bits for "time_low" + mt_rand(0, 0xffff), mt_rand(0, 0xffff), + // 16 bits for "time_mid" + mt_rand(0, 0xffff), + // 16 bits for "time_hi_and_version", + // four most significant bits holds version number 4 + mt_rand(0, 0x0fff) | 0x4000, + // 16 bits, 8 bits for "clk_seq_hi_res", + // 8 bits for "clk_seq_low", + // two most significant bits holds zero and one for variant DCE1.1 + mt_rand(0, 0x3fff) | 0x8000, + // 48 bits for "node" + mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff) + ); + } + + /** + * Get SOAP namespace for the given $version. + * + * @param int $version SOAP_1_1|SOAP_1_2 + * @return string + */ + public static function getSoapNamespace($version) + { + if ($version === SOAP_1_2) { + return self::NS_SOAP_1_2; + } else { + return self::NS_SOAP_1_1; + } + } + + /** + * Get SOAP version from namespace URI. + * + * @param string $namespace NS_SOAP_1_1|NS_SOAP_1_2 + * @return int SOAP_1_1|SOAP_1_2 + */ + public static function getSoapVersionFromNamespace($namespace) + { + if ($namespace === self::NS_SOAP_1_2) { + return SOAP_1_2; + } else { + return SOAP_1_1; + } + } +} \ No newline at end of file diff --git a/src/BeSimple/SoapCommon/Mime/MultiPart.php b/src/BeSimple/SoapCommon/Mime/MultiPart.php index 6267b77..810b655 100644 --- a/src/BeSimple/SoapCommon/Mime/MultiPart.php +++ b/src/BeSimple/SoapCommon/Mime/MultiPart.php @@ -10,7 +10,9 @@ * with this source code in the file LICENSE. */ -namespace BeSimple\SoapCommon; +namespace BeSimple\SoapCommon\Mime; + +use BeSimple\SoapCommon\Helper; /** * Mime multi part container. @@ -174,7 +176,6 @@ class MultiPart extends PartHeader */ protected function generateBoundary() { - // TODO - return 'urn:uuid:' . \ass\Soap\Helper::generateUUID(); + return 'urn:uuid:' . Helper::generateUUID(); } } \ No newline at end of file diff --git a/src/BeSimple/SoapCommon/Mime/Parser.php b/src/BeSimple/SoapCommon/Mime/Parser.php index c7b815f..b5b78e0 100644 --- a/src/BeSimple/SoapCommon/Mime/Parser.php +++ b/src/BeSimple/SoapCommon/Mime/Parser.php @@ -10,7 +10,7 @@ * with this source code in the file LICENSE. */ -namespace BeSimple\SoapCommon; +namespace BeSimple\SoapCommon\Mime; /** * Simple Multipart-Mime parser. diff --git a/src/BeSimple/SoapCommon/Mime/Part.php b/src/BeSimple/SoapCommon/Mime/Part.php index 45065ab..88a2ca7 100644 --- a/src/BeSimple/SoapCommon/Mime/Part.php +++ b/src/BeSimple/SoapCommon/Mime/Part.php @@ -10,7 +10,9 @@ * with this source code in the file LICENSE. */ -namespace BeSimple\SoapCommon; +namespace BeSimple\SoapCommon\Mime; + +use BeSimple\SoapCommon\Helper; /** * Mime part. Everything must be UTF-8. Default charset for text is UTF-8. @@ -159,7 +161,6 @@ class Part extends PartHeader */ protected function generateContentId() { - // TODO - return 'urn:uuid:' . \ass\Soap\Helper::generateUUID(); + return 'urn:uuid:' . Helper::generateUUID(); } } \ No newline at end of file diff --git a/src/BeSimple/SoapCommon/Mime/PartHeader.php b/src/BeSimple/SoapCommon/Mime/PartHeader.php index 4dc3d06..fea2694 100644 --- a/src/BeSimple/SoapCommon/Mime/PartHeader.php +++ b/src/BeSimple/SoapCommon/Mime/PartHeader.php @@ -10,7 +10,7 @@ * with this source code in the file LICENSE. */ -namespace BeSimple\SoapCommon; +namespace BeSimple\SoapCommon\Mime; /** * Mime part base class. From 4becca0208cbbc35050d408d107f646aa76d3b15 Mon Sep 17 00:00:00 2001 From: Andreas Schamberger Date: Mon, 31 Oct 2011 11:54:41 +0100 Subject: [PATCH 17/48] fixed parsing of mime headers --- src/BeSimple/SoapCommon/Mime/Parser.php | 32 +++++++++++++------------ 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/BeSimple/SoapCommon/Mime/Parser.php b/src/BeSimple/SoapCommon/Mime/Parser.php index b5b78e0..7d98edf 100644 --- a/src/BeSimple/SoapCommon/Mime/Parser.php +++ b/src/BeSimple/SoapCommon/Mime/Parser.php @@ -49,8 +49,9 @@ class Parser $currentPart = $multipart; $lines = preg_split("/\r\n|\n/", $mimeMessage); foreach ($lines as $line) { - // ignore http status code - if (substr($line, 0, 5) == 'HTTP/') { + // ignore http status code and POST * + if ( substr( $line, 0, 5 ) == 'HTTP/' || substr( $line, 0, 4 ) == 'POST') + { continue; } if (isset($currentHeader)) { @@ -58,14 +59,16 @@ class Parser $currentHeader .= $line; continue; } - list($headerName, $headerValue) = explode(':', $currentHeader, 2); - $headerValue = iconv_mime_decode($headerValue, 0, 'utf-8'); - if (strpos($headerValue, ';') !== false) { - self::parseContentTypeHeader($currentPart, $headerName, $headerValue); - $boundary = $multipart->getHeader('Content-Type', 'boundary'); - $start = $multipart->getHeader('Content-Type', 'start'); - } else { - $currentPart->setHeader($headerName, trim($headerValue)); + if (strpos($currentHeader,':') !== false) { + list($headerName, $headerValue) = explode(':', $currentHeader, 2); + $headerValue = iconv_mime_decode($headerValue, 0, 'utf-8'); + if (strpos($headerValue, ';') !== false) { + self::parseContentTypeHeader($currentPart, $headerName, $headerValue); + $boundary = $multipart->getHeader('Content-Type', 'boundary'); + $start = $multipart->getHeader('Content-Type', 'start'); + } else { + $currentPart->setHeader($headerName, trim($headerValue)); + } } unset($currentHeader); } @@ -123,7 +126,7 @@ class Parser /** * Parse a "Content-Type" header with multiple sub values. - * e.g. Content-Type: Multipart/Related; boundary=boundary; type=text/xml; + * e.g. Content-Type: multipart/related; boundary=boundary; type=text/xml; * start="<123@abc>" * * @see https://labs.omniti.com/alexandria/trunk/OmniTI/Mail/Parser.php @@ -140,18 +143,17 @@ class Parser $part->setHeader($headerName, $value); $remainder = trim($remainder); while (strlen($remainder) > 0) { - if (!preg_match('/^([a-zA-Z0-9_-]+)=(.)/', $remainder, $matches)) { + if (!preg_match('/^([a-zA-Z0-9_-]+)=(.{1})/', $remainder, $matches)) { break; } $name = $matches[1]; $delimiter = $matches[2]; - $pattern = '/(\S+)(\s|$)/'; $remainder = substr($remainder, strlen($name)+1); - if (!preg_match($pattern, $remainder, $matches)) { + if (!preg_match('/([^;]+)(;)?(\s|$)?/', $remainder, $matches)) { break; } $value = rtrim($matches[1], ';'); - if ($delimiter == '\'' || $delimiter == '"') { + if ($delimiter == "'" || $delimiter == '"') { $value = trim($value, $delimiter); } $part->setHeader($headerName, $name, $value); From e0049c906d6ca7c15c15a659206e3815dbd592aa Mon Sep 17 00:00:00 2001 From: Andreas Schamberger Date: Mon, 31 Oct 2011 11:55:14 +0100 Subject: [PATCH 18/48] unit tests --- .gitignore | 4 + src/BeSimple/SoapCommon/Mime/MultiPart.php | 15 +- src/BeSimple/SoapCommon/Mime/PartHeader.php | 54 +++++-- .../SoapCommon/Fixtures/MimePartHeader.php | 9 ++ .../Fixtures/SwA-response-amazon.txt | 29 ++++ .../SoapCommon/Fixtures/SwA-response-axis.txt | 13 ++ .../SoapCommon/Fixtures/WS-I-MTOM-request.txt | 22 +++ .../Fixtures/WS-I-MTOM-request_noheader.txt | 16 ++ .../Fixtures/WS-I-MTOM-response.txt | 17 ++ .../Tests/SoapCommon/Mime/MultiPartTest.php | 144 +++++++++++++++++ .../Tests/SoapCommon/Mime/ParserTest.php | 153 ++++++++++++++++++ .../Tests/SoapCommon/Mime/PartHeaderTest.php | 57 +++++++ .../Tests/SoapCommon/Mime/PartTest.php | 62 +++++++ 13 files changed, 569 insertions(+), 26 deletions(-) create mode 100644 tests/BeSimple/Tests/SoapCommon/Fixtures/MimePartHeader.php create mode 100644 tests/BeSimple/Tests/SoapCommon/Fixtures/SwA-response-amazon.txt create mode 100644 tests/BeSimple/Tests/SoapCommon/Fixtures/SwA-response-axis.txt create mode 100644 tests/BeSimple/Tests/SoapCommon/Fixtures/WS-I-MTOM-request.txt create mode 100644 tests/BeSimple/Tests/SoapCommon/Fixtures/WS-I-MTOM-request_noheader.txt create mode 100644 tests/BeSimple/Tests/SoapCommon/Fixtures/WS-I-MTOM-response.txt create mode 100644 tests/BeSimple/Tests/SoapCommon/Mime/MultiPartTest.php create mode 100644 tests/BeSimple/Tests/SoapCommon/Mime/ParserTest.php create mode 100644 tests/BeSimple/Tests/SoapCommon/Mime/PartHeaderTest.php create mode 100644 tests/BeSimple/Tests/SoapCommon/Mime/PartTest.php diff --git a/.gitignore b/.gitignore index 22d0d82..f8d178e 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,5 @@ vendor +/phpunit.xml +.buildpath +.project +.settings \ No newline at end of file diff --git a/src/BeSimple/SoapCommon/Mime/MultiPart.php b/src/BeSimple/SoapCommon/Mime/MultiPart.php index 810b655..1a3b7e1 100644 --- a/src/BeSimple/SoapCommon/Mime/MultiPart.php +++ b/src/BeSimple/SoapCommon/Mime/MultiPart.php @@ -80,6 +80,7 @@ class MultiPart extends PartHeader /** * Get string array with MIME headers for usage in HTTP header (with CURL). + * Only 'Content-Type' and 'Content-Description' headers are returned. * * @return arrray(string) */ @@ -92,19 +93,7 @@ class MultiPart extends PartHeader $headers = array(); foreach ($this->headers as $fieldName => $value) { if (in_array($fieldName, $allowed)) { - $fieldValue = ''; - if (is_array($value)) { - if (isset($value['@'])) { - $fieldValue .= $value['@']; - } - foreach ($value as $subName => $subValue) { - if ($subName != '@') { - $fieldValue .= '; ' . $subName . '="' . $subValue . '"'; - } - } - } else { - $fieldValue .= $value; - } + $fieldValue = $this->generateHeaderFieldValue($value); // for http only ISO-8859-1 $headers[] = $fieldName . ': '. iconv('utf-8', 'ISO-8859-1//TRANSLIT', $fieldValue); } diff --git a/src/BeSimple/SoapCommon/Mime/PartHeader.php b/src/BeSimple/SoapCommon/Mime/PartHeader.php index fea2694..b6458cd 100644 --- a/src/BeSimple/SoapCommon/Mime/PartHeader.php +++ b/src/BeSimple/SoapCommon/Mime/PartHeader.php @@ -92,23 +92,51 @@ abstract class PartHeader ); $headers = ''; foreach ($this->headers as $fieldName => $value) { - $fieldValue = ''; - if (is_array($value)) { - if (isset($value['@'])) { - $fieldValue .= $value['@']; - } - foreach ($value as $subName => $subValue) { - if ($subName != '@') { - $fieldValue .= '; ' . $subName . '=' . $subValue; - } - } - } else { - $fieldValue .= $value; - } + $fieldValue = $this->generateHeaderFieldValue($value); // do not use proper encoding as Apache Axis does not understand this // $headers .= iconv_mime_encode($field_name, $field_value, $preferences) . "\r\n"; $headers .= $fieldName . ': ' . $fieldValue . "\r\n"; } return $headers; } + + /** + * Generates a header field value from the given value paramater. + * + * @param array(string=>string)|string $value + * @return string + */ + protected function generateHeaderFieldValue($value) + { + $fieldValue = ''; + if (is_array($value)) { + if (isset($value['@'])) { + $fieldValue .= $value['@']; + } + foreach ($value as $subName => $subValue) { + if ($subName != '@') { + $fieldValue .= '; ' . $subName . '=' . $this->quoteValueString($subValue); + } + } + } else { + $fieldValue .= $value; + } + return $fieldValue; + } + + /** + * Quote string with '"' if it contains one of the special characters: + * "(" / ")" / "<" / ">" / "@" / "," / ";" / ":" / "\" / <"> / "/" / "[" / "]" / "?" / "=" + * + * @param string $string + * @return string + */ + private function quoteValueString($string) + { + if (preg_match('~[()<>@,;:\\"/\[\]?=]~', $string)) { + return '"' . $string . '"'; + } else { + return $string; + } + } } \ No newline at end of file diff --git a/tests/BeSimple/Tests/SoapCommon/Fixtures/MimePartHeader.php b/tests/BeSimple/Tests/SoapCommon/Fixtures/MimePartHeader.php new file mode 100644 index 0000000..2ecd558 --- /dev/null +++ b/tests/BeSimple/Tests/SoapCommon/Fixtures/MimePartHeader.php @@ -0,0 +1,9 @@ + +--xxx-MIME-Boundary-xxx-0xa36cb38-0a36cb38-xxx-END-xxx +Content-ID: <0x9d6ad00-0xa19ef48-0x9de7500-0xa4fae78-0xa382698> +Content-Type: application/binary + + + + +--xxx-MIME-Boundary-xxx-0xa36cb38-0a36cb38-xxx-END-xxx-- diff --git a/tests/BeSimple/Tests/SoapCommon/Fixtures/SwA-response-axis.txt b/tests/BeSimple/Tests/SoapCommon/Fixtures/SwA-response-axis.txt new file mode 100644 index 0000000..58cda6f --- /dev/null +++ b/tests/BeSimple/Tests/SoapCommon/Fixtures/SwA-response-axis.txt @@ -0,0 +1,13 @@ +HTTP/1.1 200 OK +Date: Sat, 11 Sep 2010 12:52:57 GMT +Server: Simple-Server/1.1 +Transfer-Encoding: chunked +Content-Type: multipart/related; boundary=MIMEBoundaryurn_uuid_2DB7ABF3DC5BED7FA51284209577582; type="application/soap+xml"; start="<0.urn:uuid:2DB7ABF3DC5BED7FA51284209577583@apache.org>"; action="urn:getVersionResponse" + +--MIMEBoundaryurn_uuid_2DB7ABF3DC5BED7FA51284209577582 +Content-Type: application/soap+xml; charset=utf-8 +Content-Transfer-Encoding: 8bit +Content-ID: <0.urn:uuid:2DB7ABF3DC5BED7FA51284209577583@apache.org> + +urn:getVersionResponseuuid:665aab53-4cef-4435-8934-4c10ed24fd42Hi - the Axis2 version is 1.5.1 +--MIMEBoundaryurn_uuid_2DB7ABF3DC5BED7FA51284209577582-- \ No newline at end of file diff --git a/tests/BeSimple/Tests/SoapCommon/Fixtures/WS-I-MTOM-request.txt b/tests/BeSimple/Tests/SoapCommon/Fixtures/WS-I-MTOM-request.txt new file mode 100644 index 0000000..efe9192 --- /dev/null +++ b/tests/BeSimple/Tests/SoapCommon/Fixtures/WS-I-MTOM-request.txt @@ -0,0 +1,22 @@ +POST http://131.107.72.15/Mtom/svc/service.svc/Soap12MtomUTF8 HTTP/1.1 +Content-Type: multipart/related; type="application/xop+xml";start="";boundary="uuid:0ca0e16e-feb1-426c-97d8-c4508ada5e82+id=7";start-info="application/soap+xml" +Host: 131.107.72.15 +Content-Length: 1941 +Expect: 100-continue +HTTP/1.1 100 Continue + +--uuid:0ca0e16e-feb1-426c-97d8-c4508ada5e82+id=7 +Content-ID: +Content-Transfer-Encoding: 8bit +Content-Type: application/xop+xml;charset=utf-8;type="application/soap+xml" + +http://xmlsoap.org/echoBinaryAsStringurn:uuid:1bf061d6-d532-4b0c-930b-8b8202c38b8ahttp://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymoushttp://131.107.72.15/Mtom/svc/service.svc/Soap12MtomUTF8 + +--uuid:0ca0e16e-feb1-426c-97d8-c4508ada5e82+id=7 +Content-ID: +Content-Transfer-Encoding: binary +Content-Type: application/octet-stream + +H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! + +--uuid:0ca0e16e-feb1-426c-97d8-c4508ada5e82+id=7-- diff --git a/tests/BeSimple/Tests/SoapCommon/Fixtures/WS-I-MTOM-request_noheader.txt b/tests/BeSimple/Tests/SoapCommon/Fixtures/WS-I-MTOM-request_noheader.txt new file mode 100644 index 0000000..f6d9942 --- /dev/null +++ b/tests/BeSimple/Tests/SoapCommon/Fixtures/WS-I-MTOM-request_noheader.txt @@ -0,0 +1,16 @@ + +--uuid:0ca0e16e-feb1-426c-97d8-c4508ada5e82+id=7 +Content-ID: +Content-Transfer-Encoding: 8bit +Content-Type: application/xop+xml;charset=utf-8;type="application/soap+xml" + +http://xmlsoap.org/echoBinaryAsStringurn:uuid:1bf061d6-d532-4b0c-930b-8b8202c38b8ahttp://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymoushttp://131.107.72.15/Mtom/svc/service.svc/Soap12MtomUTF8 + +--uuid:0ca0e16e-feb1-426c-97d8-c4508ada5e82+id=7 +Content-ID: +Content-Transfer-Encoding: binary +Content-Type: application/octet-stream + +H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! + +--uuid:0ca0e16e-feb1-426c-97d8-c4508ada5e82+id=7-- diff --git a/tests/BeSimple/Tests/SoapCommon/Fixtures/WS-I-MTOM-response.txt b/tests/BeSimple/Tests/SoapCommon/Fixtures/WS-I-MTOM-response.txt new file mode 100644 index 0000000..7a76cb4 --- /dev/null +++ b/tests/BeSimple/Tests/SoapCommon/Fixtures/WS-I-MTOM-response.txt @@ -0,0 +1,17 @@ +HTTP/1.1 200 OK +Proxy-Connection: Keep-Alive +Connection: Keep-Alive +Content-Length: 1166 +Via: 1.1 RED-PRXY-03 +Date: Fri, 09 Sep 2005 06:57:22 GMT +Content-Type: multipart/related; type="application/xop+xml";start="";boundary="uuid:b71dc628-ec8f-4422-8a4a-992f041cb94c+id=46";start-info="application/soap+xml" + + + +--uuid:b71dc628-ec8f-4422-8a4a-992f041cb94c+id=46 +Content-ID: +Content-Transfer-Encoding: 8bit +Content-Type: application/xop+xml;charset=utf-8;type="application/soap+xml" + +*urn:uuid:1bf061d6-d532-4b0c-930b-8b8202c38b8ahttp://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousHello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World! +--uuid:b71dc628-ec8f-4422-8a4a-992f041cb94c+id=46-- diff --git a/tests/BeSimple/Tests/SoapCommon/Mime/MultiPartTest.php b/tests/BeSimple/Tests/SoapCommon/Mime/MultiPartTest.php new file mode 100644 index 0000000..1d1ab16 --- /dev/null +++ b/tests/BeSimple/Tests/SoapCommon/Mime/MultiPartTest.php @@ -0,0 +1,144 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\Tests\SoapCommon\Soap; + +use BeSimple\SoapCommon\Mime\MultiPart; +use BeSimple\SoapCommon\Mime\Part; +use BeSimple\SoapCommon\Mime\PartHeader; + +class MultiPartTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $mp = new MultiPart(); + + $this->assertEquals('1.0', $mp->getHeader('MIME-Version')); + $this->assertEquals('multipart/related', $mp->getHeader('Content-Type')); + $this->assertEquals('text/xml', $mp->getHeader('Content-Type', 'type')); + $this->assertEquals('utf-8', $mp->getHeader('Content-Type', 'charset')); + $this->assertRegExp('~urn:uuid:.*~', $mp->getHeader('Content-Type', 'boundary')); + } + + public function testGetMimeMessage() + { + $mp = new MultiPart(); + + /* + string(51) " + --urn:uuid:a81ca327-591e-4656-91a1-8f177ada95b0--" + */ + $this->assertEquals(51, strlen($mp->getMimeMessage())); + + $p = new Part('test'); + $mp->addPart($p, true); + + /* + string(259) " + --urn:uuid:a81ca327-591e-4656-91a1-8f177ada95b0 + Content-Type: application/octet-stream; charset=utf-8 + Content-Transfer-Encoding: binary + Content-ID: + + test + --urn:uuid:a81ca327-591e-4656-91a1-8f177ada95b0--" + */ + $this->assertEquals(259, strlen($mp->getMimeMessage())); + } + + public function testGetMimeMessageWithHeaders() + { + $mp = new MultiPart(); + + /* + string(189) "MIME-Version: 1.0 + Content-Type: multipart/related; type="text/xml"; charset=utf-8; boundary="urn:uuid:231833e2-a23b-410a-862e-250524fc38f6" + + --urn:uuid:231833e2-a23b-410a-862e-250524fc38f6--" + */ + $this->assertEquals(193, strlen($mp->getMimeMessage(true))); + + $p = new Part('test'); + $mp->addPart($p, true); + + /* + string(452) "MIME-Version: 1.0 + Content-Type: multipart/related; type="text/xml"; charset=utf-8; boundary="urn:uuid:231833e2-a23b-410a-862e-250524fc38f6"; start="" + + --urn:uuid:231833e2-a23b-410a-862e-250524fc38f6 + Content-Type: application/octet-stream; charset=utf-8 + Content-Transfer-Encoding: binary + Content-ID: + + test + --urn:uuid:231833e2-a23b-410a-862e-250524fc38f6--" + */ + $this->assertEquals(458, strlen($mp->getMimeMessage(true))); + } + + public function testGetHeadersForHttp() + { + $mp = new MultiPart(); + + $result = array( + 'Content-Type: multipart/related; type="text/xml"; charset=utf-8; boundary="' . $mp->getHeader('Content-Type', 'boundary') . '"', + ); + $this->assertEquals($result, $mp->getHeadersForHttp()); + + $result = array( + 'Content-Type: multipart/related; type="text/xml"; charset=utf-8; boundary="' . $mp->getHeader('Content-Type', 'boundary') . '"', + 'Content-Description: test', + ); + $mp->setHeader('Content-Description', 'test'); + $this->assertEquals($result, $mp->getHeadersForHttp()); + } + + public function testAddGetPart() + { + $mp = new MultiPart(); + + $p = new Part('test'); + $p->setHeader('Content-ID', 'mycontentid'); + $mp->addPart($p); + $this->assertEquals($p, $mp->getPart('mycontentid')); + } + + public function testAddGetPartWithMain() + { + $mp = new MultiPart(); + + $p = new Part('test'); + $mp->addPart($p, true); + $this->assertEquals($p, $mp->getPart()); + } + + public function testGetParts() + { + $mp = new MultiPart(); + + $p1 = new Part('test'); + $mp->addPart($p1, true); + $p2 = new Part('test'); + $mp->addPart($p2); + + $withoutMain = array( + trim($p2->getHeader('Content-ID'),'<>') => $p2, + ); + $this->assertEquals($withoutMain, $mp->getParts()); + + $withMain = array( + trim($p1->getHeader('Content-ID'),'<>') => $p1, + trim($p2->getHeader('Content-ID'),'<>') => $p2, + ); + $this->assertEquals($withMain, $mp->getParts(true)); + } +} \ No newline at end of file diff --git a/tests/BeSimple/Tests/SoapCommon/Mime/ParserTest.php b/tests/BeSimple/Tests/SoapCommon/Mime/ParserTest.php new file mode 100644 index 0000000..8efa1f9 --- /dev/null +++ b/tests/BeSimple/Tests/SoapCommon/Mime/ParserTest.php @@ -0,0 +1,153 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\Tests\SoapCommon\Soap; + +use BeSimple\SoapCommon\Mime\MultiPart; +use BeSimple\SoapCommon\Mime\Parser; +use BeSimple\SoapCommon\Mime\Part; +use BeSimple\SoapCommon\Mime\PartHeader; + +class ParserTest extends \PHPUnit_Framework_TestCase +{ + public function testParserRequestWsi() + { + $filename = dirname(__DIR__).DIRECTORY_SEPARATOR.'Fixtures/WS-I-MTOM-request.txt'; + $mimeMessage = file_get_contents($filename); + + $mp = Parser::parseMimeMessage($mimeMessage); + $this->assertsForWsiMtomRequest($mp); + } + + public function testParserResponseAmazon() + { + $filename = dirname(__DIR__).DIRECTORY_SEPARATOR.'Fixtures/SwA-response-amazon.txt'; + $mimeMessage = file_get_contents($filename); + + $mp = Parser::parseMimeMessage($mimeMessage); + $this->assertEquals('Fri, 12 Feb 2010 15:46:00 GMT', $mp->getHeader('Date')); + $this->assertEquals('Server', $mp->getHeader('Server')); + $this->assertEquals('1.0', $mp->getHeader('MIME-Version')); + $this->assertEquals('close', $mp->getHeader('Cneonction')); + $this->assertEquals('chunked', $mp->getHeader('Transfer-Encoding')); + $this->assertEquals('multipart/related', $mp->getHeader('Content-Type')); + $this->assertEquals('text/xml', $mp->getHeader('Content-Type', 'type')); + $this->assertEquals('utf-8', $mp->getHeader('Content-Type', 'charset')); + $this->assertEquals('xxx-MIME-Boundary-xxx-0xa36cb38-0a36cb38-xxx-END-xxx', $mp->getHeader('Content-Type', 'boundary')); + + $p1 = $mp->getPart(); + $this->assertEquals('text/xml', $p1->getHeader('Content-Type')); + $this->assertEquals('UTF-8', $p1->getHeader('Content-Type', 'charset')); + $this->assertEquals(389, strlen($p1->getContent())); + + $p2 = $mp->getPart('0x9d6ad00-0xa19ef48-0x9de7500-0xa4fae78-0xa382698'); + $this->assertEquals('binary', $p2->getHeader('Content-Transfer-Encoding')); + $this->assertEquals('application/binary', $p2->getHeader('Content-Type')); + $this->assertEquals(81, strlen($p2->getContent())); + } + + public function testParserResponseAxis() + { + $filename = dirname(__DIR__).DIRECTORY_SEPARATOR.'Fixtures/SwA-response-axis.txt'; + $mimeMessage = file_get_contents($filename); + + $mp = Parser::parseMimeMessage($mimeMessage); + $this->assertEquals('Sat, 11 Sep 2010 12:52:57 GMT', $mp->getHeader('Date')); + $this->assertEquals('Simple-Server/1.1', $mp->getHeader('Server')); + $this->assertEquals('1.0', $mp->getHeader('MIME-Version')); + $this->assertEquals('chunked', $mp->getHeader('Transfer-Encoding')); + $this->assertEquals('multipart/related', $mp->getHeader('Content-Type')); + $this->assertEquals('application/soap+xml', $mp->getHeader('Content-Type', 'type')); + $this->assertEquals('utf-8', $mp->getHeader('Content-Type', 'charset')); + $this->assertEquals('<0.urn:uuid:2DB7ABF3DC5BED7FA51284209577583@apache.org>', $mp->getHeader('Content-Type', 'start')); + $this->assertEquals('urn:getVersionResponse', $mp->getHeader('Content-Type', 'action')); + $this->assertEquals('MIMEBoundaryurn_uuid_2DB7ABF3DC5BED7FA51284209577582', $mp->getHeader('Content-Type', 'boundary')); + + $p1 = $mp->getPart('0.urn:uuid:2DB7ABF3DC5BED7FA51284209577583@apache.org'); + $this->assertEquals('8bit', $p1->getHeader('Content-Transfer-Encoding')); + $this->assertEquals('application/soap+xml', $p1->getHeader('Content-Type')); + $this->assertEquals('utf-8', $p1->getHeader('Content-Type', 'charset')); + $this->assertEquals(499, strlen($p1->getContent())); + } + + public function testParserResponseWsi() + { + $filename = dirname(__DIR__).DIRECTORY_SEPARATOR.'Fixtures/WS-I-MTOM-response.txt'; + $mimeMessage = file_get_contents($filename); + + $mp = Parser::parseMimeMessage($mimeMessage); + $this->assertEquals('Keep-Alive', $mp->getHeader('Proxy-Connection')); + $this->assertEquals('Keep-Alive', $mp->getHeader('Connection')); + $this->assertEquals('1166', $mp->getHeader('Content-Length')); + $this->assertEquals('1.1 RED-PRXY-03', $mp->getHeader('Via')); + $this->assertEquals('Fri, 09 Sep 2005 06:57:22 GMT', $mp->getHeader('Date')); + $this->assertEquals('multipart/related', $mp->getHeader('Content-Type')); + $this->assertEquals('application/xop+xml', $mp->getHeader('Content-Type', 'type')); + $this->assertEquals('utf-8', $mp->getHeader('Content-Type', 'charset')); + $this->assertEquals('', $mp->getHeader('Content-Type', 'start')); + $this->assertEquals('application/soap+xml', $mp->getHeader('Content-Type', 'start-info')); + $this->assertEquals('uuid:b71dc628-ec8f-4422-8a4a-992f041cb94c+id=46', $mp->getHeader('Content-Type', 'boundary')); + + $p1 = $mp->getPart('http://tempuri.org/0'); + $this->assertEquals('8bit', $p1->getHeader('Content-Transfer-Encoding')); + $this->assertEquals('application/xop+xml', $p1->getHeader('Content-Type')); + $this->assertEquals('utf-8', $p1->getHeader('Content-Type', 'charset')); + $this->assertEquals('application/soap+xml', $p1->getHeader('Content-Type', 'type')); + $this->assertEquals(910, strlen($p1->getContent())); + } + + public function testParserWithHeaderArray() + { + $filename = dirname(__DIR__).DIRECTORY_SEPARATOR.'Fixtures/WS-I-MTOM-request_noheader.txt'; + $mimeMessage = file_get_contents($filename); + + $headers = array( + 'Content-Type' => 'multipart/related; type="application/xop+xml";start="";boundary="uuid:0ca0e16e-feb1-426c-97d8-c4508ada5e82+id=7";start-info="application/soap+xml"', + 'Content-Length' => 1941, + 'Host' => '131.107.72.15', + 'Expect' => '100-continue', + ); + + $mp = Parser::parseMimeMessage($mimeMessage, $headers); + $this->assertsForWsiMtomRequest($mp); + } + + /* + * used in: + * - testParserWithHeaderArray + * - testParserRequestWsi + */ + private function assertsForWsiMtomRequest(MultiPart $mp) + { + $this->assertEquals('multipart/related', $mp->getHeader('Content-Type')); + $this->assertEquals('application/xop+xml', $mp->getHeader('Content-Type', 'type')); + $this->assertEquals('utf-8', $mp->getHeader('Content-Type', 'charset')); + $this->assertEquals('', $mp->getHeader('Content-Type', 'start')); + $this->assertEquals('application/soap+xml', $mp->getHeader('Content-Type', 'start-info')); + $this->assertEquals('uuid:0ca0e16e-feb1-426c-97d8-c4508ada5e82+id=7', $mp->getHeader('Content-Type', 'boundary')); + $this->assertEquals('1941', $mp->getHeader('Content-Length')); + $this->assertEquals('131.107.72.15', $mp->getHeader('Host')); + $this->assertEquals('100-continue', $mp->getHeader('Expect')); + + $p1 = $mp->getPart('http://tempuri.org/0'); + $this->assertEquals('8bit', $p1->getHeader('Content-Transfer-Encoding')); + $this->assertEquals('application/xop+xml', $p1->getHeader('Content-Type')); + $this->assertEquals('utf-8', $p1->getHeader('Content-Type', 'charset')); + $this->assertEquals('application/soap+xml', $p1->getHeader('Content-Type', 'type')); + $this->assertEquals(737, strlen($p1->getContent())); + + $p2 = $mp->getPart('http://tempuri.org/1/632618206527087310'); + $this->assertEquals('binary', $p2->getHeader('Content-Transfer-Encoding')); + $this->assertEquals('application/octet-stream', $p2->getHeader('Content-Type')); + $this->assertEquals(769, strlen($p2->getContent())); + } +} diff --git a/tests/BeSimple/Tests/SoapCommon/Mime/PartHeaderTest.php b/tests/BeSimple/Tests/SoapCommon/Mime/PartHeaderTest.php new file mode 100644 index 0000000..ef9f08d --- /dev/null +++ b/tests/BeSimple/Tests/SoapCommon/Mime/PartHeaderTest.php @@ -0,0 +1,57 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\Tests\SoapCommon\Soap; + +use BeSimple\SoapCommon\Mime\PartHeader; +use BeSimple\Tests\SoapCommon\Fixtures\MimePartHeader; + +class PartHeaderTest extends \PHPUnit_Framework_TestCase +{ + public function testSetGetHeader() + { + $ph = new MimePartHeader(); + $ph->setHeader('Content-Type', 'text/xml'); + $this->assertEquals('text/xml', $ph->getHeader('Content-Type')); + } + + public function testSetGetHeaderSubvalue() + { + $ph = new MimePartHeader(); + $ph->setHeader('Content-Type', 'utf-8', 'charset'); + $this->assertEquals(null, $ph->getHeader('Content-Type', 'charset')); + + $ph->setHeader('Content-Type', 'text/xml'); + $ph->setHeader('Content-Type', 'charset', 'utf-8'); + $this->assertEquals('utf-8', $ph->getHeader('Content-Type', 'charset')); + } + + public function testGenerateHeaders() + { + $ph = new MimePartHeader(); + + $class = new \ReflectionClass($ph); + $method = $class->getMethod('generateHeaders'); + $method->setAccessible(true); + + $this->assertEquals('', $method->invoke($ph)); + + $ph->setHeader('Content-Type', 'text/xml'); + $this->assertEquals("Content-Type: text/xml\r\n", $method->invoke($ph)); + + $ph->setHeader('Content-Type', 'charset', 'utf-8'); + $this->assertEquals("Content-Type: text/xml; charset=utf-8\r\n", $method->invoke($ph)); + + $ph->setHeader('Content-Type', 'type', 'text/xml'); + $this->assertEquals("Content-Type: text/xml; charset=utf-8; type=\"text/xml\"\r\n", $method->invoke($ph)); + } +} \ No newline at end of file diff --git a/tests/BeSimple/Tests/SoapCommon/Mime/PartTest.php b/tests/BeSimple/Tests/SoapCommon/Mime/PartTest.php new file mode 100644 index 0000000..031ba5b --- /dev/null +++ b/tests/BeSimple/Tests/SoapCommon/Mime/PartTest.php @@ -0,0 +1,62 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\Tests\SoapCommon\Soap; + +use BeSimple\SoapCommon\Mime\Part; +use BeSimple\SoapCommon\Mime\PartHeader; + +class PartTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $p = new Part('', 'text/xml', 'utf-8', Part::ENCODING_BINARY, 'urn:myuniqueresource'); + + $this->assertEquals('', $p->getContent()); + $this->assertEquals('text/xml', $p->getHeader('Content-Type')); + $this->assertEquals('utf-8', $p->getHeader('Content-Type', 'charset')); + $this->assertEquals(Part::ENCODING_BINARY, $p->getHeader('Content-Transfer-Encoding')); + $this->assertEquals('', $p->getHeader('Content-ID')); + } + + public function testDefaultConstructor() + { + $p = new Part(); + + $this->assertNull($p->getContent()); + $this->assertEquals('application/octet-stream', $p->getHeader('Content-Type')); + $this->assertEquals('utf-8', $p->getHeader('Content-Type', 'charset')); + $this->assertEquals(Part::ENCODING_BINARY, $p->getHeader('Content-Transfer-Encoding')); + $this->assertRegExp('~~', $p->getHeader('Content-ID')); + } + + public function testSetContent() + { + $p = new Part(); + + $p->setContent(''); + $this->assertEquals('', $p->getContent()); + } + + public function testGetMessagePart() + { + $p = new Part('', 'text/xml', 'utf-8', Part::ENCODING_BINARY, 'urn:myuniqueresource'); + + $messagePart = "Content-Type: text/xml; charset=utf-8\r\n" . + "Content-Transfer-Encoding: binary\r\n" . + "Content-ID: \r\n" . + "\r\n". + ""; + + $this->assertEquals($messagePart, $p->getMessagePart()); + } +} \ No newline at end of file From da344ad03f439bcb314a37eb86e1836a0a011735 Mon Sep 17 00:00:00 2001 From: Andreas Schamberger Date: Tue, 1 Nov 2011 08:28:08 +0100 Subject: [PATCH 19/48] CS fixes --- src/BeSimple/SoapCommon/Helper.php | 2 +- src/BeSimple/SoapCommon/Mime/Parser.php | 7 +++---- src/BeSimple/SoapCommon/Mime/PartHeader.php | 4 ++-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/BeSimple/SoapCommon/Helper.php b/src/BeSimple/SoapCommon/Helper.php index 6d11b0f..58f9779 100644 --- a/src/BeSimple/SoapCommon/Helper.php +++ b/src/BeSimple/SoapCommon/Helper.php @@ -159,7 +159,7 @@ class Helper mt_rand(0, 0x3fff) | 0x8000, // 48 bits for "node" mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff) - ); + ); } /** diff --git a/src/BeSimple/SoapCommon/Mime/Parser.php b/src/BeSimple/SoapCommon/Mime/Parser.php index 7d98edf..ab41b97 100644 --- a/src/BeSimple/SoapCommon/Mime/Parser.php +++ b/src/BeSimple/SoapCommon/Mime/Parser.php @@ -50,8 +50,7 @@ class Parser $lines = preg_split("/\r\n|\n/", $mimeMessage); foreach ($lines as $line) { // ignore http status code and POST * - if ( substr( $line, 0, 5 ) == 'HTTP/' || substr( $line, 0, 4 ) == 'POST') - { + if (substr($line, 0, 5) == 'HTTP/' || substr($line, 0, 4) == 'POST') { continue; } if (isset($currentHeader)) { @@ -59,7 +58,7 @@ class Parser $currentHeader .= $line; continue; } - if (strpos($currentHeader,':') !== false) { + if (strpos($currentHeader, ':') !== false) { list($headerName, $headerValue) = explode(':', $currentHeader, 2); $headerValue = iconv_mime_decode($headerValue, 0, 'utf-8'); if (strpos($headerValue, ';') !== false) { @@ -129,7 +128,7 @@ class Parser * e.g. Content-Type: multipart/related; boundary=boundary; type=text/xml; * start="<123@abc>" * - * @see https://labs.omniti.com/alexandria/trunk/OmniTI/Mail/Parser.php + * Based on: https://labs.omniti.com/alexandria/trunk/OmniTI/Mail/Parser.php * * @param \BeSimple\SoapCommon\Mime\PartHeader $part Header part * @param string $headerName Header name diff --git a/src/BeSimple/SoapCommon/Mime/PartHeader.php b/src/BeSimple/SoapCommon/Mime/PartHeader.php index b6458cd..8e78bea 100644 --- a/src/BeSimple/SoapCommon/Mime/PartHeader.php +++ b/src/BeSimple/SoapCommon/Mime/PartHeader.php @@ -103,7 +103,7 @@ abstract class PartHeader /** * Generates a header field value from the given value paramater. * - * @param array(string=>string)|string $value + * @param array(string=>string)|string $value Header value * @return string */ protected function generateHeaderFieldValue($value) @@ -128,7 +128,7 @@ abstract class PartHeader * Quote string with '"' if it contains one of the special characters: * "(" / ")" / "<" / ">" / "@" / "," / ";" / ":" / "\" / <"> / "/" / "[" / "]" / "?" / "=" * - * @param string $string + * @param string $string String to quote * @return string */ private function quoteValueString($string) From 247f9ae5dacc8bd778d6aa113591cff86dafc53d Mon Sep 17 00:00:00 2001 From: Andreas Schamberger Date: Tue, 1 Nov 2011 10:45:02 +0100 Subject: [PATCH 20/48] added WsdlHandler --- src/BeSimple/SoapCommon/WsdlHandler.php | 206 ++++++++++++++++++ .../SoapCommon/Fixtures/WsdlMimeContent.wsdl | 64 ++++++ .../SoapCommon/Fixtures/WsdlMimeContent2.wsdl | 64 ++++++ .../SoapCommon/Fixtures/WsdlMimeContent3.wsdl | 64 ++++++ .../Tests/SoapCommon/WsdlHandlerTest.php | 48 ++++ 5 files changed, 446 insertions(+) create mode 100644 src/BeSimple/SoapCommon/WsdlHandler.php create mode 100644 tests/BeSimple/Tests/SoapCommon/Fixtures/WsdlMimeContent.wsdl create mode 100644 tests/BeSimple/Tests/SoapCommon/Fixtures/WsdlMimeContent2.wsdl create mode 100644 tests/BeSimple/Tests/SoapCommon/Fixtures/WsdlMimeContent3.wsdl create mode 100644 tests/BeSimple/Tests/SoapCommon/WsdlHandlerTest.php diff --git a/src/BeSimple/SoapCommon/WsdlHandler.php b/src/BeSimple/SoapCommon/WsdlHandler.php new file mode 100644 index 0000000..cec21f5 --- /dev/null +++ b/src/BeSimple/SoapCommon/WsdlHandler.php @@ -0,0 +1,206 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\SoapCommon; + +/** + * This class loads the given WSDL file and allows to check MIME binding + * information. + * + * @author Andreas Schamberger + */ +class WsdlHandler +{ + /** + * Binding type 'input' . + */ + const BINDING_OPERATION_INPUT = 'input'; + + /** + * Binding type 'output' . + */ + const BINDING_OPERATION_OUTPUT = 'output'; + + /** + * WSDL 1.1 namespace. + */ + const NS_WSDL = 'http://schemas.xmlsoap.org/wsdl/'; + + /** + * WSDL MIME namespace. + */ + const NS_WSDL_MIME = 'http://schemas.xmlsoap.org/wsdl/mime/'; + + /** + * WSDL SOAP 1.1 namespace. + */ + const NS_WSDL_SOAP_1_1 = 'http://schemas.xmlsoap.org/wsdl/soap/'; + + /** + * WSDL SOAP 1.2 namespace. + */ + const NS_WSDL_SOAP_1_2 = 'http://schemas.xmlsoap.org/wsdl/soap12/'; + + /** + * WSDL file name. + * + * @var string + */ + private $wsdlFile; + + /** + * DOMDocument WSDL file. + * + * @var \DOMDocument + */ + private $domDocument; + + /** + * DOMXPath WSDL file. + * + * @var DOMXPath + */ + private $domXpath; + + /** + * Array of mime type information. + * + * @var array(string=>array(string=>array(string=>array(string)))) + */ + private $mimeTypes = array(); + + /** + * WSDL namespace of current WSDL file. + * + * @var string + */ + private $wsdlSoapNamespace; + + /** + * Constructor. + * + * @param string $wsdlFile WSDL file name + * @param string $soapVersion SOAP version constant + */ + public function __construct($wsdlFile, $soapVersion) + { + $this->wsdlFile = $wsdlFile; + if ($soapVersion == SOAP_1_1) { + $this->wsdlSoapNamespace = self::NS_WSDL_SOAP_1_1; + } else { + $this->wsdlSoapNamespace = self::NS_WSDL_SOAP_1_2; + } + $this->domDocument = new \DOMDocument('1.0', 'utf-8'); + $this->domDocument->load($this->wsdlFile); + $this->domXpath = new \DOMXPath($this->domDocument); + $this->domXpath->registerNamespace('wsdl', self::NS_WSDL); + $this->domXpath->registerNamespace('mime', self::NS_WSDL_MIME); + $this->domXpath->registerNamespace('soap', $this->wsdlSoapNamespace); + } + + /** + * Gets the mime type information from the WSDL file. + * + * @param string $soapAction Soap action to analyse + * @return array(string=>array(string=>array(string))) + */ + private function getMimeTypesForSoapAction($soapAction) + { + $query = '/wsdl:definitions/wsdl:binding/wsdl:operation/soap:operation[@soapAction="'.$soapAction.'"]/..'; + $nodes = $this->domXpath->query($query); + $mimeTypes = array(); + if (($wsdlOperation = $nodes->item(0)) !== null) { + //$wsdlOperationName = $wsdlOperation->getAttribute('name'); + foreach ($wsdlOperation->childNodes as $soapOperationChild) { + // wsdl:input or wsdl:output + if ($soapOperationChild->localName == 'input' || $soapOperationChild->localName == 'output') { + $operationType = $soapOperationChild->localName; + // mime:multipartRelated/mime:part + $mimeParts = $soapOperationChild->getElementsByTagNameNS(self::NS_WSDL_MIME, 'part'); + if ($mimeParts->length > 0) { + foreach ($mimeParts as $mimePart) { + foreach ($mimePart->childNodes as $child) { + switch ($child->localName) { + case 'body': + $parts = $child->getAttribute('parts'); + $parts = ($parts == '') ? '[body]' : $parts; + $mimeTypes[$operationType][$parts] = array('text/xml'); + break; + case 'content': + $part = $child->getAttribute('part'); + $part = ($part == '') ? null : $part; + $type = $child->getAttribute('type'); + $type = ($type == '') ? '*/*' : $type; + if (!isset($mimeTypes[$operationType][$part])) { + $mimeTypes[$operationType][$part] = array(); + } + $mimeTypes[$operationType][$part][] = $type; + break; + case 'mimeXml': + // this does not conform to the spec + $part = $child->getAttribute('part'); + $part = ($part == '') ? null : $part; + $mimeTypes[$operationType][$part] = array('text/xml'); + break; + } + } + } + } else { + $child = $soapOperationChild->getElementsByTagNameNS($this->wsdlSoapNamespace, 'body')->item(0); + if (!is_null($child)) { + $parts = $child->getAttribute('parts'); + $parts = ($parts == '') ? '[body]' : $parts; + $mimeTypes[$operationType][$parts] = array('text/xml'); + } + } + } + } + } + return $mimeTypes; + } + + /** + * Checks the mime type of the part for the given operation. + * + * @param string $soapAction Soap action + * @param string $operationType Operation type + * @param string $part Part name + * @param string $currentMimeType Current mime type + * @return boolean + */ + public function isValidMimeTypeType($soapAction, $operationType, $part, $currentMimeType) + { + // load data from WSDL + if (!isset($this->mimeTypes[$soapAction])) { + $this->mimeTypes[$soapAction] = $this->getMimeTypesForSoapAction($soapAction); + } + // part is valid as we do not have an explicit entry for current part + if (!isset($this->mimeTypes[$soapAction][$operationType][$part])) { + return true; + } + $mimeTypes = $this->mimeTypes[$soapAction][$operationType][$part]; + // wildcard or exact match + if (in_array('*/*', $mimeTypes) || in_array($currentMimeType, $mimeTypes)) { + return true; + // type/* match + } else { + list($ctype, $csubtype) = explode('/', $currentMimeType); + foreach ($mimeTypes as $mimeType) { + list($type, $subtype) = explode('/', $mimeType); + if ($subtype == '*' && $type == $ctype) { + return true; + } + } + } + return false; + } +} \ No newline at end of file diff --git a/tests/BeSimple/Tests/SoapCommon/Fixtures/WsdlMimeContent.wsdl b/tests/BeSimple/Tests/SoapCommon/Fixtures/WsdlMimeContent.wsdl new file mode 100644 index 0000000..30984b3 --- /dev/null +++ b/tests/BeSimple/Tests/SoapCommon/Fixtures/WsdlMimeContent.wsdl @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/BeSimple/Tests/SoapCommon/Fixtures/WsdlMimeContent2.wsdl b/tests/BeSimple/Tests/SoapCommon/Fixtures/WsdlMimeContent2.wsdl new file mode 100644 index 0000000..a6168da --- /dev/null +++ b/tests/BeSimple/Tests/SoapCommon/Fixtures/WsdlMimeContent2.wsdl @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/BeSimple/Tests/SoapCommon/Fixtures/WsdlMimeContent3.wsdl b/tests/BeSimple/Tests/SoapCommon/Fixtures/WsdlMimeContent3.wsdl new file mode 100644 index 0000000..c7e1d82 --- /dev/null +++ b/tests/BeSimple/Tests/SoapCommon/Fixtures/WsdlMimeContent3.wsdl @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/BeSimple/Tests/SoapCommon/WsdlHandlerTest.php b/tests/BeSimple/Tests/SoapCommon/WsdlHandlerTest.php new file mode 100644 index 0000000..7c10c92 --- /dev/null +++ b/tests/BeSimple/Tests/SoapCommon/WsdlHandlerTest.php @@ -0,0 +1,48 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\Tests\SoapCommon\Soap; + +use BeSimple\SoapCommon\WsdlHandler; + +class WsdlHandlerTest extends \PHPUnit_Framework_TestCase +{ + public function testIsValidMimeTypeType() + { + $filename = __DIR__.DIRECTORY_SEPARATOR.'Fixtures/WsdlMimeContent.wsdl'; + $wh = new WsdlHandler($filename, SOAP_1_1); + + $this->assertTrue($wh->isValidMimeTypeType('http://example.com/soapaction', WsdlHandler::BINDING_OPERATION_INPUT, 'body', 'text/xml')); + $this->assertFalse($wh->isValidMimeTypeType('http://example.com/soapaction', WsdlHandler::BINDING_OPERATION_INPUT, 'body', 'image/gif')); + $this->assertTrue($wh->isValidMimeTypeType('http://example.com/soapaction', WsdlHandler::BINDING_OPERATION_INPUT, 'ClaimPhoto', 'image/jpeg')); + $this->assertFalse($wh->isValidMimeTypeType('http://example.com/soapaction', WsdlHandler::BINDING_OPERATION_INPUT, 'ClaimPhoto', 'image/gif')); + $this->assertFalse($wh->isValidMimeTypeType('http://example.com/soapaction', WsdlHandler::BINDING_OPERATION_INPUT, 'ClaimPhoto', 'text/xml')); + + $filename = __DIR__.DIRECTORY_SEPARATOR.'Fixtures/WsdlMimeContent2.wsdl'; + $wh = new WsdlHandler($filename, SOAP_1_1); + + $this->assertTrue($wh->isValidMimeTypeType('http://example.com/soapaction', WsdlHandler::BINDING_OPERATION_INPUT, 'body', 'text/xml')); + $this->assertFalse($wh->isValidMimeTypeType('http://example.com/soapaction', WsdlHandler::BINDING_OPERATION_INPUT, 'body', 'image/gif')); + $this->assertTrue($wh->isValidMimeTypeType('http://example.com/soapaction', WsdlHandler::BINDING_OPERATION_INPUT, 'ClaimPhoto', 'image/jpeg')); + $this->assertTrue($wh->isValidMimeTypeType('http://example.com/soapaction', WsdlHandler::BINDING_OPERATION_INPUT, 'ClaimPhoto', 'image/gif')); + $this->assertFalse($wh->isValidMimeTypeType('http://example.com/soapaction', WsdlHandler::BINDING_OPERATION_INPUT, 'ClaimPhoto', 'text/xml')); + + $filename = __DIR__.DIRECTORY_SEPARATOR.'Fixtures/WsdlMimeContent3.wsdl'; + $wh = new WsdlHandler($filename, SOAP_1_1); + + $this->assertTrue($wh->isValidMimeTypeType('http://example.com/soapaction', WsdlHandler::BINDING_OPERATION_INPUT, 'body', 'text/xml')); + $this->assertFalse($wh->isValidMimeTypeType('http://example.com/soapaction', WsdlHandler::BINDING_OPERATION_INPUT, 'body', 'image/gif')); + $this->assertTrue($wh->isValidMimeTypeType('http://example.com/soapaction', WsdlHandler::BINDING_OPERATION_INPUT, 'ClaimPhoto', 'image/jpeg')); + $this->assertTrue($wh->isValidMimeTypeType('http://example.com/soapaction', WsdlHandler::BINDING_OPERATION_INPUT, 'ClaimPhoto', 'image/gif')); + $this->assertTrue($wh->isValidMimeTypeType('http://example.com/soapaction', WsdlHandler::BINDING_OPERATION_INPUT, 'ClaimPhoto', 'text/xml')); + } +} \ No newline at end of file From 4ea60adcb7b41304597ba2763680d07560af279d Mon Sep 17 00:00:00 2001 From: Andreas Schamberger Date: Tue, 1 Nov 2011 11:12:07 +0100 Subject: [PATCH 21/48] added WsSecurityKey --- src/BeSimple/SoapCommon/WsSecurityKey.php | 114 +++++++++++++++++ .../Tests/SoapCommon/Fixtures/clientcert.pem | 17 +++ .../Tests/SoapCommon/Fixtures/clientkey.pem | 14 +++ .../Tests/SoapCommon/Fixtures/servercert.pem | 17 +++ .../Tests/SoapCommon/WsSecurityKeyTest.php | 119 ++++++++++++++++++ tests/bootstrap.php | 9 +- vendors.php | 1 + 7 files changed, 290 insertions(+), 1 deletion(-) create mode 100644 src/BeSimple/SoapCommon/WsSecurityKey.php create mode 100644 tests/BeSimple/Tests/SoapCommon/Fixtures/clientcert.pem create mode 100644 tests/BeSimple/Tests/SoapCommon/Fixtures/clientkey.pem create mode 100644 tests/BeSimple/Tests/SoapCommon/Fixtures/servercert.pem create mode 100644 tests/BeSimple/Tests/SoapCommon/WsSecurityKeyTest.php mode change 100755 => 100644 vendors.php diff --git a/src/BeSimple/SoapCommon/WsSecurityKey.php b/src/BeSimple/SoapCommon/WsSecurityKey.php new file mode 100644 index 0000000..6eaf908 --- /dev/null +++ b/src/BeSimple/SoapCommon/WsSecurityKey.php @@ -0,0 +1,114 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\SoapCommon; + +use ass\XmlSecurity\Key as XmlSecurityKey; + +/** + * This class represents a security key for WS-Security (WSS). + * + * @author Andreas Schamberger + */ +class WsSecurityKey +{ + /** + * Private key. + * + * @var \ass\XmlSecurity\Key + */ + protected $privateKey = null; + + /** + * Public key. + * + * @var \ass\XmlSecurity\Key + */ + protected $publicKey = null; + + /** + * Add private key. + * + * @param string $encryptionType Encryption type + * @param string $key Private key + * @param boolean $keyIsFile Given key parameter is path to key file + * @param string $passphrase Passphrase for key + * @return void + */ + public function addPrivateKey($encryptionType, $key = null, $keyIsFile = true, $passphrase = null) + { + $this->privateKey = XmlSecurityKey::factory($encryptionType, $key, $keyIsFile, XmlSecurityKey::TYPE_PRIVATE, $passphrase); + } + + /** + * Add public key. + * + * @param string $encryptionType Encryption type + * @param string $key Public key + * @param boolean $keyIsFile Given key parameter is path to key file + * @return void + */ + public function addPublicKey($encryptionType, $key = null, $keyIsFile = true) + { + $this->publicKey = XmlSecurityKey::factory($encryptionType, $key, $keyIsFile, XmlSecurityKey::TYPE_PUBLIC); + } + + /** + * Get private key. + * + * @return \ass\XmlSecurity\Key + */ + public function getPrivateKey() + { + return $this->privateKey; + } + + /** + * Get public key. + * + * @return \ass\XmlSecurity\Key + */ + public function getPublicKey() + { + return $this->publicKey; + } + + /** + * Has private and public key? + * + * @return boolean + */ + public function hasKeys() + { + return !is_null($this->privateKey) && !is_null($this->publicKey); + } + + /** + * Has private key? + * + * @return boolean + */ + public function hasPrivateKey() + { + return !is_null($this->privateKey); + } + + /** + * Has public key? + * + * @return boolean + */ + public function hasPublicKey() + { + return !is_null($this->publicKey); + } +} \ No newline at end of file diff --git a/tests/BeSimple/Tests/SoapCommon/Fixtures/clientcert.pem b/tests/BeSimple/Tests/SoapCommon/Fixtures/clientcert.pem new file mode 100644 index 0000000..f433d48 --- /dev/null +++ b/tests/BeSimple/Tests/SoapCommon/Fixtures/clientcert.pem @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICoDCCAgkCBEnhw2IwDQYJKoZIhvcNAQEFBQAwgZYxCzAJBgNVBAYTAk5aMRMw +EQYDVQQIEwpXZWxsaW5ndG9uMRowGAYDVQQHExFQYXJhcGFyYXVtdSBCZWFjaDEq +MCgGA1UEChMhU29zbm9za2kgU29mdHdhcmUgQXNzb2NpYXRlcyBMdGQuMRAwDgYD +VQQLEwdVbmtub3duMRgwFgYDVQQDEw9EZW5uaXMgU29zbm9za2kwHhcNMDkwNDEy +MTAzMzA2WhcNMzYwODI3MTAzMzA2WjCBljELMAkGA1UEBhMCTloxEzARBgNVBAgT +CldlbGxpbmd0b24xGjAYBgNVBAcTEVBhcmFwYXJhdW11IEJlYWNoMSowKAYDVQQK +EyFTb3Nub3NraSBTb2Z0d2FyZSBBc3NvY2lhdGVzIEx0ZC4xEDAOBgNVBAsTB1Vu +a25vd24xGDAWBgNVBAMTD0Rlbm5pcyBTb3Nub3NraTCBnzANBgkqhkiG9w0BAQEF +AAOBjQAwgYkCgYEAhOVyNK8xyxtb4DnKtU6mF9KoiFqCk7eKoLE26+9h410CtTkx +zWAfgnR+8i+LPbdsPY+yXAo6NYpCCKolXfDLe+AG2GwnMZGrIl6+BLF3hqTmIXBF +TLGUmC7A7uBTivaWgdH1w3hb33rASoVU67BVtQ3QQi99juZX4vU9o9pScocCAwEA +ATANBgkqhkiG9w0BAQUFAAOBgQBMNPo1KAGbz8Jl6HGbtAcetieSJ3bEAXmv1tcj +ysBS67AXzdu1Ac+onHh2EpzBM7kuGbw+trU+AhulooPpewIQRApXP1F0KHRDcbqW +jwvknS6HnomN9572giLGKn2601bHiRUj35hiA8aLmMUBppIRPFFAoQ0QUBCPx+m8 +/0n33w== +-----END CERTIFICATE----- diff --git a/tests/BeSimple/Tests/SoapCommon/Fixtures/clientkey.pem b/tests/BeSimple/Tests/SoapCommon/Fixtures/clientkey.pem new file mode 100644 index 0000000..a47f923 --- /dev/null +++ b/tests/BeSimple/Tests/SoapCommon/Fixtures/clientkey.pem @@ -0,0 +1,14 @@ +-----BEGIN PRIVATE KEY----- +MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAITlcjSvMcsbW+A5yrVOphfSqIha +gpO3iqCxNuvvYeNdArU5Mc1gH4J0fvIviz23bD2PslwKOjWKQgiqJV3wy3vgBthsJzGRqyJevgSx +d4ak5iFwRUyxlJguwO7gU4r2loHR9cN4W996wEqFVOuwVbUN0EIvfY7mV+L1PaPaUnKHAgMBAAEC +gYAZ6UqtLwN8YGc3fs0hMKZ9upsViuAuwPiMgED/G3twgzAF+ZLWQkmie+hMfCyf6eV200+pVm0n +Bz/8xH/oowxpX0Kk3szoB4vFghjU84GKUcrbhu/NRIm7l3drnfbzqhQkHDCx6n1CotI4Gs49cDWu +4uEAuxJkEIVY553unZjZgQJBAOJVIallNKmD0iQlvtWRmRzpmYDjt9vhNY6WBTIOx6SDn9SRaoSA +fkipQ2HXo04r78TQ674+zfZ1lRTkFG7px6ECQQCWUPHp3pSZOM1oGzJrNvNaw+MizZAZjq34npHm +9GRquFLG7BlCaI9QNGE7pN2ryYsYCRUMaM2e4GR0tUXxVGknAkAgrxqFU9AfCqI2Bh1gyf3KZxF7 +w2axofwR8ygc6nV6FGfoUneHWubhp0/LuVAj4cRmL6Vbe8ZSaPh2Y9lviuMBAkEAicP8Q+1E4j1m +PPEYP51oYprANOiUFmhnWEL00+jPk+QFsd03tV6hYs/vAbwzkjuwqMHCMdJoCiH8z95IEUvc5wJA +MvLOuZdu4dmhOXg/YKsbMSPjFNEVskLQNSXqw6O2wIrpPg1NQvBBAOTbiuZj3vind4VPos1wc4vB +QocvdUC6dA== +-----END PRIVATE KEY----- diff --git a/tests/BeSimple/Tests/SoapCommon/Fixtures/servercert.pem b/tests/BeSimple/Tests/SoapCommon/Fixtures/servercert.pem new file mode 100644 index 0000000..040b22c --- /dev/null +++ b/tests/BeSimple/Tests/SoapCommon/Fixtures/servercert.pem @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICoDCCAgkCBEnhwzMwDQYJKoZIhvcNAQEFBQAwgZYxCzAJBgNVBAYTAk5aMRMw +EQYDVQQIEwpXZWxsaW5ndG9uMRowGAYDVQQHExFQYXJhcGFyYXVtdSBCZWFjaDEq +MCgGA1UEChMhU29zbm9za2kgU29mdHdhcmUgQXNzb2NpYXRlcyBMdGQuMRAwDgYD +VQQLEwdVbmtub3duMRgwFgYDVQQDEw9EZW5uaXMgU29zbm9za2kwHhcNMDkwNDEy +MTAzMjE5WhcNMzYwODI3MTAzMjE5WjCBljELMAkGA1UEBhMCTloxEzARBgNVBAgT +CldlbGxpbmd0b24xGjAYBgNVBAcTEVBhcmFwYXJhdW11IEJlYWNoMSowKAYDVQQK +EyFTb3Nub3NraSBTb2Z0d2FyZSBBc3NvY2lhdGVzIEx0ZC4xEDAOBgNVBAsTB1Vu +a25vd24xGDAWBgNVBAMTD0Rlbm5pcyBTb3Nub3NraTCBnzANBgkqhkiG9w0BAQEF +AAOBjQAwgYkCgYEA1H3mjQCF9uce2jmm/Yq9kE4ytfvkp4c8G90cDfJXJvOiGQds +p2vDZXKuCkHQ7vsBBXPNTt8J/d8ZbEwyuB9Ccz5pJqi6Ig6Y2/mEsPthDyh5SrJV +yQ/wxUGwmfSuwdrIMnplMTq+OR9BOfT3CvjSvuy9d6BQNo4wOMkDvmZTtI8CAwEA +ATANBgkqhkiG9w0BAQUFAAOBgQCqv4475QaqlKcN2QCZJbLVKZEX+76XLQurGkgf +2fCgesRHjfUfOHyTTlhWQdEKTcBB2XviUyyW6I//fmKfXUIiQqvgh4LHdXRPEXDf +Y9nr89MjyQpDlnl6AlrvSej30a9iwVRUeVk4d6gxWHMRonKBFgh+TGexxUXHtPkf +B1Pdtg== +-----END CERTIFICATE----- diff --git a/tests/BeSimple/Tests/SoapCommon/WsSecurityKeyTest.php b/tests/BeSimple/Tests/SoapCommon/WsSecurityKeyTest.php new file mode 100644 index 0000000..3571d3c --- /dev/null +++ b/tests/BeSimple/Tests/SoapCommon/WsSecurityKeyTest.php @@ -0,0 +1,119 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\Tests\SoapCommon\Soap; + +use BeSimple\SoapCommon\WsSecurityKey; +use ass\XmlSecurity\Key as XmlSecurityKey; + +class WsSecurityKeyTest extends \PHPUnit_Framework_TestCase +{ + public function testHasKeys() + { + $wsk = new WsSecurityKey(); + + $filename = __DIR__.DIRECTORY_SEPARATOR.'Fixtures/clientkey.pem'; + $wsk->addPrivateKey(\ass\XmlSecurity\Key::RSA_SHA1, $filename); + $filename = __DIR__.DIRECTORY_SEPARATOR.'Fixtures/clientcert.pem'; + $wsk->addPublicKey(\ass\XmlSecurity\Key::RSA_SHA1, $filename); + + $this->assertTrue($wsk->hasKeys()); + $this->assertTrue($wsk->hasPrivateKey()); + $this->assertTrue($wsk->hasPublicKey()); + } + + public function testHasKeysNone() + { + $wsk = new WsSecurityKey(); + + $this->assertFalse($wsk->hasKeys()); + $this->assertFalse($wsk->hasPrivateKey()); + $this->assertFalse($wsk->hasPublicKey()); + } + + public function testHasPrivateKey() + { + $wsk = new WsSecurityKey(); + + $filename = __DIR__.DIRECTORY_SEPARATOR.'Fixtures/clientkey.pem'; + $wsk->addPrivateKey(\ass\XmlSecurity\Key::RSA_SHA1, $filename); + + $this->assertFalse($wsk->hasKeys()); + $this->assertTrue($wsk->hasPrivateKey()); + } + + public function testHasPublicKey() + { + $wsk = new WsSecurityKey(); + + $filename = __DIR__.DIRECTORY_SEPARATOR.'Fixtures/clientcert.pem'; + $wsk->addPublicKey(\ass\XmlSecurity\Key::RSA_SHA1, $filename); + + $this->assertFalse($wsk->hasKeys()); + $this->assertTrue($wsk->hasPublicKey()); + } + + public function testAddPrivateKey() + { + $wsk = new WsSecurityKey(); + + $filename = __DIR__.DIRECTORY_SEPARATOR.'Fixtures/clientkey.pem'; + $wsk->addPrivateKey(\ass\XmlSecurity\Key::RSA_SHA1, $filename); + + $this->assertTrue($wsk->hasPrivateKey()); + $this->assertInstanceOf('ass\XmlSecurity\Key', $wsk->getPrivateKey()); + } + + public function testAddPrivateKeySessionKey() + { + $wsk = new WsSecurityKey(); + + $filename = __DIR__.DIRECTORY_SEPARATOR.'Fixtures/clientkey.pem'; + $wsk->addPrivateKey(\ass\XmlSecurity\Key::TRIPLEDES_CBC); + + $this->assertTrue($wsk->hasPrivateKey()); + $this->assertInstanceOf('ass\XmlSecurity\Key', $wsk->getPrivateKey()); + } + + public function testAddPrivateKeyNoFile() + { + $wsk = new WsSecurityKey(); + + $filename = __DIR__.DIRECTORY_SEPARATOR.'Fixtures/clientkey.pem'; + $wsk->addPrivateKey(\ass\XmlSecurity\Key::RSA_SHA1, file_get_contents($filename), false); + + $this->assertTrue($wsk->hasPrivateKey()); + $this->assertInstanceOf('ass\XmlSecurity\Key', $wsk->getPrivateKey()); + } + + public function testAddPublicKey() + { + $wsk = new WsSecurityKey(); + + $filename = __DIR__.DIRECTORY_SEPARATOR.'Fixtures/clientcert.pem'; + $wsk->addPublicKey(\ass\XmlSecurity\Key::RSA_SHA1, $filename); + + $this->assertTrue($wsk->hasPublicKey()); + $this->assertInstanceOf('ass\XmlSecurity\Key', $wsk->getPublicKey()); + } + + public function testAddPublicKeyNoFile() + { + $wsk = new WsSecurityKey(); + + $filename = __DIR__.DIRECTORY_SEPARATOR.'Fixtures/clientcert.pem'; + $wsk->addPublicKey(\ass\XmlSecurity\Key::RSA_SHA1, file_get_contents($filename), false); + + $this->assertTrue($wsk->hasPublicKey()); + $this->assertInstanceOf('ass\XmlSecurity\Key', $wsk->getPublicKey()); + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 69c30f0..f2d913c 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -12,7 +12,14 @@ spl_autoload_register(function($class) { return true; } } elseif (0 === strpos($class, 'BeSimple\SoapCommon\\')) { - $path = __DIR__.'/../src/'.($class = strtr($class, '\\', '/')).'.php'; + $path = __DIR__.'/../src/'.strtr($class, '\\', '/').'.php'; + if (file_exists($path) && is_readable($path)) { + require_once $path; + + return true; + } + } elseif (0 === strpos($class, 'ass\XmlSecurity\\')) { + $path = __DIR__.'/../vendor/XmlSecurity/src/'.strtr($class, '\\', '/').'.php'; if (file_exists($path) && is_readable($path)) { require_once $path; diff --git a/vendors.php b/vendors.php old mode 100755 new mode 100644 index 7f127b0..6180e27 --- a/vendors.php +++ b/vendors.php @@ -25,6 +25,7 @@ if (!is_dir($vendorDir = dirname(__FILE__).'/vendor')) { $deps = array( array('vfsStream', 'https://github.com/mikey179/vfsStream.git', 'RELEASE-0.10.1'), + array('XmlSecurity', 'https://github.com/aschamberger/XmlSecurity.git', 'origin/HEAD'), ); foreach ($deps as $dep) { From 3d906dc97a7e3e4002f5123f6bc875901afec7d3 Mon Sep 17 00:00:00 2001 From: Andreas Schamberger Date: Tue, 1 Nov 2011 17:17:16 +0100 Subject: [PATCH 22/48] load WSDL file into DOM only if necessary --- src/BeSimple/SoapCommon/WsdlHandler.php | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/BeSimple/SoapCommon/WsdlHandler.php b/src/BeSimple/SoapCommon/WsdlHandler.php index cec21f5..28ab5b5 100644 --- a/src/BeSimple/SoapCommon/WsdlHandler.php +++ b/src/BeSimple/SoapCommon/WsdlHandler.php @@ -99,12 +99,6 @@ class WsdlHandler } else { $this->wsdlSoapNamespace = self::NS_WSDL_SOAP_1_2; } - $this->domDocument = new \DOMDocument('1.0', 'utf-8'); - $this->domDocument->load($this->wsdlFile); - $this->domXpath = new \DOMXPath($this->domDocument); - $this->domXpath->registerNamespace('wsdl', self::NS_WSDL); - $this->domXpath->registerNamespace('mime', self::NS_WSDL_MIME); - $this->domXpath->registerNamespace('soap', $this->wsdlSoapNamespace); } /** @@ -179,6 +173,8 @@ class WsdlHandler */ public function isValidMimeTypeType($soapAction, $operationType, $part, $currentMimeType) { + // create DOMDocument from WSDL file + $this->loadWsdlInDom(); // load data from WSDL if (!isset($this->mimeTypes[$soapAction])) { $this->mimeTypes[$soapAction] = $this->getMimeTypesForSoapAction($soapAction); @@ -203,4 +199,21 @@ class WsdlHandler } return false; } + + /** + * Loads the WSDL file into a DOM + * + * @return void + */ + private function loadWsdlInDom() + { + if (is_null($this->domDocument)) { + $this->domDocument = new \DOMDocument('1.0', 'utf-8'); + $this->domDocument->load($this->wsdlFile); + $this->domXpath = new \DOMXPath($this->domDocument); + $this->domXpath->registerNamespace('wsdl', self::NS_WSDL); + $this->domXpath->registerNamespace('mime', self::NS_WSDL_MIME); + $this->domXpath->registerNamespace('soap', $this->wsdlSoapNamespace); + } + } } \ No newline at end of file From 6ac3493bc223768f666b487e02eca03d21241ec2 Mon Sep 17 00:00:00 2001 From: Christian Kerl Date: Mon, 21 Nov 2011 20:21:24 +0100 Subject: [PATCH 23/48] added basic soap processing classes discussed in #2 --- src/BeSimple/SoapCommon/SoapKernel.php | 82 ++++++++++++++++ src/BeSimple/SoapCommon/SoapMessage.php | 97 +++++++++++++++++++ src/BeSimple/SoapCommon/SoapRequest.php | 24 +++++ src/BeSimple/SoapCommon/SoapRequestFilter.php | 24 +++++ src/BeSimple/SoapCommon/SoapResponse.php | 24 +++++ .../SoapCommon/SoapResponseFilter.php | 24 +++++ 6 files changed, 275 insertions(+) create mode 100644 src/BeSimple/SoapCommon/SoapKernel.php create mode 100644 src/BeSimple/SoapCommon/SoapMessage.php create mode 100644 src/BeSimple/SoapCommon/SoapRequest.php create mode 100644 src/BeSimple/SoapCommon/SoapRequestFilter.php create mode 100644 src/BeSimple/SoapCommon/SoapResponse.php create mode 100644 src/BeSimple/SoapCommon/SoapResponseFilter.php diff --git a/src/BeSimple/SoapCommon/SoapKernel.php b/src/BeSimple/SoapCommon/SoapKernel.php new file mode 100644 index 0000000..74c6c7c --- /dev/null +++ b/src/BeSimple/SoapCommon/SoapKernel.php @@ -0,0 +1,82 @@ + + * (c) Francis Besset + * (c) Andreas Schamberger + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\SoapCommon; + +use BeSimple\SoapCommon\SoapRequest; +use BeSimple\SoapCommon\SoapResponse; + +use BeSimple\SoapCommon\SoapRequestFilter; +use BeSimple\SoapCommon\SoapResponseFilter; + +/** + * SoapKernel provides methods to pre- and post-process SoapRequests and SoapResponses using + * chains of SoapRequestFilter and SoapResponseFilter objects (roughly following + * the chain-of-responsibility pattern). + * + * @author Christian Kerl + */ +class SoapKernel +{ + private $requestFilters = array(); + private $responseFilters = array(); + + /** + * Registers the given object either as filter for SoapRequests or as filter for SoapResponses + * or as filter for both depending on the implemented interfaces. Inner filters have to be registered + * before outer filters. This means the order is as follows: RequestFilter2->RequestFilter1 and + * ResponseFilter1->ResponseFilter2. + * + * TODO: add priority mechanism to ensure correct order of filters + * + * @param SoapRequestFilter|SoapResponseFilter $filter + */ + public function registerFilter($filter) + { + if($filter instanceof SoapRequestFilter) + { + array_unshift($this->requestFilters, $filter); + } + + if($filter instanceof SoapResponseFilter) + { + array_push($this->responseFilters, $filter); + } + } + + /** + * Applies all registered SoapRequestFilter to the given SoapRequest. + * + * @param SoapRequest $request + */ + public function filterRequest(SoapRequest $request) + { + foreach($this->requestFilters as $filter) + { + $filter->filterRequest($request); + } + } + + /** + * Applies all registered SoapResponseFilter to the given SoapResponse. + * + * @param SoapResponse $response + */ + public function filterResponse(SoapResponse $response) + { + foreach($this->responseFilters as $filter) + { + $filter->filterRequest($response); + } + } +} \ No newline at end of file diff --git a/src/BeSimple/SoapCommon/SoapMessage.php b/src/BeSimple/SoapCommon/SoapMessage.php new file mode 100644 index 0000000..a795139 --- /dev/null +++ b/src/BeSimple/SoapCommon/SoapMessage.php @@ -0,0 +1,97 @@ + + * (c) Francis Besset + * (c) Andreas Schamberger + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\SoapCommon; + +/** + * @author Christian Kerl + */ +abstract class SoapMessage +{ + const CONTENT_TYPE_HEADER = 'CONTENT_TYPE'; + const ACTION_HEADER = 'HTTP_SOAPACTION'; + + static protected $versionToContentTypeMap = array( + SOAP_1_1 => 'text/xml; charset=%s', + SOAP_1_2 => 'application/soap+xml; charset=%s' + ); + + static public function getContentTypeForVersion($version, $encoding = 'utf-8') + { + if(!in_array($soapVersion, array(SOAP_1_1, SOAP_1_2))) + { + throw new \InvalidArgumentException("The 'version' argument has to be either 'SOAP_1_1' or 'SOAP_1_2'!"); + } + + return sprintf(self::$versionToContentTypeMap[$version], $encoding); + } + + protected $contentType; + protected $content; + + protected $contentDomDocument = null; + + protected $version; + protected $action; + + public function getContentType() + { + return $this->contentType; + } + + public function setContentType($contentType) + { + $this->contentType = $contentType; + } + + public function getContent() + { + return $this->content; + } + + public function setContent($content) + { + $this->content = $content; + } + + public function getContentDocument() + { + if(null === $this->contentDomDocument) + { + $this->contentDomDocument = new \DOMDocument(); + $this->contentDomDocument->loadXML($this->content); + } + + return $this->contentDomDocument; + } + + public function getVersion() + { + return $this->version; + } + + public function setVersion($version) + { + $this->version = $version; + } + + public function getAction() + { + return $this->action; + } + + public function setAction($action) + { + $this->action = $action; + } +} \ No newline at end of file diff --git a/src/BeSimple/SoapCommon/SoapRequest.php b/src/BeSimple/SoapCommon/SoapRequest.php new file mode 100644 index 0000000..24120c4 --- /dev/null +++ b/src/BeSimple/SoapCommon/SoapRequest.php @@ -0,0 +1,24 @@ + + * (c) Francis Besset + * (c) Andreas Schamberger + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\SoapCommon; + +use BeSimple\SoapCommon\SoapMessage; + +/** + * @author Christian Kerl + */ +class SoapRequest extends SoapMessage +{ + +} \ No newline at end of file diff --git a/src/BeSimple/SoapCommon/SoapRequestFilter.php b/src/BeSimple/SoapCommon/SoapRequestFilter.php new file mode 100644 index 0000000..8b200ee --- /dev/null +++ b/src/BeSimple/SoapCommon/SoapRequestFilter.php @@ -0,0 +1,24 @@ + + * (c) Francis Besset + * (c) Andreas Schamberger + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\SoapCommon; + +use BeSimple\SoapCommon\SoapRequest; + +/** + * @author Christian Kerl + */ +interface SoapRequestFilter +{ + public function filterRequest(SoapRequest $request); +} \ No newline at end of file diff --git a/src/BeSimple/SoapCommon/SoapResponse.php b/src/BeSimple/SoapCommon/SoapResponse.php new file mode 100644 index 0000000..7141917 --- /dev/null +++ b/src/BeSimple/SoapCommon/SoapResponse.php @@ -0,0 +1,24 @@ + + * (c) Francis Besset + * (c) Andreas Schamberger + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\SoapCommon; + +use BeSimple\SoapCommon\SoapMessage; + +/** + * @author Christian Kerl + */ +class SoapResponse extends SoapMessage +{ + +} \ No newline at end of file diff --git a/src/BeSimple/SoapCommon/SoapResponseFilter.php b/src/BeSimple/SoapCommon/SoapResponseFilter.php new file mode 100644 index 0000000..ebc4c50 --- /dev/null +++ b/src/BeSimple/SoapCommon/SoapResponseFilter.php @@ -0,0 +1,24 @@ + + * (c) Francis Besset + * (c) Andreas Schamberger + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\SoapCommon; + +use BeSimple\SoapCommon\SoapResponse; + +/** + * @author Christian Kerl + */ +interface SoapResponseFilter +{ + public function filterResponse(SoapResponse $response); +} \ No newline at end of file From c09a163b90c799c46afe697115d67045983b8d0d Mon Sep 17 00:00:00 2001 From: Christian Kerl Date: Mon, 21 Nov 2011 20:28:31 +0100 Subject: [PATCH 24/48] fixed method call in SoapKernel --- src/BeSimple/SoapCommon/SoapKernel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BeSimple/SoapCommon/SoapKernel.php b/src/BeSimple/SoapCommon/SoapKernel.php index 74c6c7c..5a8f650 100644 --- a/src/BeSimple/SoapCommon/SoapKernel.php +++ b/src/BeSimple/SoapCommon/SoapKernel.php @@ -76,7 +76,7 @@ class SoapKernel { foreach($this->responseFilters as $filter) { - $filter->filterRequest($response); + $filter->filterResponse($response); } } } \ No newline at end of file From 80e8384c8b8b293733c7f7256ae901f6254de981 Mon Sep 17 00:00:00 2001 From: Francis Besset Date: Thu, 1 Dec 2011 11:05:59 +0100 Subject: [PATCH 25/48] [Converters] Fixed bug if the date(time) is null --- .../SoapCommon/Converter/DateTimeTypeConverter.php | 7 ++++++- .../SoapCommon/Converter/DateTypeConverter.php | 5 +++++ .../Converter/DateTimeTypeConverterTest.php | 13 ++++++++++++- .../SoapCommon/Converter/DateTypeConverterTest.php | 13 ++++++++++++- 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/BeSimple/SoapCommon/Converter/DateTimeTypeConverter.php b/src/BeSimple/SoapCommon/Converter/DateTimeTypeConverter.php index 6446e35..6b0a729 100644 --- a/src/BeSimple/SoapCommon/Converter/DateTimeTypeConverter.php +++ b/src/BeSimple/SoapCommon/Converter/DateTimeTypeConverter.php @@ -32,6 +32,10 @@ class DateTimeTypeConverter implements TypeConverterInterface $doc = new \DOMDocument(); $doc->loadXML($data); + if ('' === $doc->textContent) { + return null; + } + return new \DateTime($doc->textContent); } @@ -39,4 +43,5 @@ class DateTimeTypeConverter implements TypeConverterInterface { return sprintf('<%1$s>%2$s', $this->getTypeName(), $data->format('Y-m-d\TH:i:sP')); } -} \ No newline at end of file +} + diff --git a/src/BeSimple/SoapCommon/Converter/DateTypeConverter.php b/src/BeSimple/SoapCommon/Converter/DateTypeConverter.php index 4e21425..11d5ad2 100644 --- a/src/BeSimple/SoapCommon/Converter/DateTypeConverter.php +++ b/src/BeSimple/SoapCommon/Converter/DateTypeConverter.php @@ -32,6 +32,10 @@ class DateTypeConverter implements TypeConverterInterface $doc = new \DOMDocument(); $doc->loadXML($data); + if ('' === $doc->textContent) { + return null; + } + return new \DateTime($doc->textContent); } @@ -40,3 +44,4 @@ class DateTypeConverter implements TypeConverterInterface return sprintf('<%1$s>%2$s', $this->getTypeName(), $data->format('Y-m-d')); } } + diff --git a/tests/BeSimple/Tests/SoapCommon/Converter/DateTimeTypeConverterTest.php b/tests/BeSimple/Tests/SoapCommon/Converter/DateTimeTypeConverterTest.php index d7913f5..9546722 100644 --- a/tests/BeSimple/Tests/SoapCommon/Converter/DateTimeTypeConverterTest.php +++ b/tests/BeSimple/Tests/SoapCommon/Converter/DateTimeTypeConverterTest.php @@ -40,4 +40,15 @@ class DateTimeTypeConverterTest extends \PHPUnit_Framework_TestCase $this->assertEquals('2002-10-10T12:00:00-05:00', $dateXml); } -} \ No newline at end of file + + public function testConvertNullDateTimeXmlToPhp() + { + $converter = new DateTimeTypeConverter(); + + $dateXml = ''; + $date = $converter->convertXmlToPhp($dateXml); + + $this->assertNull($date); + } +} + diff --git a/tests/BeSimple/Tests/SoapCommon/Converter/DateTypeConverterTest.php b/tests/BeSimple/Tests/SoapCommon/Converter/DateTypeConverterTest.php index 6e164cb..48bddcc 100644 --- a/tests/BeSimple/Tests/SoapCommon/Converter/DateTypeConverterTest.php +++ b/tests/BeSimple/Tests/SoapCommon/Converter/DateTypeConverterTest.php @@ -38,4 +38,15 @@ class DateTypeConverterTest extends \PHPUnit_Framework_TestCase $this->assertEquals('2002-10-10', $dateXml); } -} \ No newline at end of file + + public function testConvertNullDateTimeXmlToPhp() + { + $converter = new DateTypeConverter(); + + $dateXml = ''; + $date = $converter->convertXmlToPhp($dateXml); + + $this->assertNull($date); + } +} + From c7e8928dfae8190090444363c7c542ae62d0b311 Mon Sep 17 00:00:00 2001 From: Andreas Schamberger Date: Sun, 4 Dec 2011 15:38:23 +0100 Subject: [PATCH 26/48] move wsdl namespace constants to Helper --- src/BeSimple/SoapCommon/Helper.php | 25 +++++++++++++++++++ src/BeSimple/SoapCommon/WsdlHandler.php | 32 ++++++------------------- 2 files changed, 32 insertions(+), 25 deletions(-) diff --git a/src/BeSimple/SoapCommon/Helper.php b/src/BeSimple/SoapCommon/Helper.php index 58f9779..19185ed 100644 --- a/src/BeSimple/SoapCommon/Helper.php +++ b/src/BeSimple/SoapCommon/Helper.php @@ -71,6 +71,26 @@ class Helper */ const NS_WSA = 'http://www.w3.org/2005/08/addressing'; + /** + * WSDL 1.1 namespace. + */ + const NS_WSDL = 'http://schemas.xmlsoap.org/wsdl/'; + + /** + * WSDL MIME namespace. + */ + const NS_WSDL_MIME = 'http://schemas.xmlsoap.org/wsdl/mime/'; + + /** + * WSDL SOAP 1.1 namespace. + */ + const NS_WSDL_SOAP_1_1 = 'http://schemas.xmlsoap.org/wsdl/soap/'; + + /** + * WSDL SOAP 1.2 namespace. + */ + const NS_WSDL_SOAP_1_2 = 'http://schemas.xmlsoap.org/wsdl/soap12/'; + /** * Web Services Security Extension namespace. */ @@ -106,6 +126,11 @@ class Helper */ const PFX_WSA = 'wsa'; + /** + * WSDL 1.1 namespace. prefix. + */ + const PFX_WSDL = 'wsdl'; + /** * Web Services Security Extension namespace. */ diff --git a/src/BeSimple/SoapCommon/WsdlHandler.php b/src/BeSimple/SoapCommon/WsdlHandler.php index 28ab5b5..d358ba6 100644 --- a/src/BeSimple/SoapCommon/WsdlHandler.php +++ b/src/BeSimple/SoapCommon/WsdlHandler.php @@ -12,6 +12,8 @@ namespace BeSimple\SoapCommon; +use BeSimple\SoapCommon\Helper; + /** * This class loads the given WSDL file and allows to check MIME binding * information. @@ -30,26 +32,6 @@ class WsdlHandler */ const BINDING_OPERATION_OUTPUT = 'output'; - /** - * WSDL 1.1 namespace. - */ - const NS_WSDL = 'http://schemas.xmlsoap.org/wsdl/'; - - /** - * WSDL MIME namespace. - */ - const NS_WSDL_MIME = 'http://schemas.xmlsoap.org/wsdl/mime/'; - - /** - * WSDL SOAP 1.1 namespace. - */ - const NS_WSDL_SOAP_1_1 = 'http://schemas.xmlsoap.org/wsdl/soap/'; - - /** - * WSDL SOAP 1.2 namespace. - */ - const NS_WSDL_SOAP_1_2 = 'http://schemas.xmlsoap.org/wsdl/soap12/'; - /** * WSDL file name. * @@ -95,9 +77,9 @@ class WsdlHandler { $this->wsdlFile = $wsdlFile; if ($soapVersion == SOAP_1_1) { - $this->wsdlSoapNamespace = self::NS_WSDL_SOAP_1_1; + $this->wsdlSoapNamespace = Helper::NS_WSDL_SOAP_1_1; } else { - $this->wsdlSoapNamespace = self::NS_WSDL_SOAP_1_2; + $this->wsdlSoapNamespace = Helper::NS_WSDL_SOAP_1_2; } } @@ -119,7 +101,7 @@ class WsdlHandler if ($soapOperationChild->localName == 'input' || $soapOperationChild->localName == 'output') { $operationType = $soapOperationChild->localName; // mime:multipartRelated/mime:part - $mimeParts = $soapOperationChild->getElementsByTagNameNS(self::NS_WSDL_MIME, 'part'); + $mimeParts = $soapOperationChild->getElementsByTagNameNS(Helper::NS_WSDL_MIME, 'part'); if ($mimeParts->length > 0) { foreach ($mimeParts as $mimePart) { foreach ($mimePart->childNodes as $child) { @@ -211,8 +193,8 @@ class WsdlHandler $this->domDocument = new \DOMDocument('1.0', 'utf-8'); $this->domDocument->load($this->wsdlFile); $this->domXpath = new \DOMXPath($this->domDocument); - $this->domXpath->registerNamespace('wsdl', self::NS_WSDL); - $this->domXpath->registerNamespace('mime', self::NS_WSDL_MIME); + $this->domXpath->registerNamespace('wsdl', Helper::NS_WSDL); + $this->domXpath->registerNamespace('mime', Helper::NS_WSDL_MIME); $this->domXpath->registerNamespace('soap', $this->wsdlSoapNamespace); } } From a39af089f4cfabbe7dbfcc40029cb8ba5dae2240 Mon Sep 17 00:00:00 2001 From: Andreas Schamberger Date: Sun, 4 Dec 2011 17:29:50 +0100 Subject: [PATCH 27/48] cs fixes --- src/BeSimple/SoapCommon/WsSecurityKey.php | 6 +++--- src/BeSimple/SoapCommon/WsdlHandler.php | 8 +++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/BeSimple/SoapCommon/WsSecurityKey.php b/src/BeSimple/SoapCommon/WsSecurityKey.php index 6eaf908..327b521 100644 --- a/src/BeSimple/SoapCommon/WsSecurityKey.php +++ b/src/BeSimple/SoapCommon/WsSecurityKey.php @@ -89,7 +89,7 @@ class WsSecurityKey */ public function hasKeys() { - return !is_null($this->privateKey) && !is_null($this->publicKey); + return null !== $this->privateKey && null !== $this->publicKey; } /** @@ -99,7 +99,7 @@ class WsSecurityKey */ public function hasPrivateKey() { - return !is_null($this->privateKey); + return null !== $this->privateKey; } /** @@ -109,6 +109,6 @@ class WsSecurityKey */ public function hasPublicKey() { - return !is_null($this->publicKey); + return null !== $this->publicKey; } } \ No newline at end of file diff --git a/src/BeSimple/SoapCommon/WsdlHandler.php b/src/BeSimple/SoapCommon/WsdlHandler.php index d358ba6..f0f533e 100644 --- a/src/BeSimple/SoapCommon/WsdlHandler.php +++ b/src/BeSimple/SoapCommon/WsdlHandler.php @@ -94,7 +94,7 @@ class WsdlHandler $query = '/wsdl:definitions/wsdl:binding/wsdl:operation/soap:operation[@soapAction="'.$soapAction.'"]/..'; $nodes = $this->domXpath->query($query); $mimeTypes = array(); - if (($wsdlOperation = $nodes->item(0)) !== null) { + if (null !== $wsdlOperation = $nodes->item(0)) { //$wsdlOperationName = $wsdlOperation->getAttribute('name'); foreach ($wsdlOperation->childNodes as $soapOperationChild) { // wsdl:input or wsdl:output @@ -132,7 +132,7 @@ class WsdlHandler } } else { $child = $soapOperationChild->getElementsByTagNameNS($this->wsdlSoapNamespace, 'body')->item(0); - if (!is_null($child)) { + if (null !== $child) { $parts = $child->getAttribute('parts'); $parts = ($parts == '') ? '[body]' : $parts; $mimeTypes[$operationType][$parts] = array('text/xml'); @@ -141,6 +141,7 @@ class WsdlHandler } } } + return $mimeTypes; } @@ -179,6 +180,7 @@ class WsdlHandler } } } + return false; } @@ -189,7 +191,7 @@ class WsdlHandler */ private function loadWsdlInDom() { - if (is_null($this->domDocument)) { + if (null === $this->domDocument) { $this->domDocument = new \DOMDocument('1.0', 'utf-8'); $this->domDocument->load($this->wsdlFile); $this->domXpath = new \DOMXPath($this->domDocument); From 7fab5be555a0f085ef96860229cce3a016b83ce4 Mon Sep 17 00:00:00 2001 From: Andreas Schamberger Date: Sat, 17 Dec 2011 13:34:34 +0100 Subject: [PATCH 28/48] fix WS --- tests/bootstrap.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/bootstrap.php b/tests/bootstrap.php index f2d913c..67150ad 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -19,7 +19,7 @@ spl_autoload_register(function($class) { return true; } } elseif (0 === strpos($class, 'ass\XmlSecurity\\')) { - $path = __DIR__.'/../vendor/XmlSecurity/src/'.strtr($class, '\\', '/').'.php'; + $path = __DIR__.'/../vendor/XmlSecurity/src/'.strtr($class, '\\', '/').'.php'; if (file_exists($path) && is_readable($path)) { require_once $path; From 099c1542a9353b6ae93cdc8fbc7dcf6197eb5c7d Mon Sep 17 00:00:00 2001 From: Andreas Schamberger Date: Sat, 17 Dec 2011 13:45:18 +0100 Subject: [PATCH 29/48] - encoding is always utf-8 - add missing location property - consider domdocument for get/setContent --- src/BeSimple/SoapCommon/SoapMessage.php | 61 +++++++++++++++---------- 1 file changed, 38 insertions(+), 23 deletions(-) diff --git a/src/BeSimple/SoapCommon/SoapMessage.php b/src/BeSimple/SoapCommon/SoapMessage.php index a795139..cf3e395 100644 --- a/src/BeSimple/SoapCommon/SoapMessage.php +++ b/src/BeSimple/SoapCommon/SoapMessage.php @@ -20,78 +20,93 @@ abstract class SoapMessage { const CONTENT_TYPE_HEADER = 'CONTENT_TYPE'; const ACTION_HEADER = 'HTTP_SOAPACTION'; - + static protected $versionToContentTypeMap = array( - SOAP_1_1 => 'text/xml; charset=%s', - SOAP_1_2 => 'application/soap+xml; charset=%s' + SOAP_1_1 => 'text/xml; charset=utf-8', + SOAP_1_2 => 'application/soap+xml; charset=utf-8' ); - - static public function getContentTypeForVersion($version, $encoding = 'utf-8') + + static public function getContentTypeForVersion($version) { - if(!in_array($soapVersion, array(SOAP_1_1, SOAP_1_2))) - { + if(!in_array($soapVersion, array(SOAP_1_1, SOAP_1_2))) { throw new \InvalidArgumentException("The 'version' argument has to be either 'SOAP_1_1' or 'SOAP_1_2'!"); } - - return sprintf(self::$versionToContentTypeMap[$version], $encoding); + + return self::$versionToContentTypeMap[$version]; } - + protected $contentType; protected $content; - + protected $contentDomDocument = null; - + protected $version; protected $action; + protected $location; public function getContentType() { return $this->contentType; } - + public function setContentType($contentType) { $this->contentType = $contentType; } - + public function getContent() { + if (null !== $this->contentDomDocument) { + $this->content = $this->contentDomDocument->saveXML(); + } return $this->content; } - + public function setContent($content) { $this->content = $content; + if (null !== $this->contentDomDocument) { + $this->contentDomDocument->loadXML($this->content); + } } - + public function getContentDocument() { - if(null === $this->contentDomDocument) - { + if (null === $this->contentDomDocument) { $this->contentDomDocument = new \DOMDocument(); $this->contentDomDocument->loadXML($this->content); } - + return $this->contentDomDocument; } - + public function getVersion() { return $this->version; } - + public function setVersion($version) { $this->version = $version; } - + public function getAction() { return $this->action; } - + public function setAction($action) { $this->action = $action; } + + public function getLocation() + { + return $this->location; + } + + public function setLocation($location) + { + $this->location = $location; + } } \ No newline at end of file From ce30ccb5041c094a9bb8391186206842f0622ba5 Mon Sep 17 00:00:00 2001 From: Andreas Schamberger Date: Sat, 17 Dec 2011 14:12:31 +0100 Subject: [PATCH 30/48] phpdoc --- src/BeSimple/SoapCommon/SoapMessage.php | 173 ++++++++++++++++++++---- 1 file changed, 143 insertions(+), 30 deletions(-) diff --git a/src/BeSimple/SoapCommon/SoapMessage.php b/src/BeSimple/SoapCommon/SoapMessage.php index cf3e395..4c256c0 100644 --- a/src/BeSimple/SoapCommon/SoapMessage.php +++ b/src/BeSimple/SoapCommon/SoapMessage.php @@ -14,46 +14,119 @@ namespace BeSimple\SoapCommon; /** + * Base class for SoapRequest and SoapResponse. + * * @author Christian Kerl + * @author Andreas Schamberger */ abstract class SoapMessage { + /** + * $_SERVER key for 'Content-Type' header. + * + * @var string + */ const CONTENT_TYPE_HEADER = 'CONTENT_TYPE'; - const ACTION_HEADER = 'HTTP_SOAPACTION'; + /** + * $_SERVER key for 'SOAPAction' header. + * + * @var string + */ + const SOAP_ACTION_HEADER = 'HTTP_SOAPACTION'; + + /** + * Content types for SOAP versions. + * + * @var array(string=>string) + */ static protected $versionToContentTypeMap = array( SOAP_1_1 => 'text/xml; charset=utf-8', SOAP_1_2 => 'application/soap+xml; charset=utf-8' ); - static public function getContentTypeForVersion($version) + /** + * SOAP action. + * + * @var string + */ + protected $action; + + /** + * Message content (MIME Message or SOAP Envelope). + * + * @var string + */ + protected $content; + + /** + * + * Enter description here ... + * @var \DOMDocument + */ + protected $contentDomDocument = null; + + /** + * Message content type. + * + * @var string + */ + protected $contentType; + + /** + * Service location. + * + * @var string + */ + protected $location; + + /** + * SOAP version (SOAP_1_1|SOAP_1_2) + * + * @var string + */ + protected $version; + + /** + * Get content type for given SOAP version. + * + * @param string $version SOAP version constant SOAP_1_1|SOAP_1_2 + * @throws \InvalidArgumentException + */ + public static function getContentTypeForVersion($version) { - if(!in_array($soapVersion, array(SOAP_1_1, SOAP_1_2))) { + if (!in_array($soapVersion, array(SOAP_1_1, SOAP_1_2))) { throw new \InvalidArgumentException("The 'version' argument has to be either 'SOAP_1_1' or 'SOAP_1_2'!"); } return self::$versionToContentTypeMap[$version]; } - protected $contentType; - protected $content; - - protected $contentDomDocument = null; - - protected $version; - protected $action; - protected $location; - - public function getContentType() + /** + * Get SOAP action. + * + * @return string + */ + public function getAction() { - return $this->contentType; + return $this->action; } - public function setContentType($contentType) + /** + * Set SOAP action. + * + * @param string $action + */ + public function setAction($action) { - $this->contentType = $contentType; + $this->action = $action; } + /** + * Get message content (MIME Message or SOAP Envelope). + * + * @return string + */ public function getContent() { if (null !== $this->contentDomDocument) { @@ -62,6 +135,11 @@ abstract class SoapMessage return $this->content; } + /** + * Set message content (MIME Message or SOAP Envelope). + * + * @param string $content + */ public function setContent($content) { $this->content = $content; @@ -70,6 +148,11 @@ abstract class SoapMessage } } + /** + * Get SOAP message as \DOMDocument + * + * @return \DOMDocument + */ public function getContentDocument() { if (null === $this->contentDomDocument) { @@ -80,33 +163,63 @@ abstract class SoapMessage return $this->contentDomDocument; } - public function getVersion() + /** + * Get content type. + * + * @return string + */ + public function getContentType() { - return $this->version; + return $this->contentType; } - public function setVersion($version) + /** + * Set content type. + * + * @param string $contentType + */ + public function setContentType($contentType) { - $this->version = $version; - } - - public function getAction() - { - return $this->action; - } - - public function setAction($action) - { - $this->action = $action; + $this->contentType = $contentType; } + /** + * Get location. + * + * @return string + */ public function getLocation() { return $this->location; } + /** + * Set location. + * + * @param string $location + */ public function setLocation($location) { $this->location = $location; } + + /** + * Get version. + * + * @return string + */ + public function getVersion() + { + return $this->version; + } + + /** + * Set version. + * + * @param string $version + */ + public function setVersion($version) + { + $this->version = $version; + } } \ No newline at end of file From 7de7ee9c6c4d4d03bb49a91320e173256a7e5e51 Mon Sep 17 00:00:00 2001 From: Andreas Schamberger Date: Sat, 17 Dec 2011 16:25:49 +0100 Subject: [PATCH 31/48] CS fixes --- src/BeSimple/SoapCommon/Helper.php | 2 + src/BeSimple/SoapCommon/Mime/MultiPart.php | 5 ++ src/BeSimple/SoapCommon/Mime/Parser.php | 3 + src/BeSimple/SoapCommon/Mime/Part.php | 4 +- src/BeSimple/SoapCommon/Mime/PartHeader.php | 4 ++ src/BeSimple/SoapCommon/SoapKernel.php | 63 ++++++++++--------- src/BeSimple/SoapCommon/SoapMessage.php | 12 ++-- src/BeSimple/SoapCommon/SoapRequest.php | 4 +- src/BeSimple/SoapCommon/SoapRequestFilter.php | 7 +++ src/BeSimple/SoapCommon/SoapResponse.php | 4 +- .../SoapCommon/SoapResponseFilter.php | 7 +++ src/BeSimple/SoapCommon/WsSecurityKey.php | 2 + src/BeSimple/SoapCommon/WsdlHandler.php | 2 + 13 files changed, 83 insertions(+), 36 deletions(-) diff --git a/src/BeSimple/SoapCommon/Helper.php b/src/BeSimple/SoapCommon/Helper.php index 19185ed..c33a9df 100644 --- a/src/BeSimple/SoapCommon/Helper.php +++ b/src/BeSimple/SoapCommon/Helper.php @@ -191,6 +191,7 @@ class Helper * Get SOAP namespace for the given $version. * * @param int $version SOAP_1_1|SOAP_1_2 + * * @return string */ public static function getSoapNamespace($version) @@ -206,6 +207,7 @@ class Helper * Get SOAP version from namespace URI. * * @param string $namespace NS_SOAP_1_1|NS_SOAP_1_2 + * * @return int SOAP_1_1|SOAP_1_2 */ public static function getSoapVersionFromNamespace($namespace) diff --git a/src/BeSimple/SoapCommon/Mime/MultiPart.php b/src/BeSimple/SoapCommon/Mime/MultiPart.php index 1a3b7e1..1b8ffa3 100644 --- a/src/BeSimple/SoapCommon/Mime/MultiPart.php +++ b/src/BeSimple/SoapCommon/Mime/MultiPart.php @@ -46,6 +46,7 @@ class MultiPart extends PartHeader * Construct new mime object. * * @param string $boundary Boundary string + * * @return void */ public function __construct($boundary = null) @@ -64,6 +65,7 @@ class MultiPart extends PartHeader * Get mime message of this object (without headers). * * @param boolean $withHeaders Returned mime message contains headers + * * @return string */ public function getMimeMessage($withHeaders = false) @@ -106,6 +108,7 @@ class MultiPart extends PartHeader * * @param \BeSimple\SoapCommon\Mime\Part $part Part that is added * @param boolean $isMain Is the given part the main part of mime message + * * @return void */ public function addPart(Part $part, $isMain = false) @@ -124,6 +127,7 @@ class MultiPart extends PartHeader * parameter. * * @param string $contentId Content id of desired part + * * @return \BeSimple\SoapCommon\Mime\Part|null */ public function getPart($contentId = null) @@ -141,6 +145,7 @@ class MultiPart extends PartHeader * Get all parts. * * @param boolean $includeMainPart Should main part be in result set + * * @return array(\BeSimple\SoapCommon\Mime\Part) */ public function getParts($includeMainPart = false) diff --git a/src/BeSimple/SoapCommon/Mime/Parser.php b/src/BeSimple/SoapCommon/Mime/Parser.php index ab41b97..1c5fe5f 100644 --- a/src/BeSimple/SoapCommon/Mime/Parser.php +++ b/src/BeSimple/SoapCommon/Mime/Parser.php @@ -24,6 +24,7 @@ class Parser * * @param string $mimeMessage Mime message string * @param array(string=>string) $headers Array of header elements (e.g. coming from http request) + * * @return \BeSimple\SoapCommon\Mime\MultiPart */ public static function parseMimeMessage($mimeMessage, array $headers = array()) @@ -133,6 +134,7 @@ class Parser * @param \BeSimple\SoapCommon\Mime\PartHeader $part Header part * @param string $headerName Header name * @param string $headerValue Header value + * * @return null */ private static function parseContentTypeHeader(PartHeader $part, $headerName, $headerValue) @@ -165,6 +167,7 @@ class Parser * * @param \BeSimple\SoapCommon\Mime\Part $part Part to add content * @param string $content Content to decode + * * @return null */ private static function decodeContent(Part $part, $content) diff --git a/src/BeSimple/SoapCommon/Mime/Part.php b/src/BeSimple/SoapCommon/Mime/Part.php index 88a2ca7..48aa438 100644 --- a/src/BeSimple/SoapCommon/Mime/Part.php +++ b/src/BeSimple/SoapCommon/Mime/Part.php @@ -68,6 +68,7 @@ class Part extends PartHeader * @param string $charset Charset * @param string $encoding Encoding * @param string $contentId Content id + * * @return void */ public function __construct($content = null, $contentType = 'application/octet-stream', $charset = null, $encoding = self::ENCODING_BINARY, $contentId = null) @@ -76,7 +77,7 @@ class Part extends PartHeader $this->setHeader('Content-Type', $contentType); if (!is_null($charset)) { $this->setHeader('Content-Type', 'charset', $charset); - } else { // if (substr($contentType, 0, 4) == 'text') { + } else { $this->setHeader('Content-Type', 'charset', 'utf-8'); } $this->setHeader('Content-Transfer-Encoding', $encoding); @@ -110,6 +111,7 @@ class Part extends PartHeader * Set mime content. * * @param mixed $content Content to set + * * @return void */ public function setContent($content) diff --git a/src/BeSimple/SoapCommon/Mime/PartHeader.php b/src/BeSimple/SoapCommon/Mime/PartHeader.php index 8e78bea..ff0dcfc 100644 --- a/src/BeSimple/SoapCommon/Mime/PartHeader.php +++ b/src/BeSimple/SoapCommon/Mime/PartHeader.php @@ -32,6 +32,7 @@ abstract class PartHeader * @param string $name Header name * @param string $value Header value * @param string $subValue Is sub value? + * * @return void */ public function setHeader($name, $value, $subValue = null) @@ -57,6 +58,7 @@ abstract class PartHeader * * @param string $name Header name * @param string $subValue Sub value name + * * @return mixed|array(mixed) */ public function getHeader($name, $subValue = null) @@ -104,6 +106,7 @@ abstract class PartHeader * Generates a header field value from the given value paramater. * * @param array(string=>string)|string $value Header value + * * @return string */ protected function generateHeaderFieldValue($value) @@ -129,6 +132,7 @@ abstract class PartHeader * "(" / ")" / "<" / ">" / "@" / "," / ";" / ":" / "\" / <"> / "/" / "[" / "]" / "?" / "=" * * @param string $string String to quote + * * @return string */ private function quoteValueString($string) diff --git a/src/BeSimple/SoapCommon/SoapKernel.php b/src/BeSimple/SoapCommon/SoapKernel.php index 5a8f650..fc349c8 100644 --- a/src/BeSimple/SoapCommon/SoapKernel.php +++ b/src/BeSimple/SoapCommon/SoapKernel.php @@ -21,61 +21,68 @@ use BeSimple\SoapCommon\SoapResponseFilter; /** * SoapKernel provides methods to pre- and post-process SoapRequests and SoapResponses using - * chains of SoapRequestFilter and SoapResponseFilter objects (roughly following + * chains of SoapRequestFilter and SoapResponseFilter objects (roughly following * the chain-of-responsibility pattern). - * + * * @author Christian Kerl */ class SoapKernel { - private $requestFilters = array(); - private $responseFilters = array(); - /** - * Registers the given object either as filter for SoapRequests or as filter for SoapResponses + * Request filters. + * + * @var array(SoapRequestFilter) + */ + private $requestFilters = array(); + + /** + * Response filters. + * + * @var array(SoapResponseFilter) + */ + private $responseFilters = array(); + + /** + * Registers the given object either as filter for SoapRequests or as filter for SoapResponses * or as filter for both depending on the implemented interfaces. Inner filters have to be registered - * before outer filters. This means the order is as follows: RequestFilter2->RequestFilter1 and + * before outer filters. This means the order is as follows: RequestFilter2->RequestFilter1 and * ResponseFilter1->ResponseFilter2. - * + * * TODO: add priority mechanism to ensure correct order of filters - * - * @param SoapRequestFilter|SoapResponseFilter $filter + * + * @param SoapRequestFilter|SoapResponseFilter $filter Filter to register */ public function registerFilter($filter) - { - if($filter instanceof SoapRequestFilter) - { + { + if ($filter instanceof SoapRequestFilter) { array_unshift($this->requestFilters, $filter); } - - if($filter instanceof SoapResponseFilter) - { + + if ($filter instanceof SoapResponseFilter) { array_push($this->responseFilters, $filter); } } - + /** - * Applies all registered SoapRequestFilter to the given SoapRequest. - * - * @param SoapRequest $request + * Applies all registered SoapRequestFilter to the given SoapRequest. + * + * @param SoapRequest $request Soap request */ public function filterRequest(SoapRequest $request) { - foreach($this->requestFilters as $filter) - { + foreach ($this->requestFilters as $filter) { $filter->filterRequest($request); } } - + /** - * Applies all registered SoapResponseFilter to the given SoapResponse. - * - * @param SoapResponse $response + * Applies all registered SoapResponseFilter to the given SoapResponse. + * + * @param SoapResponse $response SOAP response */ public function filterResponse(SoapResponse $response) { - foreach($this->responseFilters as $filter) - { + foreach ($this->responseFilters as $filter) { $filter->filterResponse($response); } } diff --git a/src/BeSimple/SoapCommon/SoapMessage.php b/src/BeSimple/SoapCommon/SoapMessage.php index 4c256c0..e92baf5 100644 --- a/src/BeSimple/SoapCommon/SoapMessage.php +++ b/src/BeSimple/SoapCommon/SoapMessage.php @@ -91,6 +91,8 @@ abstract class SoapMessage * Get content type for given SOAP version. * * @param string $version SOAP version constant SOAP_1_1|SOAP_1_2 + * + * @return string * @throws \InvalidArgumentException */ public static function getContentTypeForVersion($version) @@ -115,7 +117,7 @@ abstract class SoapMessage /** * Set SOAP action. * - * @param string $action + * @param string $action SOAP action */ public function setAction($action) { @@ -138,7 +140,7 @@ abstract class SoapMessage /** * Set message content (MIME Message or SOAP Envelope). * - * @param string $content + * @param string $content SOAP message */ public function setContent($content) { @@ -176,7 +178,7 @@ abstract class SoapMessage /** * Set content type. * - * @param string $contentType + * @param string $contentType Content type header */ public function setContentType($contentType) { @@ -196,7 +198,7 @@ abstract class SoapMessage /** * Set location. * - * @param string $location + * @param string $location Location string */ public function setLocation($location) { @@ -216,7 +218,7 @@ abstract class SoapMessage /** * Set version. * - * @param string $version + * @param string $version SOAP version SOAP_1_1|SOAP_1_2 */ public function setVersion($version) { diff --git a/src/BeSimple/SoapCommon/SoapRequest.php b/src/BeSimple/SoapCommon/SoapRequest.php index 24120c4..314d157 100644 --- a/src/BeSimple/SoapCommon/SoapRequest.php +++ b/src/BeSimple/SoapCommon/SoapRequest.php @@ -16,9 +16,11 @@ namespace BeSimple\SoapCommon; use BeSimple\SoapCommon\SoapMessage; /** + * SOAP request message. + * * @author Christian Kerl */ class SoapRequest extends SoapMessage { - + } \ No newline at end of file diff --git a/src/BeSimple/SoapCommon/SoapRequestFilter.php b/src/BeSimple/SoapCommon/SoapRequestFilter.php index 8b200ee..dbc990c 100644 --- a/src/BeSimple/SoapCommon/SoapRequestFilter.php +++ b/src/BeSimple/SoapCommon/SoapRequestFilter.php @@ -16,9 +16,16 @@ namespace BeSimple\SoapCommon; use BeSimple\SoapCommon\SoapRequest; /** + * SOAP request filter interface. + * * @author Christian Kerl */ interface SoapRequestFilter { + /** + * Modify SOAP response. + * + * @param SoapRequest $request SOAP request + */ public function filterRequest(SoapRequest $request); } \ No newline at end of file diff --git a/src/BeSimple/SoapCommon/SoapResponse.php b/src/BeSimple/SoapCommon/SoapResponse.php index 7141917..b6815b2 100644 --- a/src/BeSimple/SoapCommon/SoapResponse.php +++ b/src/BeSimple/SoapCommon/SoapResponse.php @@ -16,9 +16,11 @@ namespace BeSimple\SoapCommon; use BeSimple\SoapCommon\SoapMessage; /** + * SOAP response message. + * * @author Christian Kerl */ class SoapResponse extends SoapMessage { - + } \ No newline at end of file diff --git a/src/BeSimple/SoapCommon/SoapResponseFilter.php b/src/BeSimple/SoapCommon/SoapResponseFilter.php index ebc4c50..c7d6bb6 100644 --- a/src/BeSimple/SoapCommon/SoapResponseFilter.php +++ b/src/BeSimple/SoapCommon/SoapResponseFilter.php @@ -16,9 +16,16 @@ namespace BeSimple\SoapCommon; use BeSimple\SoapCommon\SoapResponse; /** + * SOAP response filter interface. + * * @author Christian Kerl */ interface SoapResponseFilter { + /** + * Modify SOAP response. + * + * @param SoapResponse $response SOAP response + */ public function filterResponse(SoapResponse $response); } \ No newline at end of file diff --git a/src/BeSimple/SoapCommon/WsSecurityKey.php b/src/BeSimple/SoapCommon/WsSecurityKey.php index 327b521..432e654 100644 --- a/src/BeSimple/SoapCommon/WsSecurityKey.php +++ b/src/BeSimple/SoapCommon/WsSecurityKey.php @@ -42,6 +42,7 @@ class WsSecurityKey * @param string $key Private key * @param boolean $keyIsFile Given key parameter is path to key file * @param string $passphrase Passphrase for key + * * @return void */ public function addPrivateKey($encryptionType, $key = null, $keyIsFile = true, $passphrase = null) @@ -55,6 +56,7 @@ class WsSecurityKey * @param string $encryptionType Encryption type * @param string $key Public key * @param boolean $keyIsFile Given key parameter is path to key file + * * @return void */ public function addPublicKey($encryptionType, $key = null, $keyIsFile = true) diff --git a/src/BeSimple/SoapCommon/WsdlHandler.php b/src/BeSimple/SoapCommon/WsdlHandler.php index f0f533e..6fd044b 100644 --- a/src/BeSimple/SoapCommon/WsdlHandler.php +++ b/src/BeSimple/SoapCommon/WsdlHandler.php @@ -87,6 +87,7 @@ class WsdlHandler * Gets the mime type information from the WSDL file. * * @param string $soapAction Soap action to analyse + * * @return array(string=>array(string=>array(string))) */ private function getMimeTypesForSoapAction($soapAction) @@ -152,6 +153,7 @@ class WsdlHandler * @param string $operationType Operation type * @param string $part Part name * @param string $currentMimeType Current mime type + * * @return boolean */ public function isValidMimeTypeType($soapAction, $operationType, $part, $currentMimeType) From f0d373f1b8241573052481552f6231b14826f6f0 Mon Sep 17 00:00:00 2001 From: Andreas Schamberger Date: Sat, 17 Dec 2011 17:52:35 +0100 Subject: [PATCH 32/48] fixed variable name --- src/BeSimple/SoapCommon/SoapMessage.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BeSimple/SoapCommon/SoapMessage.php b/src/BeSimple/SoapCommon/SoapMessage.php index e92baf5..4bceb21 100644 --- a/src/BeSimple/SoapCommon/SoapMessage.php +++ b/src/BeSimple/SoapCommon/SoapMessage.php @@ -97,7 +97,7 @@ abstract class SoapMessage */ public static function getContentTypeForVersion($version) { - if (!in_array($soapVersion, array(SOAP_1_1, SOAP_1_2))) { + if (!in_array($version, array(SOAP_1_1, SOAP_1_2))) { throw new \InvalidArgumentException("The 'version' argument has to be either 'SOAP_1_1' or 'SOAP_1_2'!"); } From e151d06371686ec4296750219152822bb9cee3b2 Mon Sep 17 00:00:00 2001 From: Andreas Schamberger Date: Tue, 3 Jan 2012 12:38:58 +0100 Subject: [PATCH 33/48] fix mime parser bug --- src/BeSimple/SoapCommon/Mime/Parser.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/BeSimple/SoapCommon/Mime/Parser.php b/src/BeSimple/SoapCommon/Mime/Parser.php index 1c5fe5f..92713d8 100644 --- a/src/BeSimple/SoapCommon/Mime/Parser.php +++ b/src/BeSimple/SoapCommon/Mime/Parser.php @@ -32,6 +32,8 @@ class Parser $boundary = null; $start = null; $multipart = new MultiPart(); + $hitFirstBoundary = false; + $inHeader = true; // add given headers, e.g. coming from HTTP headers if (count($headers) > 0) { foreach ($headers as $name => $value) { @@ -43,9 +45,8 @@ class Parser $multipart->setHeader($name, $value); } } + $inHeader = false; } - $hitFirstBoundary = false; - $inHeader = true; $content = ''; $currentPart = $multipart; $lines = preg_split("/\r\n|\n/", $mimeMessage); From f6ea609f46d40c53c6f14a0aac2b4575f473f340 Mon Sep 17 00:00:00 2001 From: Andreas Schamberger Date: Tue, 3 Jan 2012 16:38:50 +0100 Subject: [PATCH 34/48] fix parser and add attachment support --- src/BeSimple/SoapCommon/Mime/Parser.php | 7 ++- src/BeSimple/SoapCommon/SoapKernel.php | 47 +++++++++++++++++++ src/BeSimple/SoapCommon/SoapMessage.php | 29 ++++++++++++ .../Tests/SoapCommon/Mime/ParserTest.php | 2 +- 4 files changed, 80 insertions(+), 5 deletions(-) diff --git a/src/BeSimple/SoapCommon/Mime/Parser.php b/src/BeSimple/SoapCommon/Mime/Parser.php index 92713d8..5ca618f 100644 --- a/src/BeSimple/SoapCommon/Mime/Parser.php +++ b/src/BeSimple/SoapCommon/Mime/Parser.php @@ -49,7 +49,7 @@ class Parser } $content = ''; $currentPart = $multipart; - $lines = preg_split("/\r\n|\n/", $mimeMessage); + $lines = preg_split("/(\r\n)/", $mimeMessage); foreach ($lines as $line) { // ignore http status code and POST * if (substr($line, 0, 5) == 'HTTP/' || substr($line, 0, 4) == 'POST') { @@ -74,7 +74,7 @@ class Parser unset($currentHeader); } if ($inHeader) { - if ($line == '') { + if (trim($line) == '') { $inHeader = false; continue; } @@ -111,7 +111,7 @@ class Parser } } else { if ($hitFirstBoundary === false) { - if ($line != '') { + if (trim($line) != '') { $inHeader = true; $currentHeader = $line; continue; @@ -120,7 +120,6 @@ class Parser $content .= $line . "\r\n"; } } - } return $multipart; } diff --git a/src/BeSimple/SoapCommon/SoapKernel.php b/src/BeSimple/SoapCommon/SoapKernel.php index fc349c8..2315b69 100644 --- a/src/BeSimple/SoapCommon/SoapKernel.php +++ b/src/BeSimple/SoapCommon/SoapKernel.php @@ -13,6 +13,8 @@ namespace BeSimple\SoapCommon; +use BeSimple\SoapCommon\Mime\Part as MimePart; + use BeSimple\SoapCommon\SoapRequest; use BeSimple\SoapCommon\SoapResponse; @@ -28,6 +30,13 @@ use BeSimple\SoapCommon\SoapResponseFilter; */ class SoapKernel { + /** + * Mime attachments. + * + * @var array(\BeSimple\SoapCommon\Mime\Part) + */ + protected $attachments = array(); + /** * Request filters. * @@ -42,6 +51,39 @@ class SoapKernel */ private $responseFilters = array(); + /** + * Add attachment. + * + * @param \BeSimple\SoapCommon\Mime\Part $attachment New attachment + * + * @return void + */ + public function addAttachment(MimePart $attachment) + { + $contentId = trim($part->getHeader('Content-ID'), '<>'); + + $this->attachments[$contentId] = $attachment; + } + + /** + * Get attachment and remove from array. + * + * @param string $contentId Content ID of attachment + * + * @return \BeSimple\SoapCommon\Mime\Part|null + */ + public function getAttachment($contentId) + { + if (isset($this->attachments[$contentId])) { + $part = $this->attachments[$contentId]; + unset($this->attachments[$contentId]); + + return $part; + } + + return null; + } + /** * Registers the given object either as filter for SoapRequests or as filter for SoapResponses * or as filter for both depending on the implemented interfaces. Inner filters have to be registered @@ -70,6 +112,9 @@ class SoapKernel */ public function filterRequest(SoapRequest $request) { + $request->setAttachments($this->attachments); + $this->attachments = array(); + foreach ($this->requestFilters as $filter) { $filter->filterRequest($request); } @@ -85,5 +130,7 @@ class SoapKernel foreach ($this->responseFilters as $filter) { $filter->filterResponse($response); } + + $this->attachments = $response->getAttachments(); } } \ No newline at end of file diff --git a/src/BeSimple/SoapCommon/SoapMessage.php b/src/BeSimple/SoapCommon/SoapMessage.php index 4bceb21..97e0a04 100644 --- a/src/BeSimple/SoapCommon/SoapMessage.php +++ b/src/BeSimple/SoapCommon/SoapMessage.php @@ -13,6 +13,8 @@ namespace BeSimple\SoapCommon; +use BeSimple\SoapCommon\Mime\Part as MimePart; + /** * Base class for SoapRequest and SoapResponse. * @@ -52,6 +54,13 @@ abstract class SoapMessage */ protected $action; + /** + * Mime attachments. + * + * @var array(\BeSimple\SoapCommon\Mime\Part) + */ + protected $attachments = array(); + /** * Message content (MIME Message or SOAP Envelope). * @@ -124,6 +133,26 @@ abstract class SoapMessage $this->action = $action; } + /** + * Get attachments. + * + * @return array(\BeSimple\SoapCommon\Mime\Part) + */ + public function getAttachments() + { + return $this->attachments; + } + + /** + * Set SOAP action. + * + * @param array(\BeSimple\SoapCommon\Mime\Part) $attachments Attachment array + */ + public function setAttachments(array $attachments) + { + $this->attachments = $attachments; + } + /** * Get message content (MIME Message or SOAP Envelope). * diff --git a/tests/BeSimple/Tests/SoapCommon/Mime/ParserTest.php b/tests/BeSimple/Tests/SoapCommon/Mime/ParserTest.php index 8efa1f9..1128325 100644 --- a/tests/BeSimple/Tests/SoapCommon/Mime/ParserTest.php +++ b/tests/BeSimple/Tests/SoapCommon/Mime/ParserTest.php @@ -52,7 +52,7 @@ class ParserTest extends \PHPUnit_Framework_TestCase $p2 = $mp->getPart('0x9d6ad00-0xa19ef48-0x9de7500-0xa4fae78-0xa382698'); $this->assertEquals('binary', $p2->getHeader('Content-Transfer-Encoding')); $this->assertEquals('application/binary', $p2->getHeader('Content-Type')); - $this->assertEquals(81, strlen($p2->getContent())); + $this->assertEquals(79, strlen($p2->getContent())); } public function testParserResponseAxis() From 22665b50ae84eb0fb745c3ac210e0b6fd848fc20 Mon Sep 17 00:00:00 2001 From: Andreas Schamberger Date: Tue, 3 Jan 2012 18:48:40 +0100 Subject: [PATCH 35/48] fixed variable name --- src/BeSimple/SoapCommon/SoapKernel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BeSimple/SoapCommon/SoapKernel.php b/src/BeSimple/SoapCommon/SoapKernel.php index 2315b69..a49b2b1 100644 --- a/src/BeSimple/SoapCommon/SoapKernel.php +++ b/src/BeSimple/SoapCommon/SoapKernel.php @@ -60,7 +60,7 @@ class SoapKernel */ public function addAttachment(MimePart $attachment) { - $contentId = trim($part->getHeader('Content-ID'), '<>'); + $contentId = trim($attachment->getHeader('Content-ID'), '<>'); $this->attachments[$contentId] = $attachment; } From 67e181fd4f85b3002fcdf48a8ebabe0c4ce9ed24 Mon Sep 17 00:00:00 2001 From: Andreas Schamberger Date: Thu, 5 Jan 2012 13:58:35 +0100 Subject: [PATCH 36/48] fixed mime to handle binary data correctly --- src/BeSimple/SoapCommon/Mime/Parser.php | 4 ++-- src/BeSimple/SoapCommon/Mime/Part.php | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/BeSimple/SoapCommon/Mime/Parser.php b/src/BeSimple/SoapCommon/Mime/Parser.php index 5ca618f..c4a8c27 100644 --- a/src/BeSimple/SoapCommon/Mime/Parser.php +++ b/src/BeSimple/SoapCommon/Mime/Parser.php @@ -85,7 +85,7 @@ class Parser if (strlen($line) > 0 && $line[0] == "-") { if (strcmp(trim($line), '--' . $boundary) === 0) { if ($currentPart instanceof Part) { - $content = iconv_substr($content, 0, -2, 'utf-8'); + $content = substr($content, 0, -2); self::decodeContent($currentPart, $content); // check if there is a start parameter given, if not set first part $isMain = (is_null($start) || $start == $currentPart->getHeader('Content-ID')) ? true : false; @@ -99,7 +99,7 @@ class Parser $inHeader = true; $content = ''; } elseif (strcmp(trim($line), '--' . $boundary . '--') === 0) { - $content = iconv_substr($content, 0, -2, 'utf-8'); + $content = substr($content, 0, -2); self::decodeContent($currentPart, $content); // check if there is a start parameter given, if not set first part $isMain = (is_null($start) || $start == $currentPart->getHeader('Content-ID')) ? true : false; diff --git a/src/BeSimple/SoapCommon/Mime/Part.php b/src/BeSimple/SoapCommon/Mime/Part.php index 48aa438..714f137 100644 --- a/src/BeSimple/SoapCommon/Mime/Part.php +++ b/src/BeSimple/SoapCommon/Mime/Part.php @@ -149,6 +149,7 @@ class Part extends PartHeader case self::ENCODING_QUOTED_PRINTABLE: return quoted_printable_encode($content); case self::ENCODING_BINARY: + return $content; case self::ENCODING_SEVEN_BIT: case self::ENCODING_EIGHT_BIT: default: From 2b2d40b3ac058c65569c93b4e18277c3010f91e6 Mon Sep 17 00:00:00 2001 From: Andreas Schamberger Date: Sat, 7 Jan 2012 13:19:22 +0100 Subject: [PATCH 37/48] moved code from client to common --- .../InternalTypeConverterInterface.php | 58 ++++++ .../Converter/MtomTypeConverter.php | 96 ++++++++++ .../SoapCommon/Converter/SwaTypeConverter.php | 82 ++++++++ .../Converter/TypeConverterInterface.php | 26 +++ src/BeSimple/SoapCommon/FilterHelper.php | 178 ++++++++++++++++++ src/BeSimple/SoapCommon/MimeFilter.php | 138 ++++++++++++++ src/BeSimple/SoapCommon/SoapKernel.php | 40 +++- 7 files changed, 617 insertions(+), 1 deletion(-) create mode 100644 src/BeSimple/SoapCommon/Converter/InternalTypeConverterInterface.php create mode 100644 src/BeSimple/SoapCommon/Converter/MtomTypeConverter.php create mode 100644 src/BeSimple/SoapCommon/Converter/SwaTypeConverter.php create mode 100644 src/BeSimple/SoapCommon/FilterHelper.php create mode 100644 src/BeSimple/SoapCommon/MimeFilter.php diff --git a/src/BeSimple/SoapCommon/Converter/InternalTypeConverterInterface.php b/src/BeSimple/SoapCommon/Converter/InternalTypeConverterInterface.php new file mode 100644 index 0000000..9c86b66 --- /dev/null +++ b/src/BeSimple/SoapCommon/Converter/InternalTypeConverterInterface.php @@ -0,0 +1,58 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\SoapCommon\Converter; + +use BeSimple\SoapCommon\SoapKernel; + +/** + * Internal type converter interface. + * + * @author Andreas Schamberger + * @author Christian Kerl + */ +interface InternalTypeConverterInterface +{ + /** + * Get type namespace. + * + * @return string + */ + function getTypeNamespace(); + + /** + * Get type name. + * + * @return string + */ + function getTypeName(); + + /** + * Convert given XML string to PHP type. + * + * @param string $data XML string + * @param \BeSimple\SoapCommon\SoapKernel $soapKernel SoapKernel instance + * + * @return mixed + */ + function convertXmlToPhp($data, SoapKernel $soapKernel); + + /** + * Convert PHP type to XML string. + * + * @param mixed $data PHP type + * @param \BeSimple\SoapCommon\SoapKernel $soapKernel SoapKernel instance + * + * @return string + */ + function convertPhpToXml($data, SoapKernel $soapKernel); +} \ No newline at end of file diff --git a/src/BeSimple/SoapCommon/Converter/MtomTypeConverter.php b/src/BeSimple/SoapCommon/Converter/MtomTypeConverter.php new file mode 100644 index 0000000..a9709e2 --- /dev/null +++ b/src/BeSimple/SoapCommon/Converter/MtomTypeConverter.php @@ -0,0 +1,96 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\SoapCommon\Converter; + +use BeSimple\SoapCommon\Helper; +use BeSimple\SoapCommon\Mime\Part as MimePart; +use BeSimple\SoapCommon\SoapKernel; +use BeSimple\SoapCommon\SoapRequest as CommonSoapRequest; +use BeSimple\SoapCommon\SoapResponse as CommonSoapResponse; +use BeSimple\SoapCommon\Converter\InternalTypeConverterInterface; + +/** + * MTOM type converter. + * + * @author Andreas Schamberger + */ +class MtomTypeConverter implements InternalTypeConverterInterface +{ + /** + * {@inheritDoc} + */ + public function getTypeNamespace() + { + return 'http://www.w3.org/2001/XMLSchema'; + } + + /** + * {@inheritDoc} + */ + public function getTypeName() + { + return 'base64Binary'; + } + + /** + * {@inheritDoc} + */ + public function convertXmlToPhp($data, SoapKernel $soapKernel) + { + $doc = new \DOMDocument(); + $doc->loadXML($data); + + $includes = $doc->getElementsByTagNameNS(Helper::NS_XOP, 'Include'); + $include = $includes->item(0); + + // convert href -> myhref for external references as PHP throws exception in this case + // http://svn.php.net/viewvc/php/php-src/branches/PHP_5_4/ext/soap/php_encoding.c?view=markup#l3436 + $ref = $include->getAttribute('myhref'); + + if ('cid:' === substr($ref, 0, 4)) { + $contentId = urldecode(substr($ref, 4)); + + if (null !== ($part = $soapKernel->getAttachment($contentId))) { + + return $part->getContent(); + } else { + + return null; + } + } + + return $data; + } + + /** + * {@inheritDoc} + */ + public function convertPhpToXml($data, SoapKernel $soapKernel) + { + $part = new MimePart($data); + $contentId = trim($part->getHeader('Content-ID'), '<>'); + + $soapKernel->addAttachment($part); + + $doc = new \DOMDocument(); + $node = $doc->createElement($this->getTypeName()); + $doc->appendChild($node); + + // add xop:Include element + $xinclude = $doc->createElementNS(Helper::NS_XOP, Helper::PFX_XOP . ':Include'); + $xinclude->setAttribute('href', 'cid:' . $contentId); + $node->appendChild($xinclude); + + return $doc->saveXML(); + } +} diff --git a/src/BeSimple/SoapCommon/Converter/SwaTypeConverter.php b/src/BeSimple/SoapCommon/Converter/SwaTypeConverter.php new file mode 100644 index 0000000..32ba467 --- /dev/null +++ b/src/BeSimple/SoapCommon/Converter/SwaTypeConverter.php @@ -0,0 +1,82 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\SoapCommon\Converter; + +use BeSimple\SoapCommon\Helper; +use BeSimple\SoapCommon\Mime\Part as MimePart; +use BeSimple\SoapCommon\SoapKernel; +use BeSimple\SoapCommon\Converter\InternalTypeConverterInterface; + +/** + * SwA type converter. + * + * @author Andreas Schamberger + */ +class SwaTypeConverter implements InternalTypeConverterInterface +{ + /** + * {@inheritDoc} + */ + public function getTypeNamespace() + { + return 'http://www.w3.org/2001/XMLSchema'; + } + + /** + * {@inheritDoc} + */ + public function getTypeName() + { + return 'base64Binary'; + } + + /** + * {@inheritDoc} + */ + public function convertXmlToPhp($data, SoapKernel $soapKernel) + { + $doc = new \DOMDocument(); + $doc->loadXML($data); + + // convert href -> myhref for external references as PHP throws exception in this case + // http://svn.php.net/viewvc/php/php-src/branches/PHP_5_4/ext/soap/php_encoding.c?view=markup#l3436 + $ref = $doc->documentElement->getAttribute('myhref'); + + if ('cid:' === substr($ref, 0, 4)) { + $contentId = urldecode(substr($ref, 4)); + + if (null !== ($part = $soapKernel->getAttachment($contentId))) { + + return $part->getContent(); + } else { + + return null; + } + } + + return $data; + } + + /** + * {@inheritDoc} + */ + public function convertPhpToXml($data, SoapKernel $soapKernel) + { + $part = new MimePart($data); + $contentId = trim($part->getHeader('Content-ID'), '<>'); + + $soapKernel->addAttachment($part); + + return sprintf('<%s href="%s"/>', $this->getTypeName(), $contentId); + } +} diff --git a/src/BeSimple/SoapCommon/Converter/TypeConverterInterface.php b/src/BeSimple/SoapCommon/Converter/TypeConverterInterface.php index 4de7d7f..e390963 100644 --- a/src/BeSimple/SoapCommon/Converter/TypeConverterInterface.php +++ b/src/BeSimple/SoapCommon/Converter/TypeConverterInterface.php @@ -13,15 +13,41 @@ namespace BeSimple\SoapCommon\Converter; /** + * Type converter interface. + * * @author Christian Kerl */ interface TypeConverterInterface { + /** + * Get type namespace. + * + * @return string + */ function getTypeNamespace(); + /** + * Get type name. + * + * @return string + */ function getTypeName(); + /** + * Convert given XML string to PHP type. + * + * @param string $data XML string + * + * @return mixed + */ function convertXmlToPhp($data); + /** + * Convert PHP type to XML string. + * + * @param mixed $data PHP type + * + * @return string + */ function convertPhpToXml($data); } \ No newline at end of file diff --git a/src/BeSimple/SoapCommon/FilterHelper.php b/src/BeSimple/SoapCommon/FilterHelper.php new file mode 100644 index 0000000..01a1717 --- /dev/null +++ b/src/BeSimple/SoapCommon/FilterHelper.php @@ -0,0 +1,178 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\SoapCommon; + +/** + * Soap request/response filter helper for manipulating SOAP messages. + * + * @author Andreas Schamberger + */ +class FilterHelper +{ + /** + * DOMDocument on which the helper functions operate. + * + * @var \DOMDocument + */ + protected $domDocument = null; + + /** + * Namespaces added. + * + * @var array(string=>string) + */ + protected $namespaces = array(); + + /** + * Constructor. + * + * @param \DOMDocument $domDocument SOAP document + */ + public function __construct(\DOMDocument $domDocument) + { + $this->domDocument = $domDocument; + } + + /** + * Add new soap header. + * + * @param \DOMElement $node DOMElement to add + * @param boolean $mustUnderstand SOAP header mustUnderstand attribute + * @param string $actor SOAP actor/role + * @param string $soapVersion SOAP version SOAP_1_1|SOAP_1_2 + * + * @return void + */ + public function addHeaderElement(\DOMElement $node, $mustUnderstand = null, $actor = null, $soapVersion = SOAP_1_1) + { + $root = $this->domDocument->documentElement; + $namespace = $root->namespaceURI; + $prefix = $root->prefix; + if (null !== $mustUnderstand) { + $node->appendChild(new \DOMAttr($prefix . ':mustUnderstand', (int) $mustUnderstand)); + } + if (null !== $actor) { + $attributeName = ($soapVersion == SOAP_1_1) ? 'actor' : 'role'; + $node->appendChild(new \DOMAttr($prefix . ':' . $attributeName, $actor)); + } + $nodeListHeader = $root->getElementsByTagNameNS($namespace, 'Header'); + // add header if not there + if ($nodeListHeader->length == 0) { + // new header element + $header = $this->domDocument->createElementNS($namespace, $prefix . ':Header'); + // try to add it before body + $nodeListBody = $root->getElementsByTagNameNS($namespace, 'Body'); + if ($nodeListBody->length == 0) { + $root->appendChild($header); + } else { + $body = $nodeListBody->item(0); + $header = $body->parentNode->insertBefore($header, $body); + } + $header->appendChild($node); + } else { + $nodeListHeader->item(0)->appendChild($node); + } + } + + /** + * Add new soap body element. + * + * @param \DOMElement $node DOMElement to add + * + * @return void + */ + public function addBodyElement(\DOMElement $node) + { + $root = $this->domDocument->documentElement; + $namespace = $root->namespaceURI; + $prefix = $root->prefix; + $nodeList = $this->domDocument->getElementsByTagNameNS($namespace, 'Body'); + // add body if not there + if ($nodeList->length == 0) { + // new body element + $body = $this->domDocument->createElementNS($namespace, $prefix . ':Body'); + $root->appendChild($body); + $body->appendChild($node); + } else { + $nodeList->item(0)->appendChild($node); + } + } + + /** + * Add new namespace to root tag. + * + * @param string $prefix Namespace prefix + * @param string $namespaceURI Namespace URI + * + * @return void + */ + public function addNamespace($prefix, $namespaceURI) + { + if (!isset($this->namespaces[$namespaceURI])) { + $root = $this->domDocument->documentElement; + $root->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:' . $prefix, $namespaceURI); + $this->namespaces[$namespaceURI] = $prefix; + } + } + + /** + * Create new element for given namespace. + * + * @param string $namespaceURI Namespace URI + * @param string $name Element name + * @param string $value Element value + * + * @return \DOMElement + */ + public function createElement($namespaceURI, $name, $value = null) + { + $prefix = $this->namespaces[$namespaceURI]; + + return $this->domDocument->createElementNS($namespaceURI, $prefix . ':' . $name, $value); + } + + /** + * Add new attribute to element with given namespace. + * + * @param \DOMElement $element DOMElement to edit + * @param string $namespaceURI Namespace URI + * @param string $name Attribute name + * @param string $value Attribute value + * + * @return void + */ + public function setAttribute(\DOMElement $element, $namespaceURI, $name, $value) + { + if (null !== $namespaceURI) { + $prefix = $this->namespaces[$namespaceURI]; + $element->setAttributeNS($namespaceURI, $prefix . ':' . $name, $value); + } else { + $element->setAttribute($name, $value); + } + } + + /** + * Register namespace. + * + * @param string $prefix Namespace prefix + * @param string $namespaceURI Namespace URI + * + * @return void + */ + public function registerNamespace($prefix, $namespaceURI) + { + if (!isset($this->namespaces[$namespaceURI])) { + $this->namespaces[$namespaceURI] = $prefix; + } + } +} \ No newline at end of file diff --git a/src/BeSimple/SoapCommon/MimeFilter.php b/src/BeSimple/SoapCommon/MimeFilter.php new file mode 100644 index 0000000..068deb7 --- /dev/null +++ b/src/BeSimple/SoapCommon/MimeFilter.php @@ -0,0 +1,138 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\SoapCommon; + +use BeSimple\SoapCommon\Helper; +use BeSimple\SoapCommon\Mime\MultiPart as MimeMultiPart; +use BeSimple\SoapCommon\Mime\Parser as MimeParser; +use BeSimple\SoapCommon\Mime\Part as MimePart; +use BeSimple\SoapCommon\SoapRequest; +use BeSimple\SoapCommon\SoapRequestFilter; +use BeSimple\SoapCommon\SoapResponse; +use BeSimple\SoapCommon\SoapResponseFilter; + +/** + * MIME filter. + * + * @author Andreas Schamberger + */ +class MimeFilter implements SoapRequestFilter, SoapResponseFilter +{ + /** + * Attachment type. + * + * @var int Helper::ATTACHMENTS_TYPE_SWA | Helper::ATTACHMENTS_TYPE_MTOM + */ + protected $attachmentType = Helper::ATTACHMENTS_TYPE_SWA; + + /** + * Constructor. + * + * @param int $attachmentType Helper::ATTACHMENTS_TYPE_SWA | Helper::ATTACHMENTS_TYPE_MTOM + */ + public function __construct($attachmentType) + { + $this->attachmentType = $attachmentType; + } + + /** + * Reset all properties to default values. + */ + public function resetFilter() + { + $this->attachmentType = Helper::ATTACHMENTS_TYPE_SWA; + } + + /** + * Modify the given request XML. + * + * @param \BeSimple\SoapCommon\SoapRequest $request SOAP request + * + * @return void + */ + public function filterRequest(SoapRequest $request) + { + // get attachments from request object + $attachmentsToSend = $request->getAttachments(); + + // build mime message if we have attachments + if (count($attachmentsToSend) > 0) { + $multipart = new MimeMultiPart(); + $soapPart = new MimePart($request->getContent(), 'text/xml', 'utf-8', MimePart::ENCODING_EIGHT_BIT); + $soapVersion = $request->getVersion(); + // change content type headers for MTOM with SOAP 1.1 + if ($soapVersion == SOAP_1_1 && $this->attachmentType & Helper::ATTACHMENTS_TYPE_MTOM) { + $multipart->setHeader('Content-Type', 'type', 'application/xop+xml'); + $multipart->setHeader('Content-Type', 'start-info', 'text/xml'); + $soapPart->setHeader('Content-Type', 'application/xop+xml'); + $soapPart->setHeader('Content-Type', 'type', 'text/xml'); + } + // change content type headers for SOAP 1.2 + elseif ($soapVersion == SOAP_1_2) { + $multipart->setHeader('Content-Type', 'type', 'application/soap+xml'); + $soapPart->setHeader('Content-Type', 'application/soap+xml'); + } + $multipart->addPart($soapPart, true); + foreach ($attachmentsToSend as $cid => $attachment) { + $multipart->addPart($attachment, false); + } + $request->setContent($multipart->getMimeMessage()); + + // TODO + $headers = $multipart->getHeadersForHttp(); + list($name, $contentType) = explode(': ', $headers[0]); + + $request->setContentType($contentType); + } + } + + /** + * Modify the given response XML. + * + * @param \BeSimple\SoapCommon\SoapResponse $response SOAP response + * + * @return void + */ + public function filterResponse(SoapResponse $response) + { + // array to store attachments + $attachmentsRecieved = array(); + + // check content type if it is a multipart mime message + $responseContentType = $response->getContentType(); + if (false !== stripos($responseContentType, 'multipart/related')) { + // parse mime message + $headers = array( + 'Content-Type' => trim($responseContentType), + ); + $multipart = MimeParser::parseMimeMessage($response->getContent(), $headers); + // get soap payload and update SoapResponse object + $soapPart = $multipart->getPart(); + // convert href -> myhref for external references as PHP throws exception in this case + // http://svn.php.net/viewvc/php/php-src/branches/PHP_5_4/ext/soap/php_encoding.c?view=markup#l3436 + $content = preg_replace('/href=(?!#)/', 'myhref=', $soapPart->getContent()); + $response->setContent($content); + $response->setContentType($soapPart->getHeader('Content-Type')); + // store attachments + $attachments = $multipart->getParts(false); + foreach ($attachments as $cid => $attachment) { + $attachmentsRecieved[$cid] = $attachment; + } + } + + // add attachments to response object + if (count($attachmentsRecieved) > 0) { + $response->setAttachments($attachmentsRecieved); + } + } +} \ No newline at end of file diff --git a/src/BeSimple/SoapCommon/SoapKernel.php b/src/BeSimple/SoapCommon/SoapKernel.php index a49b2b1..f0ead8c 100644 --- a/src/BeSimple/SoapCommon/SoapKernel.php +++ b/src/BeSimple/SoapCommon/SoapKernel.php @@ -15,9 +15,10 @@ namespace BeSimple\SoapCommon; use BeSimple\SoapCommon\Mime\Part as MimePart; +use BeSimple\SoapCommon\Converter\MtomTypeConverter; +use BeSimple\SoapCommon\Converter\SwaTypeConverter; use BeSimple\SoapCommon\SoapRequest; use BeSimple\SoapCommon\SoapResponse; - use BeSimple\SoapCommon\SoapRequestFilter; use BeSimple\SoapCommon\SoapResponseFilter; @@ -133,4 +134,41 @@ class SoapKernel $this->attachments = $response->getAttachments(); } + + /** + * Configure filter and type converter for SwA/MTOM. + * + * @param array &$options SOAP constructor options array. + * + * @return void + */ + public function configureMime(array &$options) + { + if (isset($options['attachment_type']) && Helper::ATTACHMENTS_TYPE_BASE64 !== $options['attachment_type']) { + // register mime filter in SoapKernel + $mimeFilter = new MimeFilter($options['attachment_type']); + $this->registerFilter($mimeFilter); + // configure type converter + if (Helper::ATTACHMENTS_TYPE_SWA === $options['attachment_type']) { + $converter = new SwaTypeConverter(); + } elseif (Helper::ATTACHMENTS_TYPE_MTOM === $options['attachment_type']) { + $converter = new MtomTypeConverter(); + } + // configure typemap + if (!isset($options['typemap'])) { + $options['typemap'] = array(); + } + $soapKernel = $this; + $options['typemap'][] = array( + 'type_name' => $converter->getTypeName(), + 'type_ns' => $converter->getTypeNamespace(), + 'from_xml' => function($input) use ($converter, $soapKernel) { + return $converter->convertXmlToPhp($input, $soapKernel); + }, + 'to_xml' => function($input) use ($converter, $soapKernel) { + return $converter->convertPhpToXml($input, $soapKernel); + }, + ); + } + } } \ No newline at end of file From bf8965895ece3a92863a9e49108d2d2340a3b360 Mon Sep 17 00:00:00 2001 From: Andreas Schamberger Date: Sun, 8 Jan 2012 09:31:01 +0100 Subject: [PATCH 38/48] mime filter is not generic --- src/BeSimple/SoapCommon/MimeFilter.php | 138 ------------------------- 1 file changed, 138 deletions(-) delete mode 100644 src/BeSimple/SoapCommon/MimeFilter.php diff --git a/src/BeSimple/SoapCommon/MimeFilter.php b/src/BeSimple/SoapCommon/MimeFilter.php deleted file mode 100644 index 068deb7..0000000 --- a/src/BeSimple/SoapCommon/MimeFilter.php +++ /dev/null @@ -1,138 +0,0 @@ - - * (c) Francis Besset - * - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -namespace BeSimple\SoapCommon; - -use BeSimple\SoapCommon\Helper; -use BeSimple\SoapCommon\Mime\MultiPart as MimeMultiPart; -use BeSimple\SoapCommon\Mime\Parser as MimeParser; -use BeSimple\SoapCommon\Mime\Part as MimePart; -use BeSimple\SoapCommon\SoapRequest; -use BeSimple\SoapCommon\SoapRequestFilter; -use BeSimple\SoapCommon\SoapResponse; -use BeSimple\SoapCommon\SoapResponseFilter; - -/** - * MIME filter. - * - * @author Andreas Schamberger - */ -class MimeFilter implements SoapRequestFilter, SoapResponseFilter -{ - /** - * Attachment type. - * - * @var int Helper::ATTACHMENTS_TYPE_SWA | Helper::ATTACHMENTS_TYPE_MTOM - */ - protected $attachmentType = Helper::ATTACHMENTS_TYPE_SWA; - - /** - * Constructor. - * - * @param int $attachmentType Helper::ATTACHMENTS_TYPE_SWA | Helper::ATTACHMENTS_TYPE_MTOM - */ - public function __construct($attachmentType) - { - $this->attachmentType = $attachmentType; - } - - /** - * Reset all properties to default values. - */ - public function resetFilter() - { - $this->attachmentType = Helper::ATTACHMENTS_TYPE_SWA; - } - - /** - * Modify the given request XML. - * - * @param \BeSimple\SoapCommon\SoapRequest $request SOAP request - * - * @return void - */ - public function filterRequest(SoapRequest $request) - { - // get attachments from request object - $attachmentsToSend = $request->getAttachments(); - - // build mime message if we have attachments - if (count($attachmentsToSend) > 0) { - $multipart = new MimeMultiPart(); - $soapPart = new MimePart($request->getContent(), 'text/xml', 'utf-8', MimePart::ENCODING_EIGHT_BIT); - $soapVersion = $request->getVersion(); - // change content type headers for MTOM with SOAP 1.1 - if ($soapVersion == SOAP_1_1 && $this->attachmentType & Helper::ATTACHMENTS_TYPE_MTOM) { - $multipart->setHeader('Content-Type', 'type', 'application/xop+xml'); - $multipart->setHeader('Content-Type', 'start-info', 'text/xml'); - $soapPart->setHeader('Content-Type', 'application/xop+xml'); - $soapPart->setHeader('Content-Type', 'type', 'text/xml'); - } - // change content type headers for SOAP 1.2 - elseif ($soapVersion == SOAP_1_2) { - $multipart->setHeader('Content-Type', 'type', 'application/soap+xml'); - $soapPart->setHeader('Content-Type', 'application/soap+xml'); - } - $multipart->addPart($soapPart, true); - foreach ($attachmentsToSend as $cid => $attachment) { - $multipart->addPart($attachment, false); - } - $request->setContent($multipart->getMimeMessage()); - - // TODO - $headers = $multipart->getHeadersForHttp(); - list($name, $contentType) = explode(': ', $headers[0]); - - $request->setContentType($contentType); - } - } - - /** - * Modify the given response XML. - * - * @param \BeSimple\SoapCommon\SoapResponse $response SOAP response - * - * @return void - */ - public function filterResponse(SoapResponse $response) - { - // array to store attachments - $attachmentsRecieved = array(); - - // check content type if it is a multipart mime message - $responseContentType = $response->getContentType(); - if (false !== stripos($responseContentType, 'multipart/related')) { - // parse mime message - $headers = array( - 'Content-Type' => trim($responseContentType), - ); - $multipart = MimeParser::parseMimeMessage($response->getContent(), $headers); - // get soap payload and update SoapResponse object - $soapPart = $multipart->getPart(); - // convert href -> myhref for external references as PHP throws exception in this case - // http://svn.php.net/viewvc/php/php-src/branches/PHP_5_4/ext/soap/php_encoding.c?view=markup#l3436 - $content = preg_replace('/href=(?!#)/', 'myhref=', $soapPart->getContent()); - $response->setContent($content); - $response->setContentType($soapPart->getHeader('Content-Type')); - // store attachments - $attachments = $multipart->getParts(false); - foreach ($attachments as $cid => $attachment) { - $attachmentsRecieved[$cid] = $attachment; - } - } - - // add attachments to response object - if (count($attachmentsRecieved) > 0) { - $response->setAttachments($attachmentsRecieved); - } - } -} \ No newline at end of file From d633516e1f19d79bae62ebdd356853a65a2c1f3c Mon Sep 17 00:00:00 2001 From: Andreas Schamberger Date: Sun, 8 Jan 2012 09:50:11 +0100 Subject: [PATCH 39/48] replace internal type converter interface with cleaner solution --- .../InternalTypeConverterInterface.php | 58 ------------------- .../Converter/MtomTypeConverter.php | 28 ++++++--- .../Converter/SoapKernelAwareInterface.php | 32 ++++++++++ .../SoapCommon/Converter/SwaTypeConverter.php | 26 +++++++-- src/BeSimple/SoapCommon/SoapKernel.php | 37 ------------ 5 files changed, 72 insertions(+), 109 deletions(-) delete mode 100644 src/BeSimple/SoapCommon/Converter/InternalTypeConverterInterface.php create mode 100644 src/BeSimple/SoapCommon/Converter/SoapKernelAwareInterface.php diff --git a/src/BeSimple/SoapCommon/Converter/InternalTypeConverterInterface.php b/src/BeSimple/SoapCommon/Converter/InternalTypeConverterInterface.php deleted file mode 100644 index 9c86b66..0000000 --- a/src/BeSimple/SoapCommon/Converter/InternalTypeConverterInterface.php +++ /dev/null @@ -1,58 +0,0 @@ - - * (c) Francis Besset - * - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -namespace BeSimple\SoapCommon\Converter; - -use BeSimple\SoapCommon\SoapKernel; - -/** - * Internal type converter interface. - * - * @author Andreas Schamberger - * @author Christian Kerl - */ -interface InternalTypeConverterInterface -{ - /** - * Get type namespace. - * - * @return string - */ - function getTypeNamespace(); - - /** - * Get type name. - * - * @return string - */ - function getTypeName(); - - /** - * Convert given XML string to PHP type. - * - * @param string $data XML string - * @param \BeSimple\SoapCommon\SoapKernel $soapKernel SoapKernel instance - * - * @return mixed - */ - function convertXmlToPhp($data, SoapKernel $soapKernel); - - /** - * Convert PHP type to XML string. - * - * @param mixed $data PHP type - * @param \BeSimple\SoapCommon\SoapKernel $soapKernel SoapKernel instance - * - * @return string - */ - function convertPhpToXml($data, SoapKernel $soapKernel); -} \ No newline at end of file diff --git a/src/BeSimple/SoapCommon/Converter/MtomTypeConverter.php b/src/BeSimple/SoapCommon/Converter/MtomTypeConverter.php index a9709e2..52ea961 100644 --- a/src/BeSimple/SoapCommon/Converter/MtomTypeConverter.php +++ b/src/BeSimple/SoapCommon/Converter/MtomTypeConverter.php @@ -15,17 +15,21 @@ namespace BeSimple\SoapCommon\Converter; use BeSimple\SoapCommon\Helper; use BeSimple\SoapCommon\Mime\Part as MimePart; use BeSimple\SoapCommon\SoapKernel; -use BeSimple\SoapCommon\SoapRequest as CommonSoapRequest; -use BeSimple\SoapCommon\SoapResponse as CommonSoapResponse; -use BeSimple\SoapCommon\Converter\InternalTypeConverterInterface; +use BeSimple\SoapCommon\Converter\SoapKernelAwareInterface; +use BeSimple\SoapCommon\Converter\TypeConverterInterface; /** * MTOM type converter. * * @author Andreas Schamberger */ -class MtomTypeConverter implements InternalTypeConverterInterface +class MtomTypeConverter implements TypeConverterInterface, SoapKernelAwareInterface { + /** + * @var \BeSimple\SoapCommon\SoapKernel $soapKernel SoapKernel instance + */ + protected $soapKernel = null; + /** * {@inheritDoc} */ @@ -45,7 +49,7 @@ class MtomTypeConverter implements InternalTypeConverterInterface /** * {@inheritDoc} */ - public function convertXmlToPhp($data, SoapKernel $soapKernel) + public function convertXmlToPhp($data) { $doc = new \DOMDocument(); $doc->loadXML($data); @@ -60,7 +64,7 @@ class MtomTypeConverter implements InternalTypeConverterInterface if ('cid:' === substr($ref, 0, 4)) { $contentId = urldecode(substr($ref, 4)); - if (null !== ($part = $soapKernel->getAttachment($contentId))) { + if (null !== ($part = $this->soapKernel->getAttachment($contentId))) { return $part->getContent(); } else { @@ -75,12 +79,12 @@ class MtomTypeConverter implements InternalTypeConverterInterface /** * {@inheritDoc} */ - public function convertPhpToXml($data, SoapKernel $soapKernel) + public function convertPhpToXml($data) { $part = new MimePart($data); $contentId = trim($part->getHeader('Content-ID'), '<>'); - $soapKernel->addAttachment($part); + $this->soapKernel->addAttachment($part); $doc = new \DOMDocument(); $node = $doc->createElement($this->getTypeName()); @@ -93,4 +97,12 @@ class MtomTypeConverter implements InternalTypeConverterInterface return $doc->saveXML(); } + + /** + * {@inheritDoc} + */ + public function setKernel(SoapKernel $soapKernel) + { + $this->soapKernel = $soapKernel; + } } diff --git a/src/BeSimple/SoapCommon/Converter/SoapKernelAwareInterface.php b/src/BeSimple/SoapCommon/Converter/SoapKernelAwareInterface.php new file mode 100644 index 0000000..655a258 --- /dev/null +++ b/src/BeSimple/SoapCommon/Converter/SoapKernelAwareInterface.php @@ -0,0 +1,32 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\SoapCommon\Converter; + +use BeSimple\SoapCommon\SoapKernel; + +/** + * Internal type converter interface. + * + * @author Andreas Schamberger + */ +interface SoapKernelAwareInterface +{ + /** + * Set SoapKernel instance. + * + * @param \BeSimple\SoapCommon\SoapKernel $soapKernel SoapKernel instance + * + * @return void + */ + function setKernel(SoapKernel $soapKernel); +} \ No newline at end of file diff --git a/src/BeSimple/SoapCommon/Converter/SwaTypeConverter.php b/src/BeSimple/SoapCommon/Converter/SwaTypeConverter.php index 32ba467..40d6372 100644 --- a/src/BeSimple/SoapCommon/Converter/SwaTypeConverter.php +++ b/src/BeSimple/SoapCommon/Converter/SwaTypeConverter.php @@ -15,15 +15,21 @@ namespace BeSimple\SoapCommon\Converter; use BeSimple\SoapCommon\Helper; use BeSimple\SoapCommon\Mime\Part as MimePart; use BeSimple\SoapCommon\SoapKernel; -use BeSimple\SoapCommon\Converter\InternalTypeConverterInterface; +use BeSimple\SoapCommon\Converter\SoapKernelAwareInterface; +use BeSimple\SoapCommon\Converter\TypeConverterInterface; /** * SwA type converter. * * @author Andreas Schamberger */ -class SwaTypeConverter implements InternalTypeConverterInterface +class SwaTypeConverter implements TypeConverterInterface, SoapKernelAwareInterface { + /** + * @var \BeSimple\SoapCommon\SoapKernel $soapKernel SoapKernel instance + */ + protected $soapKernel = null; + /** * {@inheritDoc} */ @@ -43,7 +49,7 @@ class SwaTypeConverter implements InternalTypeConverterInterface /** * {@inheritDoc} */ - public function convertXmlToPhp($data, SoapKernel $soapKernel) + public function convertXmlToPhp($data) { $doc = new \DOMDocument(); $doc->loadXML($data); @@ -55,7 +61,7 @@ class SwaTypeConverter implements InternalTypeConverterInterface if ('cid:' === substr($ref, 0, 4)) { $contentId = urldecode(substr($ref, 4)); - if (null !== ($part = $soapKernel->getAttachment($contentId))) { + if (null !== ($part = $this->soapKernel->getAttachment($contentId))) { return $part->getContent(); } else { @@ -70,13 +76,21 @@ class SwaTypeConverter implements InternalTypeConverterInterface /** * {@inheritDoc} */ - public function convertPhpToXml($data, SoapKernel $soapKernel) + public function convertPhpToXml($data) { $part = new MimePart($data); $contentId = trim($part->getHeader('Content-ID'), '<>'); - $soapKernel->addAttachment($part); + $this->soapKernel->addAttachment($part); return sprintf('<%s href="%s"/>', $this->getTypeName(), $contentId); } + + /** + * {@inheritDoc} + */ + public function setKernel(SoapKernel $soapKernel) + { + $this->soapKernel = $soapKernel; + } } diff --git a/src/BeSimple/SoapCommon/SoapKernel.php b/src/BeSimple/SoapCommon/SoapKernel.php index f0ead8c..da57db0 100644 --- a/src/BeSimple/SoapCommon/SoapKernel.php +++ b/src/BeSimple/SoapCommon/SoapKernel.php @@ -134,41 +134,4 @@ class SoapKernel $this->attachments = $response->getAttachments(); } - - /** - * Configure filter and type converter for SwA/MTOM. - * - * @param array &$options SOAP constructor options array. - * - * @return void - */ - public function configureMime(array &$options) - { - if (isset($options['attachment_type']) && Helper::ATTACHMENTS_TYPE_BASE64 !== $options['attachment_type']) { - // register mime filter in SoapKernel - $mimeFilter = new MimeFilter($options['attachment_type']); - $this->registerFilter($mimeFilter); - // configure type converter - if (Helper::ATTACHMENTS_TYPE_SWA === $options['attachment_type']) { - $converter = new SwaTypeConverter(); - } elseif (Helper::ATTACHMENTS_TYPE_MTOM === $options['attachment_type']) { - $converter = new MtomTypeConverter(); - } - // configure typemap - if (!isset($options['typemap'])) { - $options['typemap'] = array(); - } - $soapKernel = $this; - $options['typemap'][] = array( - 'type_name' => $converter->getTypeName(), - 'type_ns' => $converter->getTypeNamespace(), - 'from_xml' => function($input) use ($converter, $soapKernel) { - return $converter->convertXmlToPhp($input, $soapKernel); - }, - 'to_xml' => function($input) use ($converter, $soapKernel) { - return $converter->convertPhpToXml($input, $soapKernel); - }, - ); - } - } } \ No newline at end of file From e697d41cc31cf2e754ee00c018b35161a4e16d1e Mon Sep 17 00:00:00 2001 From: Andreas Schamberger Date: Sun, 29 Jan 2012 17:54:48 +0100 Subject: [PATCH 40/48] fix to make XmlMimeTypeConvert work --- src/BeSimple/SoapCommon/SoapMessage.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/BeSimple/SoapCommon/SoapMessage.php b/src/BeSimple/SoapCommon/SoapMessage.php index 97e0a04..35f9bb6 100644 --- a/src/BeSimple/SoapCommon/SoapMessage.php +++ b/src/BeSimple/SoapCommon/SoapMessage.php @@ -162,6 +162,7 @@ abstract class SoapMessage { if (null !== $this->contentDomDocument) { $this->content = $this->contentDomDocument->saveXML(); + $this->contentDomDocument = null; } return $this->content; } From 587e421dca3fa112463b281733f49baccf6d224f Mon Sep 17 00:00:00 2001 From: Andreas Schamberger Date: Sun, 29 Apr 2012 20:26:00 +0200 Subject: [PATCH 41/48] moved client specific code to SoapClient --- src/BeSimple/SoapCommon/SoapKernel.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/BeSimple/SoapCommon/SoapKernel.php b/src/BeSimple/SoapCommon/SoapKernel.php index da57db0..05cc826 100644 --- a/src/BeSimple/SoapCommon/SoapKernel.php +++ b/src/BeSimple/SoapCommon/SoapKernel.php @@ -113,9 +113,6 @@ class SoapKernel */ public function filterRequest(SoapRequest $request) { - $request->setAttachments($this->attachments); - $this->attachments = array(); - foreach ($this->requestFilters as $filter) { $filter->filterRequest($request); } @@ -131,7 +128,5 @@ class SoapKernel foreach ($this->responseFilters as $filter) { $filter->filterResponse($response); } - - $this->attachments = $response->getAttachments(); } } \ No newline at end of file From 17d065295906b219b4de273fc0123519f47c8f7d Mon Sep 17 00:00:00 2001 From: Francis Besset Date: Tue, 15 Jan 2013 22:30:56 +0100 Subject: [PATCH 42/48] Added composer.json --- composer.json | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 composer.json diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..9af4a30 --- /dev/null +++ b/composer.json @@ -0,0 +1,35 @@ +{ + "name": "besimple/soap-common", + "type": "library", + "description": "Build and consume SOAP Common based web services", + "keywords": [ "soap", "soap-common" ], + "homepage": "https://github.com/BeSimple/BeSimpleSoapCommon", + "license": "MIT", + "authors": [ + { + "name": "Francis Besset", + "email": "francis.besset@gmail.com" + }, + { + "name": "Christian Kerl", + "email": "christian-kerl@web.de" + }, + { + "name": "Andreas Schamberger", + "email": "mail@andreass.net" + } + ], + "require": { + "php": ">=5.3.0", + "ass/xmlsecurity": "dev-master" + }, + "require-dev": { + "mikey179/vfsStream": "dev-master" + }, + "autoload": { + "psr-0": { + "BeSimple\\SoapCommon": "BeSimple/SoapCommon/src/" + } + }, + "target-dir": "BeSimple/SoapCommon" +} From 3bd32c380d72bf740efb45bb1d4e9f9c68483a6f Mon Sep 17 00:00:00 2001 From: Francis Besset Date: Wed, 16 Jan 2013 11:53:33 +0100 Subject: [PATCH 43/48] Fixed tests --- composer.json | 2 +- tests/BeSimple/Tests/SoapCommon/CacheTest.php | 16 +++++---- tests/bootstrap.php | 35 ++----------------- 3 files changed, 12 insertions(+), 41 deletions(-) diff --git a/composer.json b/composer.json index 9af4a30..afde016 100644 --- a/composer.json +++ b/composer.json @@ -28,7 +28,7 @@ }, "autoload": { "psr-0": { - "BeSimple\\SoapCommon": "BeSimple/SoapCommon/src/" + "BeSimple\\SoapCommon": "src/" } }, "target-dir": "BeSimple/SoapCommon" diff --git a/tests/BeSimple/Tests/SoapCommon/CacheTest.php b/tests/BeSimple/Tests/SoapCommon/CacheTest.php index 602f894..79e936a 100644 --- a/tests/BeSimple/Tests/SoapCommon/CacheTest.php +++ b/tests/BeSimple/Tests/SoapCommon/CacheTest.php @@ -13,6 +13,8 @@ namespace BeSimple\Tests\SoapCommon\Soap; use BeSimple\SoapCommon\Cache; +use org\bovigo\vfs\vfsStream; +use org\bovigo\vfs\vfsStreamWrapper; class SoapRequestTest extends \PHPUnit_Framework_TestCase { @@ -48,19 +50,19 @@ class SoapRequestTest extends \PHPUnit_Framework_TestCase public function testSetDirectory() { - \vfsStream::setup('Fixtures'); + vfsStream::setup('Fixtures'); - $this->assertFalse(\vfsStreamWrapper::getRoot()->hasChild('foo')); - $dir = \vfsStream::url('Fixtures/foo'); + $this->assertFalse(vfsStreamWrapper::getRoot()->hasChild('foo')); + $dir = vfsStream::url('Fixtures/foo'); Cache::setDirectory($dir); $this->assertEquals($dir, Cache::getDirectory()); - $this->assertTrue(\vfsStreamWrapper::getRoot()->hasChild('foo')); + $this->assertTrue(vfsStreamWrapper::getRoot()->hasChild('foo')); - $this->assertFalse(\vfsStreamWrapper::getRoot()->hasChild('bar')); - $dir = \vfsStream::url('Fixtures/bar'); + $this->assertFalse(vfsStreamWrapper::getRoot()->hasChild('bar')); + $dir = vfsStream::url('Fixtures/bar'); Cache::setDirectory($dir); $this->assertEquals($dir, Cache::getDirectory()); - $this->assertTrue(\vfsStreamWrapper::getRoot()->hasChild('bar')); + $this->assertTrue(vfsStreamWrapper::getRoot()->hasChild('bar')); } public function testSetLifetime() diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 67150ad..008870c 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -1,36 +1,5 @@ add('BeSimple\Tests\SoapCommon\\', __DIR__); From c49afcd7f65c278ccc26cafb1c8b0bbadad33d4c Mon Sep 17 00:00:00 2001 From: Francis Besset Date: Wed, 16 Jan 2013 12:01:18 +0100 Subject: [PATCH 44/48] Added .travis.yml --- .travis.yml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..1c85c0d --- /dev/null +++ b/.travis.yml @@ -0,0 +1,7 @@ +language: php + +php: + - 5.3 + - 5.4 + +before_script: composer install --dev --prefer-source From 66e8de0f94952ec9816767a72b7f48a25da71a39 Mon Sep 17 00:00:00 2001 From: Rekky Date: Wed, 30 Jan 2013 19:38:53 +0400 Subject: [PATCH 45/48] Fix autoload path --- composer.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/composer.json b/composer.json index afde016..ab36100 100644 --- a/composer.json +++ b/composer.json @@ -30,6 +30,5 @@ "psr-0": { "BeSimple\\SoapCommon": "src/" } - }, - "target-dir": "BeSimple/SoapCommon" + } } From b4900b95c5f1f79d5da28547788c2f18794314b3 Mon Sep 17 00:00:00 2001 From: Francis Besset Date: Wed, 20 Feb 2013 21:58:49 +0100 Subject: [PATCH 46/48] Added Util\MessageBinder --- .../SoapCommon/Util/MessageBinder.php | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 src/BeSimple/SoapCommon/Util/MessageBinder.php diff --git a/src/BeSimple/SoapCommon/Util/MessageBinder.php b/src/BeSimple/SoapCommon/Util/MessageBinder.php new file mode 100644 index 0000000..d018355 --- /dev/null +++ b/src/BeSimple/SoapCommon/Util/MessageBinder.php @@ -0,0 +1,103 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\SoapCommon\Util; + +/** + * @author Francis Besset + */ +class MessageBinder +{ + /** + * @var Object + */ + protected $message; + + /** + * @var \ReflectionClass + */ + protected $reflectionClass; + + public function __construct($message) + { + if (!is_object($message)) { + throw new \InvalidArgumentException(sprintf('The message must be an object, %s given', gettype($message))); + } + + $this->message = $message; + $this->reflectionClass = new \ReflectionClass($this->message); + } + + public function readProperty($property) + { + if ($this->reflectionClass->hasMethod($getter = 'get'.$property)) { + if (!$this->reflectionClass->getMethod($getter)->isPublic()) { + throw new \RuntimeException(sprintf('Method "%s()" is not public in class "%s"', $getter, $this->reflectionClass->name)); + } + + $value = $this->message->{$getter}(); + } elseif ($this->reflectionClass->hasMethod($isser = 'is'.$property)) { + if (!$this->reflectionClass->getMethod($isser)->isPublic()) { + throw new \RuntimeException(sprintf('Method "%s()" is not public in class "%s"', $isser, $this->reflectionClass->name)); + } + + $value = $this->message->{$isser}(); + } elseif ($this->reflectionClass->hasMethod($hasser = 'has'.$property)) { + if (!$this->reflectionClass->getMethod($hasser)->isPublic()) { + throw new \RuntimeException(sprintf('Method "%s()" is not public in class "%s"', $hasser, $this->reflectionClass->name)); + } + + $value = $this->message->{$hasser}(); + } elseif ($this->reflectionClass->hasMethod('__get')) { + // needed to support magic method __get + $value = $this->message->{$property}; + } elseif ($this->reflectionClass->hasProperty($property)) { + if (!$this->reflectionClass->getProperty($property)->isPublic()) { + throw new \RuntimeException(sprintf('Property "%s" is not public in class "%s". Maybe you should create the method "%s()" or "%s()" or "%s()"?', $property, $this->reflectionClass->name, $getter, $isser, $hasser)); + } + + $value = $this->message->{$property}; + } elseif (property_exists($this->message, $property)) { + // needed to support \stdClass instances + $value = $this->message->{$property}; + } else { + throw new \RuntimeException(sprintf('Neither property "%s" nor method "%s()" nor method "%s()" exists in class "%s"', $property, $getter, $isser, $this->reflectionClass->name)); + } + + return $value; + } + + public function writeProperty($property, $value) + { + if ($this->reflectionClass->hasMethod($setter = 'set'.$property)) { + if (!$this->reflectionClass->getMethod($setter)->isPublic()) { + throw new \RuntimeException(sprintf('Method "%s()" is not public in class "%s"', $setter, $this->reflectionClass->name)); + } + + $this->message->{$setter}($value); + } elseif ($this->reflectionClass->hasMethod('__set')) { + // needed to support magic method __set + $this->message->{$property} = $value; + } elseif ($this->reflectionClass->hasProperty($property)) { + if (!$this->reflectionClass->getProperty($property)->isPublic()) { + throw new \RuntimeException(sprintf('Property "%s" is not public in class "%s". Maybe you should create the method "%s()"?', $property, $this->reflectionClass->name, $setter)); + } + + $this->message->{$property} = $value; + } elseif (property_exists($this->message, $property)) { + // needed to support \stdClass instances + $this->message->{$property} = $value; + } else { + throw new \RuntimeException(sprintf('Neither element "%s" nor method "%s()" exists in class "%s"', $property, $setter, $this->reflectionClass->name)); + } + } +} From 2b60cec19065b6cba65e2c320a937d803f87d8e0 Mon Sep 17 00:00:00 2001 From: Francis Besset Date: Thu, 21 Feb 2013 08:13:40 +0100 Subject: [PATCH 47/48] Fixed MessageBinder to avoid BC Break --- src/BeSimple/SoapCommon/Util/MessageBinder.php | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/BeSimple/SoapCommon/Util/MessageBinder.php b/src/BeSimple/SoapCommon/Util/MessageBinder.php index d018355..748c2c9 100644 --- a/src/BeSimple/SoapCommon/Util/MessageBinder.php +++ b/src/BeSimple/SoapCommon/Util/MessageBinder.php @@ -61,16 +61,15 @@ class MessageBinder // needed to support magic method __get $value = $this->message->{$property}; } elseif ($this->reflectionClass->hasProperty($property)) { - if (!$this->reflectionClass->getProperty($property)->isPublic()) { - throw new \RuntimeException(sprintf('Property "%s" is not public in class "%s". Maybe you should create the method "%s()" or "%s()" or "%s()"?', $property, $this->reflectionClass->name, $getter, $isser, $hasser)); + $p = $this->reflectionClass->getProperty($property); + if (!$p->isPublic()) { + $p->setAccessible(true); } - $value = $this->message->{$property}; + $value = $p->getValue($this->message); } elseif (property_exists($this->message, $property)) { // needed to support \stdClass instances $value = $this->message->{$property}; - } else { - throw new \RuntimeException(sprintf('Neither property "%s" nor method "%s()" nor method "%s()" exists in class "%s"', $property, $getter, $isser, $this->reflectionClass->name)); } return $value; @@ -88,16 +87,15 @@ class MessageBinder // needed to support magic method __set $this->message->{$property} = $value; } elseif ($this->reflectionClass->hasProperty($property)) { - if (!$this->reflectionClass->getProperty($property)->isPublic()) { - throw new \RuntimeException(sprintf('Property "%s" is not public in class "%s". Maybe you should create the method "%s()"?', $property, $this->reflectionClass->name, $setter)); + $p = $this->reflectionClass->getProperty($property); + if (!$p->isPublic()) { + $p->setAccessible(true); } - $this->message->{$property} = $value; + $p->setValue($this->message, $value); } elseif (property_exists($this->message, $property)) { // needed to support \stdClass instances $this->message->{$property} = $value; - } else { - throw new \RuntimeException(sprintf('Neither element "%s" nor method "%s()" exists in class "%s"', $property, $setter, $this->reflectionClass->name)); } } } From db885b2be7328fd8e663ee256df2d8570daeac3c Mon Sep 17 00:00:00 2001 From: Francis Besset Date: Wed, 27 Feb 2013 17:03:38 +0100 Subject: [PATCH 48/48] Added KeyValue type --- .../SoapCommon/Type/AbstractKeyValue.php | 34 +++++++++++++++++++ .../SoapCommon/Type/KeyValue/Boolean.php | 14 ++++++++ .../SoapCommon/Type/KeyValue/Date.php | 14 ++++++++ .../SoapCommon/Type/KeyValue/DateTime.php | 14 ++++++++ .../SoapCommon/Type/KeyValue/Float.php | 14 ++++++++ src/BeSimple/SoapCommon/Type/KeyValue/Int.php | 14 ++++++++ .../SoapCommon/Type/KeyValue/String.php | 14 ++++++++ 7 files changed, 118 insertions(+) create mode 100644 src/BeSimple/SoapCommon/Type/AbstractKeyValue.php create mode 100644 src/BeSimple/SoapCommon/Type/KeyValue/Boolean.php create mode 100644 src/BeSimple/SoapCommon/Type/KeyValue/Date.php create mode 100644 src/BeSimple/SoapCommon/Type/KeyValue/DateTime.php create mode 100644 src/BeSimple/SoapCommon/Type/KeyValue/Float.php create mode 100644 src/BeSimple/SoapCommon/Type/KeyValue/Int.php create mode 100644 src/BeSimple/SoapCommon/Type/KeyValue/String.php diff --git a/src/BeSimple/SoapCommon/Type/AbstractKeyValue.php b/src/BeSimple/SoapCommon/Type/AbstractKeyValue.php new file mode 100644 index 0000000..bf5d207 --- /dev/null +++ b/src/BeSimple/SoapCommon/Type/AbstractKeyValue.php @@ -0,0 +1,34 @@ +key = $key; + $this->value = $value; + } + + public function getKey() + { + return $this->key; + } + + public function getValue() + { + return $this->value; + } +} diff --git a/src/BeSimple/SoapCommon/Type/KeyValue/Boolean.php b/src/BeSimple/SoapCommon/Type/KeyValue/Boolean.php new file mode 100644 index 0000000..bfe684a --- /dev/null +++ b/src/BeSimple/SoapCommon/Type/KeyValue/Boolean.php @@ -0,0 +1,14 @@ +