Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
| 1 | <?php | ||
| 16 | final class UserAgentTest extends TestCase | ||
| 17 | { | ||
| 18 | /** @test */ | ||
| 19 | View Code Duplication | public function user_agents_are_bots() | |
| 20 |     { | ||
| 21 | $this->CrawlerDetect = new CrawlerDetect(); | ||
|  | |||
| 22 | $lines = file(__DIR__.'/crawlers.txt', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); | ||
| 23 | |||
| 24 |         foreach ($lines as $line) { | ||
| 25 | $test = $this->CrawlerDetect->isCrawler($line); | ||
| 26 | $this->assertTrue($test, $line); | ||
| 27 | } | ||
| 28 | } | ||
| 29 | |||
| 30 | /** @test */ | ||
| 31 | View Code Duplication | public function user_agents_are_devices() | |
| 32 |     { | ||
| 33 | $this->CrawlerDetect = new CrawlerDetect(); | ||
| 34 | $lines = file(__DIR__.'/devices.txt', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); | ||
| 35 | |||
| 36 |         foreach ($lines as $line) { | ||
| 37 | $test = $this->CrawlerDetect->isCrawler($line); | ||
| 38 | $this->assertFalse($test, $line); | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | /** @test */ | ||
| 43 | View Code Duplication | public function it_returns_correct_matched_bot_name() | |
| 44 |     { | ||
| 45 | $this->CrawlerDetect = new CrawlerDetect(); | ||
| 46 |         $this->CrawlerDetect->isCrawler('Mozilla/5.0 (iPhone; CPU iPhone OS 7_1 like Mac OS X) AppleWebKit (KHTML, like Gecko) Mobile (compatible; Yahoo Ad monitoring; https://help.yahoo.com/kb/yahoo-ad-monitoring-SLN24857.html)'); | ||
| 47 | |||
| 48 | $matches = $this->CrawlerDetect->getMatches(); | ||
| 49 | |||
| 50 | $this->assertEquals($this->CrawlerDetect->getMatches(), 'monitoring', $matches); | ||
| 51 | } | ||
| 52 | |||
| 53 | /** @test */ | ||
| 54 | View Code Duplication | public function it_returns_full_matched_bot_name() | |
| 55 |     { | ||
| 56 | $this->CrawlerDetect = new CrawlerDetect(); | ||
| 57 |         $this->CrawlerDetect->isCrawler('somenaughtybot'); | ||
| 58 | |||
| 59 | $matches = $this->CrawlerDetect->getMatches(); | ||
| 60 | |||
| 61 | $this->assertEquals($this->CrawlerDetect->getMatches(), 'somenaughtybot', $matches); | ||
| 62 | } | ||
| 63 | |||
| 64 | /** @test */ | ||
| 65 | View Code Duplication | public function it_returns_null_when_no_bot_detected() | |
| 66 |     { | ||
| 67 | $this->CrawlerDetect = new CrawlerDetect(); | ||
| 68 |         $this->CrawlerDetect->isCrawler('nothing to see here'); | ||
| 69 | |||
| 70 | $matches = $this->CrawlerDetect->getMatches(); | ||
| 71 | |||
| 72 | $this->assertNull($this->CrawlerDetect->getMatches()); | ||
| 73 | } | ||
| 74 | |||
| 75 | /** @test */ | ||
| 76 | public function empty_user_agent() | ||
| 77 |     { | ||
| 78 | $this->CrawlerDetect = new CrawlerDetect(); | ||
| 79 |         $test = $this->CrawlerDetect->isCrawler('      '); | ||
| 80 | |||
| 81 | $this->assertFalse($test); | ||
| 82 | } | ||
| 83 | |||
| 84 | /** @test */ | ||
| 85 | public function current_visitor() | ||
| 86 |     { | ||
| 87 |         $headers = (array) json_decode('{"DOCUMENT_ROOT":"\/home\/test\/public_html","GATEWAY_INTERFACE":"CGI\/1.1","HTTP_ACCEPT":"*\/*","HTTP_ACCEPT_ENCODING":"gzip, deflate","HTTP_CACHE_CONTROL":"no-cache","HTTP_CONNECTION":"Keep-Alive","HTTP_FROM":"bingbot(at)microsoft.com","HTTP_HOST":"www.test.com","HTTP_PRAGMA":"no-cache","HTTP_USER_AGENT":"Mozilla\/5.0 (compatible; bingbot\/2.0; +http:\/\/www.bing.com\/bingbot.htm)","PATH":"\/bin:\/usr\/bin","QUERY_STRING":"order=closingDate","REDIRECT_STATUS":"200","REMOTE_ADDR":"127.0.0.1","REMOTE_PORT":"3360","REQUEST_METHOD":"GET","REQUEST_URI":"\/?test=testing","SCRIPT_FILENAME":"\/home\/test\/public_html\/index.php","SCRIPT_NAME":"\/index.php","SERVER_ADDR":"127.0.0.1","SERVER_ADMIN":"[email protected]","SERVER_NAME":"www.test.com","SERVER_PORT":"80","SERVER_PROTOCOL":"HTTP\/1.1","SERVER_SIGNATURE":"","SERVER_SOFTWARE":"Apache","UNIQUE_ID":"Vx6MENRxerBUSDEQgFLAAAAAS","PHP_SELF":"\/index.php","REQUEST_TIME_FLOAT":1461619728.0705,"REQUEST_TIME":1461619728}'); | ||
| 88 | |||
| 89 | $cd = new CrawlerDetect($headers); | ||
| 90 | |||
| 91 | $this->assertTrue($cd->isCrawler()); | ||
| 92 | } | ||
| 93 | |||
| 94 | /** @test */ | ||
| 95 | public function user_agent_passed_via_contructor() | ||
| 96 |     { | ||
| 97 | $cd = new CrawlerDetect(null, 'Mozilla/5.0 (iPhone; CPU iPhone OS 7_1 like Mac OS X) AppleWebKit (KHTML, like Gecko) Mobile (compatible; Yahoo Ad monitoring; https://help.yahoo.com/kb/yahoo-ad-monitoring-SLN24857.html)'); | ||
| 98 | |||
| 99 | $this->assertTrue($cd->isCrawler()); | ||
| 100 | } | ||
| 101 | |||
| 102 | /** @test */ | ||
| 103 | public function http_from_header() | ||
| 104 |     { | ||
| 105 |         $headers = (array) json_decode('{"DOCUMENT_ROOT":"\/home\/test\/public_html","GATEWAY_INTERFACE":"CGI\/1.1","HTTP_ACCEPT":"*\/*","HTTP_ACCEPT_ENCODING":"gzip, deflate","HTTP_CACHE_CONTROL":"no-cache","HTTP_CONNECTION":"Keep-Alive","HTTP_FROM":"googlebot(at)googlebot.com","HTTP_HOST":"www.test.com","HTTP_PRAGMA":"no-cache","HTTP_USER_AGENT":"Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/28.0.1500.71 Safari\/537.36","PATH":"\/bin:\/usr\/bin","QUERY_STRING":"order=closingDate","REDIRECT_STATUS":"200","REMOTE_ADDR":"127.0.0.1","REMOTE_PORT":"3360","REQUEST_METHOD":"GET","REQUEST_URI":"\/?test=testing","SCRIPT_FILENAME":"\/home\/test\/public_html\/index.php","SCRIPT_NAME":"\/index.php","SERVER_ADDR":"127.0.0.1","SERVER_ADMIN":"[email protected]","SERVER_NAME":"www.test.com","SERVER_PORT":"80","SERVER_PROTOCOL":"HTTP\/1.1","SERVER_SIGNATURE":"","SERVER_SOFTWARE":"Apache","UNIQUE_ID":"Vx6MENRxerBUSDEQgFLAAAAAS","PHP_SELF":"\/index.php","REQUEST_TIME_FLOAT":1461619728.0705,"REQUEST_TIME":1461619728}'); | ||
| 106 | |||
| 107 | $cd = new CrawlerDetect($headers); | ||
| 108 | |||
| 109 | $this->assertTrue($cd->isCrawler()); | ||
| 110 | } | ||
| 111 | |||
| 112 | /** @test */ | ||
| 113 | public function matches_does_not_persit_across_multiple_calls() | ||
| 114 |     { | ||
| 115 | $this->CrawlerDetect = new CrawlerDetect(); | ||
| 116 |         $this->CrawlerDetect->isCrawler('Mozilla/5.0 (iPhone; CPU iPhone OS 7_1 like Mac OS X) AppleWebKit (KHTML, like Gecko) Mobile (compatible; Yahoo Ad monitoring; https://help.yahoo.com/kb/yahoo-ad-monitoring-SLN24857.html)'); | ||
| 117 | $matches = $this->CrawlerDetect->getMatches(); | ||
| 118 | $this->assertEquals($this->CrawlerDetect->getMatches(), 'monitoring', $matches); | ||
| 119 | |||
| 120 |         $this->CrawlerDetect->isCrawler('This should not match'); | ||
| 121 | $matches = $this->CrawlerDetect->getMatches(); | ||
| 122 | $this->assertNull($this->CrawlerDetect->getMatches()); | ||
| 123 | } | ||
| 124 | |||
| 125 | /** @test */ | ||
| 126 | public function the_regex_patterns_are_unique() | ||
| 132 | |||
| 133 | /** @test */ | ||
| 134 | public function there_are_no_regex_collisions() | ||
| 149 | } | ||
| 150 | 
In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:
Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion: