diff --git a/src/Sav/Writer.php b/src/Sav/Writer.php index 36260d8..c6e27db 100644 --- a/src/Sav/Writer.php +++ b/src/Sav/Writer.php @@ -59,7 +59,7 @@ public function __construct($data = [], $buffer = null) $this->write($data); } } - + /** * @param array $data * @param string $file @@ -101,7 +101,7 @@ public function write($data) $this->data = new Record\Data(); - $nominalIdx = 0; + $nominalIdx = 0; /** @var Variable $var */ // for ($idx = 0; $idx <= $variablesCount; $idx++) { @@ -110,12 +110,15 @@ public function write($data) $var = new Variable($var); } - //if (! preg_match('/^[A-Za-z0-9_]+$/', $var->name)) { // UTF-8 and '.' characters could pass here - if (!preg_match('/^[A-Za-z0-9_\.\x{4e00}-\x{9fa5}]+$/u', $var->name)) { + if (!preg_match('/^(?!#|\$|\.)[\w0-9_.#@$\x{4e00}-\x{9fa5}]+(?name)) { throw new \InvalidArgumentException(sprintf('Variable name `%s` contains an illegal character.', $var->name)); } + if (in_array($var->name, ['ALL', 'AND', 'BY', 'EQ', 'GE', 'GT', 'LE', 'LT', 'NE', 'NOT', 'OR', 'TO', 'WITH'])) { + $var->name = \uniqid($var->name); + } + if (empty($var->width)) { throw new \InvalidArgumentException(sprintf('Invalid field width. Should be an integer number greater than zero.')); } diff --git a/tests/NamingTest.php b/tests/NamingTest.php new file mode 100644 index 0000000..b417ed8 --- /dev/null +++ b/tests/NamingTest.php @@ -0,0 +1,80 @@ + [ + 'prodName' => '@(#) IBM SPSS STATISTICS', + 'layoutCode' => 2, + 'creationDate' => date('d M y'), + 'creationTime' => date('H:i:s'), + ], + 'variables' => [ + [ + 'name' => 'WITH', + 'width' => 16, + 'format' => 1, + ], + [ + 'name' => 'OR', + 'format' => 5, + ], + ], + ]; + $writer = new Writer($data); + + $buffer = $writer->getBuffer(); + $buffer->rewind(); + + $reader = Reader::fromString($buffer->getStream())->read(); + + $this->assertRegExp('/^' . $data['variables'][0]['name'] . '[\w]{13}$/', $reader->info[LongVariableNames::SUBTYPE]['V00001']); + $this->assertRegExp('/^' . $data['variables'][1]['name'] . '[\w]{13}$/', $reader->info[LongVariableNames::SUBTYPE]['V00002']); + } + + + /** + * @dataProvider illegalNameProvider + */ + public function testIllegalNames($name) + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage(sprintf('Variable name `%s` contains an illegal character.', $name)); + + $data = [ + 'header' => [ + 'prodName' => '@(#) IBM SPSS STATISTICS', + 'layoutCode' => 2, + 'creationDate' => date('d M y'), + 'creationTime' => date('H:i:s'), + ], + 'variables' => [ + [ + 'name' => $name, + 'width' => 16, + 'format' => 1, + ], + ], + ]; + + new Writer($data); + } +} diff --git a/tests/SavRandomReadWriteTest.php b/tests/SavRandomReadWriteTest.php index 0814ed7..4b5f22a 100644 --- a/tests/SavRandomReadWriteTest.php +++ b/tests/SavRandomReadWriteTest.php @@ -37,7 +37,7 @@ public function provider() $count = 1; // mt_rand(1, 20); for ($i = 0; $i < $count; $i++) { $var = $this->generateVariable([ - 'id' => $this->generateRandomString(mt_rand(2, 100)), + 'id' => $this->generateRandomString(mt_rand(2, 100)) . 'a', 'casesCount' => $header['casesCount'], ] ); @@ -50,7 +50,7 @@ public function provider() $header['casesCount'] = 5; for ($i = 0; $i < 100; $i++) { $variable = $this->generateVariable([ - 'id' => $this->generateRandomString(mt_rand(2, 100)), + 'id' => $this->generateRandomString(mt_rand(2, 100)) . 'a', 'casesCount' => $header['casesCount'], ]); $header['nominalCaseSize'] = Utils::widthToOcts($variable['width']); diff --git a/tests/WriteMultibyteTest.php b/tests/WriteMultibyteTest.php index d00b3e9..c42bea7 100644 --- a/tests/WriteMultibyteTest.php +++ b/tests/WriteMultibyteTest.php @@ -3,6 +3,7 @@ namespace SPSS\Tests; use SPSS\Sav\Reader; +use SPSS\Sav\Record\Info\LongVariableNames; use SPSS\Sav\Variable; use SPSS\Sav\Writer; @@ -11,7 +12,7 @@ class WriteMultibyteTest extends TestCase public function testMultiByteLabel() { $data = [ - 'header' => [ + 'header' => [ 'prodName' => '@(#) IBM SPSS STATISTICS', 'layoutCode' => 2, 'creationDate' => date('d M y'), @@ -55,13 +56,12 @@ public function testMultiByteLabel() /** * ISSUE #20. - * * Chinese value labels seem to work fine, but free text does not work */ public function testChinese() { $input = [ - 'header' => [ + 'header' => [ 'prodName' => '@(#) IBM SPSS STATISTICS 64-bit Macintosh 23.0.0.0', 'creationDate' => '05 Oct 18', 'creationTime' => '01:36:53', @@ -124,4 +124,40 @@ public function testChinese() $expected[2][1] = $input['variables'][1]['data'][2]; $this->assertEquals($expected, $reader->data); } + + public function testMultiByteVariableName() + { + $data = [ + 'header' => [ + 'prodName' => '@(#) IBM SPSS STATISTICS', + 'layoutCode' => 2, + 'creationDate' => date('d M y'), + 'creationTime' => date('H:i:s'), + ], + 'variables' => [ + [ + 'name' => 'Å', + 'width' => 16, + 'format' => 1, + ], + [ + 'name' => 'DSADÆØØÅÅÅÅÅSAAA', + 'format' => 5, + ], + ], + ]; + $writer = new Writer($data); + + $buffer = $writer->getBuffer(); + $buffer->rewind(); + + $reader = Reader::fromString($buffer->getStream())->read(); + + // Short variable name + $this->assertEquals($data['variables'][0]['name'], $reader->info[LongVariableNames::SUBTYPE]['V00001']); + // Long variable name + $this->assertEquals($data['variables'][1]['name'], $reader->info[LongVariableNames::SUBTYPE]['V00002']); + + } + }