@@ -91,7 +91,7 @@ public function testNonIntUmaskThrowsInvalidArgumentException()
9191
9292 $ this ->getMock (
9393 'Doctrine\Common\Cache\FileCache ' ,
94- array (),
94+ array (' doFetch ' , ' doContains ' , ' doSave ' ),
9595 array ('' , '' , 'invalid ' )
9696 );
9797 }
@@ -129,4 +129,86 @@ public function testGetExtensionReturnsExtensionString()
129129
130130 $ this ->assertEquals ($ extension , $ actualExtension );
131131 }
132+
133+ /**
134+ * @runInSeparateProcess
135+ *
136+ * @covers \Doctrine\Common\Cache\FileCache::getFilename
137+ */
138+ public function testWindowsPathLengthLimitationsAreCorrectlyRespected ()
139+ {
140+ if (! defined ('PHP_WINDOWS_VERSION_BUILD ' )) {
141+ define ('PHP_WINDOWS_VERSION_BUILD ' , 'Yes, this is the "usual suspect", with the usual limitations ' );
142+ }
143+
144+ // Not using __DIR__ because it can get screwed up when xdebug debugger is attached.
145+ $ basePath = realpath (sys_get_temp_dir ());
146+
147+ // If the base path length is even, pad it with '/aa' so it's odd.
148+ // That way we can test a path of length 260
149+ // 260 characters is too large - null terminator is included in allowable length
150+ if (!(strlen ($ basePath ) % 1 )) {
151+ $ basePath .= DIRECTORY_SEPARATOR . "aa " ;
152+ }
153+
154+ $ fileCache = $ this ->getMockForAbstractClass (
155+ 'Doctrine\Common\Cache\FileCache ' ,
156+ array ($ basePath , '.doctrine.cache ' )
157+ );
158+
159+ $ baseDirLength = strlen ($ basePath );
160+ $ extensionLength = strlen ('.doctrine.cache ' );
161+ $ windowsPathMaxLength = 259 ; // 260 bytes including null terminator
162+ $ maxKeyLength = $ windowsPathMaxLength - ($ baseDirLength + $ extensionLength );
163+
164+ self ::assertSame ('61 ' , bin2hex ('a ' ), '(added just for clarity and system integrity check) ' );
165+
166+ $ tooLongKey = str_repeat ('a ' , ($ maxKeyLength / 2 ) - 1 ); // note: 1 char because reasons, ok?
167+ $ fittingKey = str_repeat ('a ' , ($ maxKeyLength / 2 ) - 2 ); // note: 2 chars due to path separator added as well
168+
169+ $ tooLongKeyHash = hash ('sha256 ' , $ tooLongKey );
170+ $ fittingKeyHash = hash ('sha256 ' , $ fittingKey );
171+
172+ $ getFileName = new \ReflectionMethod ($ fileCache , 'getFilename ' );
173+
174+ $ getFileName ->setAccessible (true );
175+
176+ $ this ->assertEquals (
177+ $ windowsPathMaxLength + 1 ,
178+ strlen ($ basePath
179+ . DIRECTORY_SEPARATOR
180+ . substr ($ tooLongKeyHash , 0 , 2 )
181+ . DIRECTORY_SEPARATOR
182+ . bin2hex ($ tooLongKey )
183+ . '.doctrine.cache ' ),
184+ sprintf ('Key expected to be too long is %d characters long ' , $ windowsPathMaxLength + 1 )
185+ );
186+
187+ $ this ->assertSame (
188+ $ basePath . DIRECTORY_SEPARATOR . substr ($ tooLongKeyHash , 0 , 2 ) . DIRECTORY_SEPARATOR . '_ ' . $ tooLongKeyHash . '.doctrine.cache ' ,
189+ $ getFileName ->invoke ($ fileCache , $ tooLongKey ),
190+ 'Keys over the limit of the allowed length are hashed correctly '
191+ );
192+
193+ $ this ->assertLessThan (
194+ $ windowsPathMaxLength ,
195+ strlen ($ basePath
196+ . DIRECTORY_SEPARATOR
197+ . substr ($ fittingKeyHash , 0 , 2 )
198+ . DIRECTORY_SEPARATOR
199+ . bin2hex ($ fittingKey )
200+ . '.doctrine.cache ' ),
201+ sprintf (
202+ 'Key expected to fit the length limit(%d) is less than %d characters long ' ,
203+ $ windowsPathMaxLength ,
204+ $ windowsPathMaxLength
205+ )
206+ );
207+
208+ $ this ->assertSame (
209+ $ basePath . DIRECTORY_SEPARATOR . substr ($ fittingKeyHash , 0 , 2 ) . DIRECTORY_SEPARATOR . bin2hex ($ fittingKey ) . '.doctrine.cache ' ,
210+ $ getFileName ->invoke ($ fileCache , $ fittingKey ),
211+ 'Keys below limit of the allowed length are used directly, unhashed '
212+ );
213+ }
132214}
0 commit comments