├── LICENSE.md
├── README.md
├── composer.json
└── src
├── Agent.php
├── AgentServiceProvider.php
└── Facades
└── Agent.php
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Jens Segers
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Agent
2 | =====
3 |
4 | [](https://packagist.org/packages/jenssegers/agent) [](https://packagist.org/packages/jenssegers/agent) [](https://travis-ci.org/jenssegers/agent) [](https://coveralls.io/r/jenssegers/agent) [](https://www.paypal.me/jenssegers)
5 |
6 | A PHP desktop/mobile user agent parser with support for Laravel, based on [Mobile Detect](https://github.com/serbanghita/Mobile-Detect) with desktop support and additional functionality.
7 |
8 |
9 |
10 |
11 |
12 | Installation
13 | ------------
14 |
15 | Install using composer:
16 |
17 | ```bash
18 | composer require jenssegers/agent
19 | ```
20 |
21 | Laravel (optional)
22 | ------------------
23 |
24 | Add the service provider in `config/app.php`:
25 |
26 | ```php
27 | Jenssegers\Agent\AgentServiceProvider::class,
28 | ```
29 |
30 | And add the Agent alias to `config/app.php`:
31 |
32 | ```php
33 | 'Agent' => Jenssegers\Agent\Facades\Agent::class,
34 | ```
35 |
36 | Basic Usage
37 | -----------
38 |
39 | Start by creating an `Agent` instance (or use the `Agent` Facade if you are using Laravel):
40 |
41 | ```php
42 | use Jenssegers\Agent\Agent;
43 |
44 | $agent = new Agent();
45 | ```
46 |
47 | If you want to parse user agents other than the current request in CLI scripts for example, you can use the `setUserAgent` and `setHttpHeaders` methods:
48 |
49 | ```php
50 | $agent->setUserAgent('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.13+ (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2');
51 | $agent->setHttpHeaders($headers);
52 | ```
53 |
54 | All of the original [Mobile Detect](https://github.com/serbanghita/Mobile-Detect) methods are still available, check out some original examples at https://github.com/serbanghita/Mobile-Detect/wiki/Code-examples
55 |
56 | ### Is?
57 |
58 | Check for a certain property in the user agent.
59 |
60 | ```php
61 | $agent->is('Windows');
62 | $agent->is('Firefox');
63 | $agent->is('iPhone');
64 | $agent->is('OS X');
65 | ```
66 |
67 | ### Magic is-method
68 |
69 | Magic method that does the same as the previous `is()` method:
70 |
71 | ```php
72 | $agent->isAndroidOS();
73 | $agent->isNexus();
74 | $agent->isSafari();
75 | ```
76 |
77 | ### Mobile detection
78 |
79 | Check for mobile device:
80 |
81 | ```php
82 | $agent->isMobile();
83 | $agent->isTablet();
84 | ```
85 |
86 | ### Match user agent
87 |
88 | Search the user agent with a regular expression:
89 |
90 | ```php
91 | $agent->match('regexp');
92 | ```
93 |
94 | Additional Functionality
95 | ------------------------
96 |
97 | ### Accept languages
98 |
99 | Get the browser's accept languages. Example:
100 |
101 | ```php
102 | $languages = $agent->languages();
103 | // ['nl-nl', 'nl', 'en-us', 'en']
104 | ```
105 |
106 | ### Device name
107 |
108 | Get the device name, if mobile. (iPhone, Nexus, AsusTablet, ...)
109 |
110 | ```php
111 | $device = $agent->device();
112 | ```
113 |
114 | ### Operating system name
115 |
116 | Get the operating system. (Ubuntu, Windows, OS X, ...)
117 |
118 | ```php
119 | $platform = $agent->platform();
120 | ```
121 |
122 | ### Browser name
123 |
124 | Get the browser name. (Chrome, IE, Safari, Firefox, ...)
125 |
126 | ```php
127 | $browser = $agent->browser();
128 | ```
129 |
130 | ### Desktop detection
131 |
132 | Check if the user is using a desktop device.
133 |
134 | ```php
135 | $agent->isDesktop();
136 | ```
137 |
138 | *This checks if a user is not a mobile device, tablet or robot.*
139 |
140 | ### Phone detection
141 |
142 | Check if the user is using a phone device.
143 |
144 | ```php
145 | $agent->isPhone();
146 | ```
147 |
148 | ### Robot detection
149 |
150 | Check if the user is a robot. This uses [jaybizzle/crawler-detect](https://github.com/JayBizzle/Crawler-Detect) to do the actual robot detection.
151 |
152 | ```php
153 | $agent->isRobot();
154 | ```
155 |
156 | ### Robot name
157 |
158 | Get the robot name.
159 |
160 | ```php
161 | $robot = $agent->robot();
162 | ```
163 |
164 | ### Browser/platform version
165 |
166 | MobileDetect recently added a `version` method that can get the version number for components. To get the browser or platform version you can use:
167 |
168 | ```php
169 | $browser = $agent->browser();
170 | $version = $agent->version($browser);
171 |
172 | $platform = $agent->platform();
173 | $version = $agent->version($platform);
174 | ```
175 |
176 | *Note, the version method is still in beta, so it might not return the correct result.*
177 |
178 | ## License
179 |
180 | Laravel User Agent is licensed under [The MIT License (MIT)](LICENSE).
181 |
182 | ## Security contact information
183 |
184 | To report a security vulnerability, follow [these steps](https://tidelift.com/security).
185 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jenssegers/agent",
3 | "description": "Desktop/mobile user agent parser with support for Laravel, based on Mobiledetect",
4 | "keywords": ["laravel", "useragent", "agent", "user agent", "browser", "platform", "mobile", "desktop"],
5 | "homepage": "https://github.com/jenssegers/agent",
6 | "license" : "MIT",
7 | "authors": [
8 | {
9 | "name": "Jens Segers",
10 | "homepage": "https://jenssegers.com"
11 | }
12 | ],
13 | "require": {
14 | "php": ">=5.6",
15 | "mobiledetect/mobiledetectlib": "^2.7.6",
16 | "jaybizzle/crawler-detect": "^1.2"
17 | },
18 | "require-dev": {
19 | "phpunit/phpunit": "^5.0|^6.0|^7.0",
20 | "php-coveralls/php-coveralls": "^2.1"
21 | },
22 | "autoload": {
23 | "psr-4": {
24 | "Jenssegers\\Agent\\": "src/"
25 | }
26 | },
27 | "extra": {
28 | "branch-alias": {
29 | "dev-master": "3.0-dev"
30 | },
31 | "laravel": {
32 | "providers": [
33 | "Jenssegers\\Agent\\AgentServiceProvider"
34 | ],
35 | "aliases": {
36 | "Agent": "Jenssegers\\Agent\\Facades\\Agent"
37 | }
38 | }
39 | },
40 | "suggest": {
41 | "illuminate/support": "Required for laravel service providers"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Agent.php:
--------------------------------------------------------------------------------
1 | 'Macintosh',
17 | ];
18 |
19 | /**
20 | * List of additional operating systems.
21 | * @var array
22 | */
23 | protected static $additionalOperatingSystems = [
24 | 'Windows' => 'Windows',
25 | 'Windows NT' => 'Windows NT',
26 | 'OS X' => 'Mac OS X',
27 | 'Debian' => 'Debian',
28 | 'Ubuntu' => 'Ubuntu',
29 | 'Macintosh' => 'PPC',
30 | 'OpenBSD' => 'OpenBSD',
31 | 'Linux' => 'Linux',
32 | 'ChromeOS' => 'CrOS',
33 | ];
34 |
35 | /**
36 | * List of additional browsers.
37 | * @var array
38 | */
39 | protected static $additionalBrowsers = [
40 | 'Opera Mini' => 'Opera Mini',
41 | 'Opera' => 'Opera|OPR',
42 | 'Edge' => 'Edge|Edg',
43 | 'Coc Coc' => 'coc_coc_browser',
44 | 'UCBrowser' => 'UCBrowser',
45 | 'Vivaldi' => 'Vivaldi',
46 | 'Chrome' => 'Chrome',
47 | 'Firefox' => 'Firefox',
48 | 'Safari' => 'Safari',
49 | 'IE' => 'MSIE|IEMobile|MSIEMobile|Trident/[.0-9]+',
50 | 'Netscape' => 'Netscape',
51 | 'Mozilla' => 'Mozilla',
52 | 'WeChat' => 'MicroMessenger',
53 | ];
54 |
55 | /**
56 | * List of additional properties.
57 | * @var array
58 | */
59 | protected static $additionalProperties = [
60 | // Operating systems
61 | 'Windows' => 'Windows NT [VER]',
62 | 'Windows NT' => 'Windows NT [VER]',
63 | 'OS X' => 'OS X [VER]',
64 | 'BlackBerryOS' => ['BlackBerry[\w]+/[VER]', 'BlackBerry.*Version/[VER]', 'Version/[VER]'],
65 | 'AndroidOS' => 'Android [VER]',
66 | 'ChromeOS' => 'CrOS x86_64 [VER]',
67 |
68 | // Browsers
69 | 'Opera Mini' => 'Opera Mini/[VER]',
70 | 'Opera' => [' OPR/[VER]', 'Opera Mini/[VER]', 'Version/[VER]', 'Opera [VER]'],
71 | 'Netscape' => 'Netscape/[VER]',
72 | 'Mozilla' => 'rv:[VER]',
73 | 'IE' => ['IEMobile/[VER];', 'IEMobile [VER]', 'MSIE [VER];', 'rv:[VER]'],
74 | 'Edge' => ['Edge/[VER]', 'Edg/[VER]'],
75 | 'Vivaldi' => 'Vivaldi/[VER]',
76 | 'Coc Coc' => 'coc_coc_browser/[VER]',
77 | ];
78 |
79 | /**
80 | * @var CrawlerDetect
81 | */
82 | protected static $crawlerDetect;
83 |
84 | /**
85 | * Get all detection rules. These rules include the additional
86 | * platforms and browsers and utilities.
87 | * @return array
88 | */
89 | public static function getDetectionRulesExtended()
90 | {
91 | static $rules;
92 |
93 | if (!$rules) {
94 | $rules = static::mergeRules(
95 | static::$desktopDevices, // NEW
96 | static::$phoneDevices,
97 | static::$tabletDevices,
98 | static::$operatingSystems,
99 | static::$additionalOperatingSystems, // NEW
100 | static::$browsers,
101 | static::$additionalBrowsers, // NEW
102 | static::$utilities
103 | );
104 | }
105 |
106 | return $rules;
107 | }
108 |
109 | public function getRules()
110 | {
111 | if ($this->detectionType === static::DETECTION_TYPE_EXTENDED) {
112 | return static::getDetectionRulesExtended();
113 | }
114 |
115 | return static::getMobileDetectionRules();
116 | }
117 |
118 | /**
119 | * @return CrawlerDetect
120 | */
121 | public function getCrawlerDetect()
122 | {
123 | if (static::$crawlerDetect === null) {
124 | static::$crawlerDetect = new CrawlerDetect();
125 | }
126 |
127 | return static::$crawlerDetect;
128 | }
129 |
130 | public static function getBrowsers()
131 | {
132 | return static::mergeRules(
133 | static::$additionalBrowsers,
134 | static::$browsers
135 | );
136 | }
137 |
138 | public static function getOperatingSystems()
139 | {
140 | return static::mergeRules(
141 | static::$operatingSystems,
142 | static::$additionalOperatingSystems
143 | );
144 | }
145 |
146 | public static function getPlatforms()
147 | {
148 | return static::mergeRules(
149 | static::$operatingSystems,
150 | static::$additionalOperatingSystems
151 | );
152 | }
153 |
154 | public static function getDesktopDevices()
155 | {
156 | return static::$desktopDevices;
157 | }
158 |
159 | public static function getProperties()
160 | {
161 | return static::mergeRules(
162 | static::$additionalProperties,
163 | static::$properties
164 | );
165 | }
166 |
167 | /**
168 | * Get accept languages.
169 | * @param string $acceptLanguage
170 | * @return array
171 | */
172 | public function languages($acceptLanguage = null)
173 | {
174 | if ($acceptLanguage === null) {
175 | $acceptLanguage = $this->getHttpHeader('HTTP_ACCEPT_LANGUAGE');
176 | }
177 |
178 | if (!$acceptLanguage) {
179 | return [];
180 | }
181 |
182 | $languages = [];
183 |
184 | // Parse accept language string.
185 | foreach (explode(',', $acceptLanguage) as $piece) {
186 | $parts = explode(';', $piece);
187 | $language = strtolower($parts[0]);
188 | $priority = empty($parts[1]) ? 1. : floatval(str_replace('q=', '', $parts[1]));
189 |
190 | $languages[$language] = $priority;
191 | }
192 |
193 | // Sort languages by priority.
194 | arsort($languages);
195 |
196 | return array_keys($languages);
197 | }
198 |
199 | /**
200 | * Match a detection rule and return the matched key.
201 | * @param array $rules
202 | * @param string|null $userAgent
203 | * @return string|bool
204 | */
205 | protected function findDetectionRulesAgainstUA(array $rules, $userAgent = null)
206 | {
207 | // Loop given rules
208 | foreach ($rules as $key => $regex) {
209 | if (empty($regex)) {
210 | continue;
211 | }
212 |
213 | // Check match
214 | if ($this->match($regex, $userAgent)) {
215 | return $key ?: reset($this->matchesArray);
216 | }
217 | }
218 |
219 | return false;
220 | }
221 |
222 | /**
223 | * Get the browser name.
224 | * @param string|null $userAgent
225 | * @return string|bool
226 | */
227 | public function browser($userAgent = null)
228 | {
229 | return $this->findDetectionRulesAgainstUA(static::getBrowsers(), $userAgent);
230 | }
231 |
232 | /**
233 | * Get the platform name.
234 | * @param string|null $userAgent
235 | * @return string|bool
236 | */
237 | public function platform($userAgent = null)
238 | {
239 | return $this->findDetectionRulesAgainstUA(static::getPlatforms(), $userAgent);
240 | }
241 |
242 | /**
243 | * Get the device name.
244 | * @param string|null $userAgent
245 | * @return string|bool
246 | */
247 | public function device($userAgent = null)
248 | {
249 | $rules = static::mergeRules(
250 | static::getDesktopDevices(),
251 | static::getPhoneDevices(),
252 | static::getTabletDevices(),
253 | static::getUtilities()
254 | );
255 |
256 | return $this->findDetectionRulesAgainstUA($rules, $userAgent);
257 | }
258 |
259 | /**
260 | * Check if the device is a desktop computer.
261 | * @param string|null $userAgent deprecated
262 | * @param array $httpHeaders deprecated
263 | * @return bool
264 | */
265 | public function isDesktop($userAgent = null, $httpHeaders = null)
266 | {
267 | // Check specifically for cloudfront headers if the useragent === 'Amazon CloudFront'
268 | if ($this->getUserAgent() === 'Amazon CloudFront') {
269 | $cfHeaders = $this->getCfHeaders();
270 | if(array_key_exists('HTTP_CLOUDFRONT_IS_DESKTOP_VIEWER', $cfHeaders)) {
271 | return $cfHeaders['HTTP_CLOUDFRONT_IS_DESKTOP_VIEWER'] === 'true';
272 | }
273 | }
274 |
275 | return !$this->isMobile($userAgent, $httpHeaders) && !$this->isTablet($userAgent, $httpHeaders) && !$this->isRobot($userAgent);
276 | }
277 |
278 | /**
279 | * Check if the device is a mobile phone.
280 | * @param string|null $userAgent deprecated
281 | * @param array $httpHeaders deprecated
282 | * @return bool
283 | */
284 | public function isPhone($userAgent = null, $httpHeaders = null)
285 | {
286 | return $this->isMobile($userAgent, $httpHeaders) && !$this->isTablet($userAgent, $httpHeaders);
287 | }
288 |
289 | /**
290 | * Get the robot name.
291 | * @param string|null $userAgent
292 | * @return string|bool
293 | */
294 | public function robot($userAgent = null)
295 | {
296 | if ($this->getCrawlerDetect()->isCrawler($userAgent ?: $this->userAgent)) {
297 | return ucfirst($this->getCrawlerDetect()->getMatches());
298 | }
299 |
300 | return false;
301 | }
302 |
303 | /**
304 | * Check if device is a robot.
305 | * @param string|null $userAgent
306 | * @return bool
307 | */
308 | public function isRobot($userAgent = null)
309 | {
310 | return $this->getCrawlerDetect()->isCrawler($userAgent ?: $this->userAgent);
311 | }
312 |
313 | /**
314 | * Get the device type
315 | * @param null $userAgent
316 | * @param null $httpHeaders
317 | * @return string
318 | */
319 | public function deviceType($userAgent = null, $httpHeaders = null)
320 | {
321 | if ($this->isDesktop($userAgent, $httpHeaders)) {
322 | return "desktop";
323 | } elseif ($this->isPhone($userAgent, $httpHeaders)) {
324 | return "phone";
325 | } elseif ($this->isTablet($userAgent, $httpHeaders)) {
326 | return "tablet";
327 | } elseif ($this->isRobot($userAgent)) {
328 | return "robot";
329 | }
330 |
331 | return "other";
332 | }
333 |
334 | public function version($propertyName, $type = self::VERSION_TYPE_STRING)
335 | {
336 | if (empty($propertyName)) {
337 | return false;
338 | }
339 |
340 | // set the $type to the default if we don't recognize the type
341 | if ($type !== self::VERSION_TYPE_STRING && $type !== self::VERSION_TYPE_FLOAT) {
342 | $type = self::VERSION_TYPE_STRING;
343 | }
344 |
345 | $properties = self::getProperties();
346 |
347 | // Check if the property exists in the properties array.
348 | if (true === isset($properties[$propertyName])) {
349 |
350 | // Prepare the pattern to be matched.
351 | // Make sure we always deal with an array (string is converted).
352 | $properties[$propertyName] = (array) $properties[$propertyName];
353 |
354 | foreach ($properties[$propertyName] as $propertyMatchString) {
355 | if (is_array($propertyMatchString)) {
356 | $propertyMatchString = implode("|", $propertyMatchString);
357 | }
358 |
359 | $propertyPattern = str_replace('[VER]', self::VER, $propertyMatchString);
360 |
361 | // Identify and extract the version.
362 | preg_match(sprintf('#%s#is', $propertyPattern), $this->userAgent, $match);
363 |
364 | if (false === empty($match[1])) {
365 | $version = ($type === self::VERSION_TYPE_FLOAT ? $this->prepareVersionNo($match[1]) : $match[1]);
366 |
367 | return $version;
368 | }
369 | }
370 | }
371 |
372 | return false;
373 | }
374 |
375 | /**
376 | * Merge multiple rules into one array.
377 | * @param array $all
378 | * @return array
379 | */
380 | protected static function mergeRules(...$all)
381 | {
382 | $merged = [];
383 |
384 | foreach ($all as $rules) {
385 | foreach ($rules as $key => $value) {
386 | if (empty($merged[$key])) {
387 | $merged[$key] = $value;
388 | } elseif (is_array($merged[$key])) {
389 | $merged[$key][] = $value;
390 | } else {
391 | $merged[$key] .= '|' . $value;
392 | }
393 | }
394 | }
395 |
396 | return $merged;
397 | }
398 |
399 | /**
400 | * @inheritdoc
401 | */
402 | public function __call($name, $arguments)
403 | {
404 | // Make sure the name starts with 'is', otherwise
405 | if (strpos($name, 'is') !== 0) {
406 | throw new BadMethodCallException("No such method exists: $name");
407 | }
408 |
409 | $this->setDetectionType(self::DETECTION_TYPE_EXTENDED);
410 |
411 | $key = substr($name, 2);
412 |
413 | return $this->matchUAAgainstKey($key);
414 | }
415 | }
416 |
--------------------------------------------------------------------------------
/src/AgentServiceProvider.php:
--------------------------------------------------------------------------------
1 | app->singleton('agent', function ($app) {
22 | return new Agent($app['request']->server());
23 | });
24 |
25 | $this->app->alias('agent', Agent::class);
26 | }
27 |
28 | /**
29 | * Get the services provided by the provider.
30 | *
31 | * @return array
32 | */
33 | public function provides()
34 | {
35 | return ['agent', Agent::class];
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Facades/Agent.php:
--------------------------------------------------------------------------------
1 |