Showing preview only (1,157K chars total). Download the full file or copy to clipboard to get everything.
Repository: serbanghita/Mobile-Detect
Branch: 4.8.x
Commit: 4583ed72248a
Files: 96
Total size: 1.1 MB
Directory structure:
gitextract_umzpjg5f/
├── .claude/
│ └── settings.json
├── .editorconfig
├── .gitattributes
├── .github/
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ └── feature_request.md
│ ├── docs/
│ │ └── index.html
│ └── workflows/
│ ├── 4.8.x-test.yml
│ └── website.yml
├── .gitignore
├── .php-cs-fixer.php
├── .phpcs.xml
├── CHANGELOG.md
├── CLAUDE.md
├── CNAME
├── CONTRIBUTING.md
├── DOCKER-COMPOSE.md
├── KNOWN_LIMITATIONS.md
├── LICENSE
├── MobileDetect.json
├── README-EXAMPLES.md
├── README.md
├── SECURITY.md
├── composer.json
├── docker/
│ ├── Dockerfile.setup
│ └── build.sh
├── docker-compose.yml
├── phpbench.json
├── scripts/
│ ├── dump_magic_methods.php
│ ├── example.php
│ ├── export_to_json.php
│ ├── pre-commit-hook.sh
│ ├── test.php
│ └── test_standalone.php
├── src/
│ ├── Cache/
│ │ ├── Cache.php
│ │ ├── CacheException.php
│ │ └── CacheInvalidArgumentException.php
│ ├── Exception/
│ │ ├── MobileDetectException.php
│ │ └── MobileDetectExceptionCode.php
│ ├── MobileDetect.php
│ └── MobileDetectStandalone.php
├── standalone/
│ ├── autoloader.php
│ └── deps/
│ └── simple-cache/
│ ├── .editorconfig
│ ├── .gitattributes
│ ├── .gitignore
│ ├── LICENSE.md
│ ├── README.md
│ ├── composer.json
│ └── src/
│ ├── CacheException.php
│ ├── CacheInterface.php
│ └── InvalidArgumentException.php
└── tests/
├── CacheTest.php
├── MobileDetectExceptionTest.php
├── MobileDetectGeneralTest.php
├── MobileDetectStandaloneTest.php
├── MobileDetectVersionTest.php
├── MobileDetectWithCacheTest.php
├── UserAgentList.inc.php
├── UserAgentTest.php
├── benchmark/
│ └── MobileDetectBench.php
├── bootstrap.php
├── phpunit.xml
├── providers/
│ └── vendors/
│ ├── AOC.php
│ ├── Acer.php
│ ├── Alcatel.php
│ ├── Allview.php
│ ├── Amazon.php
│ ├── Apple.php
│ ├── Archos.php
│ ├── Asus.php
│ ├── Blackberry.php
│ ├── Dell.php
│ ├── Google.php
│ ├── HP.php
│ ├── HTC.php
│ ├── Huawei.php
│ ├── LG.php
│ ├── Lava.php
│ ├── Leader.php
│ ├── Lenovo.php
│ ├── Mi.php
│ ├── Microsoft.php
│ ├── Motorola.php
│ ├── Mpman.php
│ ├── Nexus.php
│ ├── Nokia.php
│ ├── Onda.php
│ ├── Others.php
│ ├── Prestigio.php
│ ├── Samsung.php
│ ├── Sony.php
│ ├── SpecialCases.php
│ ├── Verizon.php
│ ├── Vodafone.php
│ └── ZTE.php
└── ualist.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .claude/settings.json
================================================
{
"attribution": {
"commit": "",
"pr": ""
},
"permissions": {
"allow": [
"Bash(git:*)",
"Bash(gh:*)",
"Bash(docker:*)",
"Bash(docker compose:*)",
"Bash(vendor/bin/phpunit:*)",
"Bash(vendor/bin/phpstan:*)",
"Bash(vendor/bin/phpcs:*)",
"Bash(vendor/bin/phpcbf:*)",
"Bash(vendor/bin/phpbench:*)",
"Bash(composer:*)",
"Bash(php:*)"
]
}
}
================================================
FILE: .editorconfig
================================================
root = true
[*]
end_of_line = lf
insert_final_newline = true
[*.php]
indent_style = space
indent_size = 4
max_line_length = 140
================================================
FILE: .gitattributes
================================================
* text=auto eol=lf
.editorconfig export-ignore
.gitattributes export-ignore
/docs/ export-ignore
/scripts/ export-ignore
/tests/ export-ignore
/.* export-ignore
================================================
FILE: .github/FUNDING.yml
================================================
github: "serbanghita"
custom:
- "https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=mobiledetectlib%40gmail%2ecom&lc=US&item_name=Mobile%20Detect¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted"
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a bug report for Mobile Detect
title: "[bug]"
labels: 'type: bug'
assignees: serbanghita
---
**Describe the bug**
A clear and concise description of what the bug is.
Include steps to reproduce the behavior.
A clear and concise description of what you expected to happen.
**User-agent/Device/Phone/Tablet (please complete the following information):**
- User-agent [e.g. "Mozilla/5.0 (Linux; Android 12; SAMSUNG SM-F926B) AppleWebKit/537.36..."]
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for Mobile Detect
title: "[feature]"
labels: feature
assignees: serbanghita
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
A clear and concise description of any alternative solutions or features you've considered.
Add any other context or code snippets about the feature request here.
================================================
FILE: .github/docs/index.html
================================================
================================================
FILE: .github/workflows/4.8.x-test.yml
================================================
name: 4.8.x on PHP 8.x
# Run this workflow every time a new commit pushed to your repository
on:
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
push:
branches: ['4.8.x']
paths-ignore:
- '*.md'
- '.github/**'
- 'scripts/**'
pull_request:
branches: ['4.8.x']
paths-ignore:
- '*.md'
- '.github/**'
- 'scripts/**'
jobs:
run:
runs-on: ${{ matrix.os }}
strategy:
matrix:
php-version: [8.0, 8.1, 8.2, 8.3, 8.4]
os: ['ubuntu-latest']
composer-version: ['latest']
phpunit-version: ['^9.6.18']
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup PHP ${{ matrix.php-version }} on ${{ matrix.os }}
uses: shivammathur/setup-php@verbose
with:
php-version: ${{ matrix.php-version }}
extensions: mbstring, intl
ini-values: post_max_size=256M, max_execution_time=180
coverage: xdebug
tools: phpunit:${{ matrix.phpunit-version }}, composer:${{ matrix.composer-version }}
- name: Install composer
run: composer install
- name: Run linting
run: vendor/bin/phpcs
- name: Run quality rules
run: vendor/bin/phpstan analyse --debug --memory-limit=1G --level 3 src tests
- name: Run tests
run: XDEBUG_MODE=coverage && phpunit -v -c tests/phpunit.xml --coverage-text --strict-coverage --stop-on-risky
shell: bash
================================================
FILE: .github/workflows/website.yml
================================================
# Simple workflow for deploying static content to GitHub Pages
name: website
on:
# Runs on pushes targeting the default branch
push:
branches: ["4.8.x"]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "pages"
cancel-in-progress: false
jobs:
# Single deploy job since we're just deploying
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Converts Markdown to HTML
uses: jaywcjlove/markdown-to-html-cli@main
with:
source: README.md
output: .github/docs/index.html
title: MobileDetect - the PHP mobile detection class
description: Mobile Detect is a lightweight PHP class for detecting mobile devices (including tablets). It uses the User-Agent string combined with specific HTTP headers to detect the mobile environment.
keywords: php mobile-detect device-detection user-agents mobile-redirects
corners: false
dark-mode: false
img-base64: true
favicon: data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🌐</text></svg>
- name: Setup Pages
uses: actions/configure-pages@v5
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
# Upload entire repository
path: './.github/docs'
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
================================================
FILE: .gitignore
================================================
vendor/
nbproject/
/*.buildpath
/*.project
/.settings
/error.log
.idea/
*.iml
/coverage
/phpunit.phar
composer.lock
/.mobiledetect.phpcs-cache
/tests/.phpunit.result.cache
/.phpunit.result.cache
/composer.phar
/tests/phpunit.xml.bak
/.coverage
/.phpbench
/index.php
.php-cs-fixer.cache
.DS_Store
================================================
FILE: .php-cs-fixer.php
================================================
<?php
$finder = PhpCsFixer\Finder::create()
->exclude('docs')
->exclude('.github')
// ->notPath('src/Symfony/Component/Translation/Tests/fixtures/resources.php')
->in(__DIR__)
;
$config = new PhpCsFixer\Config();
return $config->setRules([
'@PSR12' => true,
// 'strict_param' => true,
'array_syntax' => ['syntax' => 'short']
])
->setFinder($finder)
;
================================================
FILE: .phpcs.xml
================================================
<?xml version="1.0"?>
<ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="PHP_CodeSniffer"
xsi:noNamespaceSchemaLocation="vendor/squizlabs/php_codesniffer/phpcs.xsd">
<file>src</file>
<file>tests</file>
<!-- <exclude-pattern>samples/Header.php</exclude-pattern>-->
<!-- <exclude-pattern>*/tests/Core/*/*Test\.(inc|css|js)$</exclude-pattern>-->
<arg name="report-width" value="200"/>
<arg name="parallel" value="80"/>
<arg name="cache" value=".mobiledetect.phpcs-cache"/>
<arg name="colors"/>
<arg value="np"/>
<!-- Include the whole PSR12 standard -->
<rule ref="PSR12">
<exclude name="PSR2.Methods.MethodDeclaration.Underscore"/>
<exclude name="Generic.Files.LineLength"/>
</rule>
</ruleset>
================================================
FILE: CHANGELOG.md
================================================
# Change log
# 4.8.10
## Fixed
- [x] `Cache::has()` now properly checks TTL expiration before returning `true` (PSR-16 compliance fix). Previously, `has()` returned `true` for expired items.
## Added
- [x] `Cache::evictExpired()` method to manually clean up expired cache entries. Useful for long-running processes (CLI scripts, workers, daemons) to prevent memory growth.
- [x] Expanded test coverage for `Cache` class: added 17 new tests covering all if/else branches including custom defaults, DateInterval TTL, key validation edge cases, and expiration scenarios.
- [x] `README-EXAMPLES.md` with comprehensive usage examples including long-running processes, framework integration, and debugging.
## Changed
- [x] `Cache::has()` now deletes expired items on check (lazy cleanup, consistent with `get()` behavior).
# 4.8.09
- [x] `sha1` is now the default fn for encoding cache keys. Using `base64` [was causing problems](https://github.com/serbanghita/Mobile-Detect/issues/974#issuecomment-2531597903) in Laravel.
# 4.8.08
- [x] fix for missing psr/cache prod dependency
- [bug] latest 4.8.07 cause site error Call to a member function get() on false #974
- [x] fix for Docker build not installing dev dependencies
# 4.8.07 (broken in composer, please skip)
- [x] fix cache and generate short cache key (#971)
- [x] Added configuration cacheKeyFn which allows for using a custom cache key creation fn.
- [x] Use Client Hints Sec-CH-UA-Mobile header to detect mobile (#962)
- [x] added Huawei (phone, OS - HarmonyOS, browser) detection (#952)
- [x] Bugfix: Allow Injection of Any PSR Cache Interface (#966)
- [x] PHP 8.4 - implicit nulls are deprecated (#960)
# 4.8.03
- [x] added optional `$config` to MobileDetect constructor.
- [x] added `autoInitOfHttpHeaders` configuration which is by default `true`. This enabled the old behavior from `3.x` and `2.x` that allows automatic detection of HTTP headers and User Agent from $_SERVER.
- [x] refactored internal CloudFront related methods and the way `setHttpHeaders` work. It no longer falls back on `$_SERVER`. The method still calls `setUserAgent` in case `HTTP_USER_AGENT` and friends are present.
- [x] added `maximumUserAgentLength` to the `$config`, by default the limit is `500`.
# 4.8.02
- [x] new user agents
- [x] Samsung Galaxy Tab S6 Lite #919
- [x] Samsung Galaxy Tab S8 series #912
# 4.8.01
- [x] PHP 8.x only.
- [x] PSR-16 cache support.
- [x] Constructor accepts `CacheFactory` class where you can inject your own PSR-6 Cache interfaces.
- [x] You need to explicitly `setUserAgent("...")` or `setUserAgentHeaders([...])` otherwise an exception is being thrown.
- [x] `scripts/` folder no longer included in the git tag release archive.
- [x] added performance tests
- [x] regexes can be arrays of strings or strings
# 2023
Launched 4.8.xx which contains PHP 8.x support, refactorings and external Cache support.
## 2022
In December 2022 we released the version for PHP7.
Mobile Detect was split into two dev branches: `2.8.x` which will support PHP5, but is deprecated and
`3.74.x` which supports PHP >= 7.3
## 2013
In August 2013 the library has 1800+ stargazers and support for: composer, PHPUnit tests, PSR standards and a new webpage http://mobiledetect.net
# 2012
Throughout 2012 the script has been updated constantly, and we have received tons of feedback and requests.
In July 2012 we moved the repository from Google Code to GitHub in order to quickly accommodate the frequent updates and to involve more people.
## 2011
In December 2011 it received a major update from the first version, an important number of issues were fixed, then 2.0 was launched.
The new version marks a new mindset and also featuring tablet detection.
## 2009
The first version of the script was developed in 2009, and it was hosted at https://code.google.com/p/php-mobile-detect/, it was a small project with around 30 stars.
(Original blog post by Victor: http://victorstanciu.ro/detectarea-platformelor-mobile-in-php/)
================================================
FILE: CLAUDE.md
================================================
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Whenever you find a new rule that applies to this project, add it to this file.
## Project Overview
Mobile-Detect is a lightweight PHP library for detecting mobile devices (including tablets) using User-Agent strings and HTTP headers. The main namespace is `Detection\MobileDetect`.
## Git Workflow
When working on 4.8.x-based branches, always rebase changes into the `4.8.x` branch only (not `main` or `master`).
When releasing a new version tag, make sure that the new tag is reflected in the `@version` comment section of `MobileDetect.php` and also
in the `protected string $VERSION` property of the `MobileDetect` class.
The latest tag should also be reflected in `MobileDetect.json`'s `version` property.
## Code Navigation
Always use LSP tools when working with code references:
- Use `goToDefinition` to find where a class, method, or function is defined
- Use `findReferences` to locate all usages of a symbol
- Use `hover` to get type information and documentation
- Use `documentSymbol` to list all symbols in a file
## Common Commands
### Testing
```bash
# Run all tests with coverage
vendor/bin/phpunit -v -c tests/phpunit.xml --coverage-html .coverage
# Run a single test file
vendor/bin/phpunit -v -c tests/phpunit.xml tests/MobileDetectGeneralTest.php
# Run a specific test method
vendor/bin/phpunit -v -c tests/phpunit.xml --filter testMethodName
```
### Code Quality
```bash
# Linting (PSR-12 standard)
vendor/bin/phpcs
# Auto-fix code style issues
vendor/bin/php-cs-fixer fix
# Static analysis (level 3)
vendor/bin/phpstan analyse --memory-limit=1G --level 3 src tests
```
### Benchmarking
```bash
# Create baseline
vendor/bin/phpbench run tests/benchmark/MobileDetectBench.php --retry-threshold=1 --iterations=10 --revs=1000 --report=aggregate --tag=baseline
# Compare against baseline
vendor/bin/phpbench run tests/benchmark/MobileDetectBench.php --ref=baseline --retry-threshold=1 --iterations=10 --revs=1000 --report=aggregate
```
## Architecture
### Core Classes (src/)
- **MobileDetect.php** - Main detection class containing:
- Device/tablet/browser regex patterns as static arrays
- Magic `isXXXX()` methods for device/browser detection (e.g., `isiPhone()`, `isAndroidOS()`)
- `isMobile()`, `isTablet()` - Primary detection methods
- `version()` - Extract version numbers from User-Agent
- PSR-16 cache integration for regex match results
- **MobileDetectStandalone.php** - Extends MobileDetect for use without Composer (autoloads dependencies from `standalone/`)
- **Cache/Cache.php** - In-memory PSR-16 cache implementation with TTL support
### Test Structure (tests/)
- **MobileDetectGeneralTest.php** - Core functionality tests
- **UserAgentTest.php** - Data-driven tests using User-Agent fixtures
- **providers/vendors/*.php** - Test data files organized by device vendor (Apple.php, Samsung.php, etc.)
- Each file returns an array of User-Agent strings mapped to expected results (`isMobile`, `isTablet`, `version`, etc.)
- **benchmark/MobileDetectBench.php** - PHPBench performance tests
### Key Patterns
- Detection results are cached using configurable PSR-16 cache (default: in-memory)
- Cache key generation is configurable via `cacheKeyFn` (default: sha1)
- HTTP headers from `$_SERVER` are auto-initialized unless `autoInitOfHttpHeaders` is false
- CloudFront headers are recognized for AWS-based detection
================================================
FILE: CNAME
================================================
mobiledetect.net
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing
Step-by-step guide to contributing to Mobile Detect library. \
By contributing to Mobile Detect library you agree with the [MIT License](LICENSE).
## Contribute by developing code
### 1. [Fork](https://help.github.com/articles/fork-a-repo/#fork-an-example-repository) the repo
```bash
git clone https://github.com/[yourname]/Mobile-Detect.git
git add remote serbanghita https://github.com/serbanghita/Mobile-Detect.git
git remote -v
...
origin git@github.com:serbanghita/Mobile-Detect.git
serbanghita https://github.com/serbanghita/Mobile-Detect.git
```
### 2. Create local branch
Next create your own git working branch from one of the existing branches `4.x`, `3.x` or `2.x`
depending on your PHP version:
```bash
git checkout -b my-new-patch origin/x.x.x
```
### 3. Build
#### Local
```shell
composer install
```
#### With Docker CLI
```shell
DOCKER_BUILDKIT=0 docker build -f ./docker/Dockerfile -t build .
```
#### With `docker-compose.yml` (recommended)
```shell
docker compose build setup
```
### 3. Lint the code
Ensure the code is clean, just run the linters:
#### Local
```bash
./vendor/bin/phpcs
./vendor/bin/phpcbf
```
#### With `docker-compose.yml` (recommended)
```shell
docker compose build runLinting
```
### 4. Run unit and integration tests
If you add new methods or make structural changes to the library then you need to add unit tests
otherwise your PR will not be accepted.
If you add new regexes make sure you commit the User-Agents in [`tests/providers/vendors`](https://github.com/serbanghita/Mobile-Detect/tree/master/tests/providers/vendors).
Now that your changes are done, **run the unit tests**:
#### Locally
```bash
vendor/bin/phpunit -v -c tests/phpunit.xml --coverage-html .coverage
```
#### With `docker-compose.yml` (recommended)
```shell
docker compose run runUnitTests
```
Make sure you check the `.coverage` folder and open the report. \
The coverage should be just like you first started (close to 100%).
### 5. Run performance tests
#### Local
```bash
./vendor/bin/phpbench run tests/Benchmark/MobileDetectBench.php --ref=baseline --retry-threshold=1 --iterations=10 --revs=1000 --report=aggregate
```
#### With `docker-compose.yml` (recommended)
```shell
docker compose run runPerfTests
```
Baseline re-creation:
```bash
./vendor/bin/phpbench run tests/Benchmark/ --retry-threshold=1 --iterations=10 --revs=1000 --report=aggregate --tag=baseline --dump-file=phpbench-baseline.xml
```
### 6. Commit
If no errors left, then proceed to committing your changes:
```bash
git status
git stage
git commit -m "your commit message here"
git push
```
### 7. Submit PR
Now go to your repo on GitHub and ["Submit the PR"](https://help.github.com/articles/about-pull-requests/).
## Other ways of contributing
### 1. Report issues
1. Specify the User-agent by visiting [http://demo.mobiledetect.net](http://demo.mobiledetect.net).
2. Specify the expected behaviour.
### 2. Add new module, plugin, plugin or port
[Submit new module, plugin, port](../../issues/new?title=New%203rd%20party%20module&body=Name,%20Link%20and%20Description%20of%20the%20module.)
including the following information:
* Module name
* Description
* Link
* Author
Or you can submit a PR against `README.md`.
### 3. Website updates
1. Our official website is hosted at [http://mobiledetect.net](http://mobiledetect.net).
2. The files are found on the `gh-pages` branch.
3. `git checkout gh-pages`
4. `npm install -g browser-sync`
5. `browser-sync start --s . --f . --port 3000 --reload-debounce 1500 --no-ui`
6. Go to `http://localhost:3000` and make changes.
7. Commit, push and submit the PR against `serbanghita:gh-pages`.
================================================
FILE: DOCKER-COMPOSE.md
================================================
# Docker Compose for Pre-Release Validation
This document describes the Docker Compose setup for running all necessary checks before a release in a controlled PHP environment.
## Architecture Overview
```
┌─────────────────────────────────────────────────────────────────────────┐
│ SETUP SERVICE │
│ (composer:latest) - Installs dependencies into ./vendor │
└─────────────────────────────────────────────────────────────────────────┘
│
service_completed_successfully
│
┌───────────────────────────┼───────────────────────────┐
▼ ▼ ▼
┌───────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ runUnitTests │ │ runPerfTests │ │ runLinting │
│ (php:8.4+xdebug)│ │ (php:8.4-alpine)│ │ (php:8.4-alpine)│
│ phpunit │ │ phpbench │ │ phpcs │
└───────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
│ │ ▼
│ │ ┌─────────────────┐
│ │ │ runQualityCheck │
│ │ │ (php:8.4-alpine)│
│ │ │ phpstan │
│ │ └─────────────────┘
│ │ │
└───────────────────────────┴───────────────────────────┘
│
all services completed successfully
│
▼
┌─────────────────────┐
│ runAll │
│ Pre-release gate │
└─────────────────────┘
│
▼
┌─────────────────────┐
│ generateJsonModel │
│ export_to_json.php │
└─────────────────────┘
```
## Services
| Service | Image | Purpose |
|---------|-------|---------|
| `setup` | composer:latest | Install dependencies |
| `runUnitTests` | alcohol/php:8.4-xdebug | PHPUnit tests with coverage |
| `runPerfTests` | php:8.4-alpine | PHPBench performance tests |
| `runLinting` | php:8.4-alpine | PHPCS code style checks + auto-fix |
| `runQualityCheck` | php:8.4-alpine | PHPStan static analysis |
| `runAll` | php:8.4-alpine | Pre-release validation gate |
| `generateJsonModel` | php:8.4-alpine | Export detection rules to JSON |
## Usage
### Run all pre-release checks
```bash
docker compose -p mobile-detect up --build runAll
```
### Run individual services
```bash
# Unit tests with coverage
docker compose -p mobile-detect up --build runUnitTests
# Performance benchmarks
docker compose -p mobile-detect up --build runPerfTests
# Code style linting
docker compose -p mobile-detect up --build runLinting
# Static analysis
docker compose -p mobile-detect up --build runQualityCheck
# Generate JSON model (runs after all checks pass)
docker compose -p mobile-detect up --build generateJsonModel
```
### Clean up
```bash
docker compose -p mobile-detect down --volumes --remove-orphans
```
================================================
FILE: KNOWN_LIMITATIONS.md
================================================
**Known limitations**
* Mobile Detect script was designed to detect `mobile` devices. Implicitly other devices are considered to be `desktop`.
* User-Agent and HTTP headers sniffing is a non-reliable method of detecting a mobile device.
* If the mobile browser is set on `Desktop mode`, the Mobile Detect script has no indicator (eg. a group of strings) that would allow it to detect that the device is `mobile`.
* Ipad 2019 is being recognized as a desktop because of Safari's default `Request Desktop Website` setting. See details and possible workaround [#820](https://github.com/serbanghita/Mobile-Detect/issues/820)
* Also see [#886](https://github.com/serbanghita/Mobile-Detect/issues/886#issuecomment-1047187763)
* Some touchscreen devices (eg. Microsoft Surface) are tough to detect as mobile since they can be used in a laptop mode. See: [#32](https://github.com/serbanghita/Mobile-Detect/issues/32), [#461](https://github.com/serbanghita/Mobile-Detect/issues/461), [#667](https://github.com/serbanghita/Mobile-Detect/issues/667)
* Some mobile devices (eg. IPadOS, Google Pixel Slate). See: [#795](https://github.com/serbanghita/Mobile-Detect/issues/795), [#788](https://github.com/serbanghita/Mobile-Detect/issues/788)
* Detecting the device brand (eg. Apple, Samsung, HTC) is not 100% reliable.
* We don't monitor the quality of the 3rd party tools based on Mobile Detect script.
We cannot guarantee that they are using the class properly or if they provide the latest version.
* Version `2.x` is made to be PHP 5.3 compatible because of the backward compatibility changes of PHP.
* There are hundreds of devices launched every month, we cannot keep a 100% up-to-date detection rate.
* The script cannot detect the viewport, pixel density or resolution of the screen since it's running server-side.
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2021 Şerban Ghiţă, Nick Ilyin and contributors.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: MobileDetect.json
================================================
{
"version": "4.8.10",
"headerMatch": {
"HTTP_ACCEPT": {
"matches": [
"application\/x-obml2d",
"application\/vnd.rim.html",
"text\/vnd.wap.wml",
"application\/vnd.wap.xhtml+xml"
]
},
"HTTP_X_WAP_PROFILE": null,
"HTTP_X_WAP_CLIENTID": null,
"HTTP_WAP_CONNECTION": null,
"HTTP_PROFILE": null,
"HTTP_X_OPERAMINI_PHONE_UA": null,
"HTTP_X_NOKIA_GATEWAY_ID": null,
"HTTP_X_ORANGE_ID": null,
"HTTP_X_VODAFONE_3GPDPCONTEXT": null,
"HTTP_X_HUAWEI_USERID": null,
"HTTP_UA_OS": null,
"HTTP_X_MOBILE_GATEWAY": null,
"HTTP_X_ATT_DEVICEID": null,
"HTTP_UA_CPU": {
"matches": [
"ARM"
]
},
"Sec-CH-UA-Mobile": {
"matches": [
"?1"
]
}
},
"uaHttpHeaders": [
"HTTP_USER_AGENT",
"HTTP_X_OPERAMINI_PHONE_UA",
"HTTP_X_DEVICE_USER_AGENT",
"HTTP_X_ORIGINAL_USER_AGENT",
"HTTP_X_SKYFIRE_PHONE",
"HTTP_X_BOLT_PHONE_UA",
"HTTP_DEVICE_STOCK_UA",
"HTTP_X_UCBROWSER_DEVICE_UA"
],
"cloudFrontHttpHeaders": [
"HTTP_CLOUDFRONT_IS_MOBILE_VIEWER",
"HTTP_CLOUDFRONT_IS_TABLET_VIEWER",
"HTTP_CLOUDFRONT_IS_DESKTOP_VIEWER"
],
"uaMatch": {
"phones": {
"iPhone": "\\biPhone\\b|\\biPod\\b",
"BlackBerry": "BlackBerry|\\bBB10\\b|rim[0-9]+|\\b(BBA100|BBB100|BBD100|BBE100|BBF100|STH100)\\b-[0-9]+",
"Pixel": "; \\bPixel\\b",
"HTC": [
"HTC|HTC.*(Sensation|Evo|Vision|Explorer|6800|8100|8900|A7272|S510e|C110e|Legend|Desire|T8282)",
"APX515CKT|Qtek9090|APA9292KT|HD_mini|Sensation.*Z710e|PG86100|Z715e|Desire.*(A8181|HD)|ADR6200",
"ADR6400L|ADR6425|001HT|Inspire 4G|Android.*\\bEVO\\b|T-Mobile G1|Z520m|Android [0-9.]+; Pixel"
],
"Nexus": "Nexus One|Nexus S|Galaxy.*Nexus|Android.*Nexus.*Mobile|Nexus 4|Nexus 5|Nexus 5X|Nexus 6",
"Dell": "Dell[;]? (Streak|Aero|Venue|Venue Pro|Flash|Smoke|Mini 3iX)|XCD28|XCD35|\\b001DL\\b|\\b101DL\\b|\\bGS01\\b",
"Motorola": [
"Motorola|DROIDX|DROID BIONIC|\\bDroid\\b.*Build|Android.*Xoom|HRI39|MOT-|A1260|A1680|A555|A853|A855|A953|A955",
"A956|Motorola.*ELECTRIFY|Motorola.*i1|i867|i940|MB200|MB300|MB501|MB502|MB508|MB511|MB520|MB525|MB526|MB611",
"MB612|MB632|MB810|MB855|MB860|MB861|MB865|MB870|ME501|ME502|ME511|ME525|ME600|ME632|ME722|ME811|ME860|ME863",
"ME865|MT620|MT710|MT716|MT720|MT810|MT870|MT917|Motorola.*TITANIUM|WX435|WX445|XT300|XT301|XT311|XT316|XT317",
"XT319|XT320|XT390|XT502|XT530|XT531|XT532|XT535|XT603|XT610|XT611|XT615|XT681|XT701|XT702|XT711|XT720|XT800",
"XT806|XT860|XT862|XT875|XT882|XT883|XT894|XT901|XT907|XT909|XT910|XT912|XT928|XT926|XT915|XT919|XT925|XT1021|\\bMoto E\\b|XT1068|XT1092|XT1052"
],
"Samsung": [
"\\bSamsung\\b|SM-G950F|SM-G955F|SM-G9250|GT-19300|SGH-I337|BGT-S5230|GT-B2100|GT-B2700|GT-B2710|GT-B3210|GT-B3310",
"SM-F946B|SM-A127F",
"SM-S908E|SM-G955N|SM-S918U1|SM-G998B|SM-G970N|SM-G973U|SM-S901U|SM-A515F|SM-S901E|SM-G980F|SM-S901B",
"GT-B3410|GT-B3730|GT-B3740|GT-B5510|GT-B5512|GT-B5722|GT-B6520|GT-B7300|GT-B7320|GT-B7330|GT-B7350|GT-B7510",
"GT-B7722|GT-B7800|GT-C3010|GT-C3011|GT-C3060|GT-C3200|GT-C3212|GT-C3212I|GT-C3262|GT-C3222|GT-C3300|GT-C3300K",
"GT-C3303|GT-C3303K|GT-C3310|GT-C3322|GT-C3330|GT-C3350|GT-C3500|GT-C3510|GT-C3530|GT-C3630|GT-C3780|GT-C5010",
"GT-C5212|GT-C6620|GT-C6625|GT-C6712|GT-E1050|GT-E1070|GT-E1075|GT-E1080|GT-E1081|GT-E1085|GT-E1087|GT-E1100",
"GT-E1107|GT-E1110|GT-E1120|GT-E1125|GT-E1130|GT-E1160|GT-E1170|GT-E1175|GT-E1180|GT-E1182|GT-E1200|GT-E1210",
"GT-E1225|GT-E1230|GT-E1390|GT-E2100|GT-E2120|GT-E2121|GT-E2152|GT-E2220|GT-E2222|GT-E2230|GT-E2232|GT-E2250",
"GT-E2370|GT-E2550|GT-E2652|GT-E3210|GT-E3213|GT-I5500|GT-I5503|GT-I5700|GT-I5800|GT-I5801|GT-I6410|GT-I6420",
"GT-I7110|GT-I7410|GT-I7500|GT-I8000|GT-I8150|GT-I8160|GT-I8190|GT-I8320|GT-I8330|GT-I8350|GT-I8530|GT-I8700",
"GT-I8703|GT-I8910|GT-I9000|GT-I9001|GT-I9003|GT-I9010|GT-I9020|GT-I9023|GT-I9070|GT-I9082|GT-I9100|GT-I9103",
"GT-I9220|GT-I9250|GT-I9300|GT-I9305|GT-I9500|GT-I9505|GT-M3510|GT-M5650|GT-M7500|GT-M7600|GT-M7603|GT-M8800",
"GT-M8910|GT-N7000|GT-S3110|GT-S3310|GT-S3350|GT-S3353|GT-S3370|GT-S3650|GT-S3653|GT-S3770|GT-S3850|GT-S5210",
"GT-S5220|GT-S5229|GT-S5230|GT-S5233|GT-S5250|GT-S5253|GT-S5260|GT-S5263|GT-S5270|GT-S5300|GT-S5330|GT-S5350",
"GT-S5360|GT-S5363|GT-S5369|GT-S5380|GT-S5380D|GT-S5560|GT-S5570|GT-S5600|GT-S5603|GT-S5610|GT-S5620|GT-S5660",
"GT-S5670|GT-S5690|GT-S5750|GT-S5780|GT-S5830|GT-S5839|GT-S6102|GT-S6500|GT-S7070|GT-S7200|GT-S7220|GT-S7230",
"GT-S7233|GT-S7250|GT-S7500|GT-S7530|GT-S7550|GT-S7562|GT-S7710|GT-S8000|GT-S8003|GT-S8500|GT-S8530|GT-S8600",
"SCH-A310|SCH-A530|SCH-A570|SCH-A610|SCH-A630|SCH-A650|SCH-A790|SCH-A795|SCH-A850|SCH-A870|SCH-A890|SCH-A930",
"SCH-A950|SCH-A970|SCH-A990|SCH-I100|SCH-I110|SCH-I400|SCH-I405|SCH-I500|SCH-I510|SCH-I515|SCH-I600|SCH-I730",
"SCH-I760|SCH-I770|SCH-I830|SCH-I910|SCH-I920|SCH-I959|SCH-LC11|SCH-N150|SCH-N300|SCH-R100|SCH-R300|SCH-R351",
"SCH-R400|SCH-R410|SCH-T300|SCH-U310|SCH-U320|SCH-U350|SCH-U360|SCH-U365|SCH-U370|SCH-U380|SCH-U410|SCH-U430",
"SCH-U450|SCH-U460|SCH-U470|SCH-U490|SCH-U540|SCH-U550|SCH-U620|SCH-U640|SCH-U650|SCH-U660|SCH-U700|SCH-U740",
"SCH-U750|SCH-U810|SCH-U820|SCH-U900|SCH-U940|SCH-U960|SCS-26UC|SGH-A107|SGH-A117|SGH-A127|SGH-A137|SGH-A157",
"SGH-A167|SGH-A177|SGH-A187|SGH-A197|SGH-A227|SGH-A237|SGH-A257|SGH-A437|SGH-A517|SGH-A597|SGH-A637|SGH-A657",
"SGH-A667|SGH-A687|SGH-A697|SGH-A707|SGH-A717|SGH-A727|SGH-A737|SGH-A747|SGH-A767|SGH-A777|SGH-A797|SGH-A817",
"SGH-A827|SGH-A837|SGH-A847|SGH-A867|SGH-A877|SGH-A887|SGH-A897|SGH-A927|SGH-B100|SGH-B130|SGH-B200|SGH-B220",
"SGH-C100|SGH-C110|SGH-C120|SGH-C130|SGH-C140|SGH-C160|SGH-C170|SGH-C180|SGH-C200|SGH-C207|SGH-C210|SGH-C225",
"SGH-C230|SGH-C417|SGH-C450|SGH-D307|SGH-D347|SGH-D357|SGH-D407|SGH-D415|SGH-D780|SGH-D807|SGH-D980|SGH-E105",
"SGH-E200|SGH-E315|SGH-E316|SGH-E317|SGH-E335|SGH-E590|SGH-E635|SGH-E715|SGH-E890|SGH-F300|SGH-F480|SGH-I200",
"SGH-I300|SGH-I320|SGH-I550|SGH-I577|SGH-I600|SGH-I607|SGH-I617|SGH-I627|SGH-I637|SGH-I677|SGH-I700|SGH-I717",
"SGH-I727|SGH-i747M|SGH-I777|SGH-I780|SGH-I827|SGH-I847|SGH-I857|SGH-I896|SGH-I897|SGH-I900|SGH-I907|SGH-I917",
"SGH-I927|SGH-I937|SGH-I997|SGH-J150|SGH-J200|SGH-L170|SGH-L700|SGH-M110|SGH-M150|SGH-M200|SGH-N105|SGH-N500",
"SGH-N600|SGH-N620|SGH-N625|SGH-N700|SGH-N710|SGH-P107|SGH-P207|SGH-P300|SGH-P310|SGH-P520|SGH-P735|SGH-P777",
"SGH-Q105|SGH-R210|SGH-R220|SGH-R225|SGH-S105|SGH-S307|SGH-T109|SGH-T119|SGH-T139|SGH-T209|SGH-T219|SGH-T229",
"SGH-T239|SGH-T249|SGH-T259|SGH-T309|SGH-T319|SGH-T329|SGH-T339|SGH-T349|SGH-T359|SGH-T369|SGH-T379|SGH-T409",
"SGH-T429|SGH-T439|SGH-T459|SGH-T469|SGH-T479|SGH-T499|SGH-T509|SGH-T519|SGH-T539|SGH-T559|SGH-T589|SGH-T609",
"SGH-T619|SGH-T629|SGH-T639|SGH-T659|SGH-T669|SGH-T679|SGH-T709|SGH-T719|SGH-T729|SGH-T739|SGH-T746|SGH-T749",
"SGH-T759|SGH-T769|SGH-T809|SGH-T819|SGH-T839|SGH-T919|SGH-T929|SGH-T939|SGH-T959|SGH-T989|SGH-U100|SGH-U200",
"SGH-U800|SGH-V205|SGH-V206|SGH-X100|SGH-X105|SGH-X120|SGH-X140|SGH-X426|SGH-X427|SGH-X475|SGH-X495|SGH-X497",
"SGH-X507|SGH-X600|SGH-X610|SGH-X620|SGH-X630|SGH-X700|SGH-X820|SGH-X890|SGH-Z130|SGH-Z150|SGH-Z170|SGH-ZX10",
"SGH-ZX20|SHW-M110|SPH-A120|SPH-A400|SPH-A420|SPH-A460|SPH-A500|SPH-A560|SPH-A600|SPH-A620|SPH-A660|SPH-A700",
"SPH-A740|SPH-A760|SPH-A790|SPH-A800|SPH-A820|SPH-A840|SPH-A880|SPH-A900|SPH-A940|SPH-A960|SPH-D600|SPH-D700",
"SPH-D710|SPH-D720|SPH-I300|SPH-I325|SPH-I330|SPH-I350|SPH-I500|SPH-I600|SPH-I700|SPH-L700|SPH-M100|SPH-M220",
"SPH-M240|SPH-M300|SPH-M305|SPH-M320|SPH-M330|SPH-M350|SPH-M360|SPH-M370|SPH-M380|SPH-M510|SPH-M540|SPH-M550",
"SPH-M560|SPH-M570|SPH-M580|SPH-M610|SPH-M620|SPH-M630|SPH-M800|SPH-M810|SPH-M850|SPH-M900|SPH-M910|SPH-M920",
"SPH-M930|SPH-N100|SPH-N200|SPH-N240|SPH-N300|SPH-N400|SPH-Z400|SWC-E100|SCH-i909|GT-N7100|GT-N7105|SCH-I535",
"SM-N900A|SGH-I317|SGH-T999L|GT-S5360B|GT-I8262|GT-S6802|GT-S6312|GT-S6310|GT-S5312|GT-S5310|GT-I9105|GT-I8510",
"GT-S6790N|SM-G7105|SM-N9005|GT-S5301|GT-I9295|GT-I9195|SM-C101|GT-S7392|GT-S7560|GT-B7610|GT-I5510|GT-S7582",
"GT-S7530E|GT-I8750|SM-G9006V|SM-G9008V|SM-G9009D|SM-G900A|SM-G900D|SM-G900F|SM-G900H|SM-G900I|SM-G900J|SM-G900K",
"SM-G900L|SM-G900M|SM-G900P|SM-G900R4|SM-G900S|SM-G900T|SM-G900V|SM-G900W8|SHV-E160K|SCH-P709|SCH-P729|SM-T2558",
"GT-I9205|SM-G9350|SM-J120F|SM-G920F|SM-G920V|SM-G930F|SM-N910C|SM-A310F|GT-I9190|SM-J500FN|SM-G903F|SM-J330F",
"SM-G610F|SM-G981B|SM-G892A|SM-A530F|SM-G988N|SM-G781B|SM-A805N|SM-G965F"
],
"LG": [
"\\bLG\\b;|LG[- ]?(C800|C900|E400|E610|E900|E-900|F160|F180K|F180L|F180S|730|855|L160|LS740|LS840|LS970|LU6200)",
"LG[- ]?(MS690|MS695|MS770|MS840|MS870|MS910|P500|P700|P705|VM696|AS680|AS695|AX840|C729|E970|GS505|272|C395|E739BK)",
"LG[- ]?(E960|L55C|L75C|LS696|LS860|P769BK|P350|P500|P509|P870|UN272|US730|VS840|VS950|LN272|LN510|LS670|LS855|LW690)",
"LG[- ]?(MN270|MN510|P509|P769|P930|UN200|UN270|UN510|UN610|US670|US740|US760|UX265|UX840|VN271|VN530|VS660|VS700|VS740)",
"LG[- ]?(VS750|VS910|VS920|VS930|VX9200|VX11000|AX840A|LW770|P506|P925|P999|E612|D955|D802|MS323|M257)|LM-G710"
],
"Sony": [
"SonyST|SonyLT|SonyEricsson|SonyEricssonLT15iv|LT18i|E10i|LT28h|LT26w|SonyEricssonMT27i",
"C5303|C6902|C6903|C6906|C6943|D2533|SOV34|601SO|F8332"
],
"Asus": "Asus.*Galaxy|PadFone.*Mobile|ASUS_Z01QD|ASUS_X00TD",
"Xiaomi": [
"^(?!.*\\bx11\\b).*xiaomi.*$|POCOPHONE F1|\\bMI\\b 8|\\bMi\\b 10|Redmi Note 9S|Redmi 5A|Redmi Note 5A Prime|Redmi Note 7 Pro",
"N2G47H|M2001J2G|M2001J2I|M1805E10A|M2004J11G|M1902F1G|M2002J9G|M2004J19G|M2003J6A1G|M2012K11C|M2007J1SC"
],
"NokiaLumia": "Lumia [0-9]{3,4}",
"Micromax": "Micromax.*\\b(A210|A92|A88|A72|A111|A110Q|A115|A116|A110|A90S|A26|A51|A35|A54|A25|A27|A89|A68|A65|A57|A90)\\b",
"Palm": "PalmSource|Palm",
"Vertu": "Vertu|Vertu.*Ltd|Vertu.*Ascent|Vertu.*Ayxta|Vertu.*Constellation(F|Quest)?|Vertu.*Monika|Vertu.*Signature",
"Pantech": [
"PANTECH|IM-A850S|IM-A840S|IM-A830L|IM-A830K|IM-A830S|IM-A820L|IM-A810K|IM-A810S|IM-A800S|IM-T100K|IM-A725L",
"IM-A780L|IM-A775C|IM-A770K|IM-A760S|IM-A750K|IM-A740S|IM-A730S|IM-A720L|IM-A710K|IM-A690L|IM-A690S|IM-A650S",
"IM-A630K|IM-A600S|VEGA PTL21|PT003|P8010|ADR910L|P6030|P6020|P9070|P4100|P9060|P5000|CDM8992|TXT8045|ADR8995",
"IS11PT|P2030|P6010|P8000|PT002|IS06|CDM8999|P9050|PT001|TXT8040|P2020|P9020|P2000|P7040|P7000|C790"
],
"Fly": "IQ230|IQ444|IQ450|IQ440|IQ442|IQ441|IQ245|IQ256|IQ236|IQ255|IQ235|IQ245|IQ275|IQ240|IQ285|IQ280|IQ270|IQ260|IQ250",
"Wiko": [
"KITE 4G|HIGHWAY|GETAWAY|STAIRWAY|DARKSIDE|DARKFULL|DARKNIGHT|DARKMOON|SLIDE|WAX 4G|RAINBOW|BLOOM|SUNSET|GOA(?!nna)|LENNY",
"BARRY|IGGY|OZZY|CINK FIVE|CINK PEAX|CINK PEAX 2|CINK SLIM|CINK SLIM 2|CINK +|CINK KING|CINK PEAX|CINK SLIM|SUBLIM"
],
"iMobile": "i-mobile (IQ|i-STYLE|idea|ZAA|Hitz)",
"SimValley": "\\b(SP-80|XT-930|SX-340|XT-930|SX-310|SP-360|SP60|SPT-800|SP-120|SPT-800|SP-140|SPX-5|SPX-8|SP-100|SPX-8|SPX-12)\\b",
"Wolfgang": "AT-B24D|AT-AS50HD|AT-AS40W|AT-AS55HD|AT-AS45q2|AT-B26D|AT-AS50Q",
"Alcatel": "Alcatel",
"Nintendo": "Nintendo (3DS|Switch)",
"Amoi": "Amoi",
"INQ": "INQ",
"OnePlus": "ONEPLUS|CPH2663",
"GenericPhone": "Tapatalk|PDA;|SAGEM|\\bmmp\\b|pocket|\\bpsp\\b|symbian|Smartphone|smartfon|treo|up.browser|up.link|vodafone|\\bwap\\b|nokia|Series40|Series60|S60|SonyEricsson|N900|MAUI.*WAP.*Browser",
"Huawei": "HMSCore|Huawei"
},
"tablets": {
"iPad": "iPad|iPad.*Mobile",
"NexusTablet": "Android.*Nexus[\\s]+(7|9|10)",
"GoogleTablet": "Android.*Pixel C",
"SamsungTablet": [
"SM-X616B|SM-X610|SM-X516B|SM-X910|SM-X916B|SM-X816B|SM-X810|SM-X710|SM-X716B|SM-X510|SM-P619|SM-T225|SM-T225N|SM-T736B|SM-T505|SM-T733|SM-X205|SM-X210|SM-X216B",
"SM-X700|SM-X706|SM-X706B|SM-X706U|SM-X706N|SM-X800|SM-X806|SM-X806B|SM-X806U|SM-X806N|SM-X900|SM-X906|SM-X906B|SM-X906U|SM-X906N|SM-P613|SM-X110|SM-X115",
"SM-T970|SM-T380|SM-T5950|SM-T905|SM-T231|SM-T500|SM-T860|SM-T536|SM-T837A|SM-X200|SM-T220|SM-T870|SM-X906C",
"SM-T815Y|SM-T585|SM-T285|SM-T825|SM-W708|SM-T835|SM-T830|SM-T837V|SM-T720|SM-T510|SM-T387V|SM-P610|SM-T290|SM-T515|SM-T590|SM-T595|SM-T725|SM-T817P|SM-P585N0|SM-T395|SM-T295|SM-T865|SM-P610N|SM-P615",
"SM-T560|SM-T670|SM-T677|SM-T377|SM-T567|SM-T357T|SM-T555|SM-T561|SM-T713|SM-T719|SM-T813|SM-T819|SM-T580|SM-T355Y?|SM-T280|SM-T817A|SM-T820|SM-W700|SM-P580|SM-T587|SM-P350|SM-P555M|SM-P355M|SM-T113NU",
"SM-T807P|SM-P607T|SM-T217T|SM-T337T|SM-T807T|SM-T116NQ|SM-T116BU|SM-P550|SM-T350|SM-T550|SM-T9000|SM-P9000|SM-T705Y|SM-T805|GT-P3113|SM-T710|SM-T810|SM-T815|SM-T360|SM-T533|SM-T113|SM-T335|SM-T715",
"SM-P900X|SM-T210X|SM-T230|SM-T230X|SM-T325|GT-P7503|SM-T531|SM-T330|SM-T530|SM-T705|SM-T705C|SM-T535|SM-T331|SM-T800|SM-T700|SM-T537|SM-T807|SM-P907A|SM-T337A|SM-T537A|SM-T707A|SM-T807A|SM-T237",
"GT-N5120|SM-P905|SM-T111|SM-T2105|SM-T315|SM-T320|SM-T320X|SM-T321|SM-T520|SM-T525|SM-T530NU|SM-T230NU|SM-T330NU|SM-T900|XE500T1C|SM-P605V|SM-P905V|SM-T337V|SM-T537V|SM-T707V|SM-T807V|SM-P600X",
"GT-P5210X|SM-T311|SM-T310|SM-T310X|SM-T210|SM-T210R|SM-T211|SM-P600|SM-P601|SM-P605|SM-P900|SM-P901|SM-T217|SM-T217A|SM-T217S|SM-P6000|SM-T3100|SGH-I467|XE500|SM-T110|GT-P5220|GT-I9200X|GT-N5110X",
"SHW-M180S|SHW-M180W|SHW-M300W|SHW-M305W|SHW-M380K|SHW-M380S|SHW-M380W|SHW-M430W|SHW-M480K|SHW-M480S|SHW-M480W|SHW-M485W|SHW-M486W|SHW-M500W|GT-I9228|SCH-P739|SCH-I925|GT-I9200|GT-P5200|GT-P5210",
"GT-P5113|GT-P8110|GT-N8010|GT-N8005|GT-N8020|GT-P1013|GT-P6201|GT-P7501|GT-N5100|GT-N5105|GT-N5110|SHV-E140K|SHV-E140L|SHV-E140S|SHV-E150S|SHV-E230K|SHV-E230L|SHV-E230S|SHW-M180K|SHW-M180L",
"SGH-T849|SGH-T859|SGH-T869|SPH-P100|GT-P3100|GT-P3108|GT-P3110|GT-P5100|GT-P5110|GT-P6200|GT-P7320|GT-P7511|GT-N8000|GT-P8510|SGH-I497|SPH-P500|SGH-T779|SCH-I705|SCH-I915|GT-N8013|GT-P3113",
"SAMSUNG.*Tablet|Galaxy.*Tab|SC-01C|GT-P1000|GT-P1003|GT-P1010|GT-P3105|GT-P6210|GT-P6800|GT-P6810|GT-P7100|GT-P7300|GT-P7310|GT-P7500|GT-P7510|SCH-I800|SCH-I815|SCH-I905|SGH-I957|SGH-I987|SM-X300|SM-T630"
],
"Kindle": "Kindle|Silk.*Accelerated|Android.*\\b(KFOT|KFTT|KFJWI|KFJWA|KFOTE|KFSOWI|KFTHWI|KFTHWA|KFAPWI|KFAPWA|WFJWAE|KFSAWA|KFSAWI|KFASWI|KFARWI|KFFOWI|KFGIWI|KFMEWI)\\b|Android.*Silk\/[0-9.]+ like Chrome\/[0-9.]+ (?!Mobile)",
"SurfaceTablet": "Windows NT [0-9.]+; ARM;.*(Tablet|ARMBJS)",
"HPTablet": "HP Slate (7|8|10)|HP ElitePad 900|hp-tablet|EliteBook.*Touch|HP 8|Slate 21|HP SlateBook 10",
"AsusTablet": [
"ME181C|P01Y|PO1MA|P01Z|\\bP027\\b|\\bP024\\b|\\bP00C\\b",
"\\bK00C\\b|\\bK00E\\b|\\bK00L\\b|TX201LA|ME176C|ME102A|\\bM80TA\\b|ME372CL|ME560CG|ME372CG|ME302KL| K01A | K010 | K011 | K017 | K01E |ME572C|ME103K|ME170C|ME171C|\\bME70C\\b|ME581C|ME581CL|ME8510C",
"^.*PadFone((?!Mobile).)*$|Transformer|TF101|TF101G|TF300T|TF300TG|TF300TL|TF700T|TF700KL|TF701T|TF810C|ME171|ME301T|ME302C|ME371MG|ME370T|ME372MG|ME172V|ME173X|ME400C|Slider SL101|\\bK00F\\b"
],
"BlackBerryTablet": "PlayBook|RIM Tablet",
"HTCtablet": "HTC_Flyer_P512|HTC Flyer|HTC Jetstream|HTC-P715a|HTC EVO View 4G|PG41200|PG09410",
"MotorolaTablet": "xoom|sholest|MZ615|MZ605|MZ505|MZ601|MZ602|MZ603|MZ604|MZ606|MZ607|MZ608|MZ609|MZ615|MZ616|MZ617",
"NookTablet": "Android.*Nook|NookColor|nook browser|BNRV200|BNRV200A|BNTV250|BNTV250A|BNTV400|BNTV600|LogicPD Zoom2",
"AcerTablet": [
"Android.*; \\b(A100|A101|A110|A200|A210|A211|A500|A501|A510|A511|A700|A701|W500|W500P|W501|W501P|W510|W511|W700|G100|G100W|B1-A71|B1-710|B1-711|A1-810|A1-811|A1-830)\\b",
"W3-810|\\bA3-A10\\b|\\bA3-A11\\b|\\bA3-A20\\b|\\bA3-A30|A3-A40"
],
"ToshibaTablet": "Android.*(AT100|AT105|AT200|AT205|AT270|AT275|AT300|AT305|AT1S5|AT500|AT570|AT700|AT830)|TOSHIBA.*FOLIO",
"LGTablet": "\\bL-06C|LG-V909|LG-V900|LG-V700|LG-V510|LG-V500|LG-V410|LG-V400|LG-VK810\\b",
"FujitsuTablet": "Android.*\\b(F-01D|F-02F|F-05E|F-10D|M532|Q572)\\b",
"PrestigioTablet": [
"PMP3170B|PMP3270B|PMP3470B|PMP7170B|PMP3370B|PMP3570C|PMP5870C|PMP3670B|PMP5570C|PMP5770D|PMP3970B|PMP3870C|PMP5580C|PMP5880D|PMP5780D|PMP5588C|PMP7280C",
"PMP7280C3G|PMP7280|PMP7880D|PMP5597D|PMP5597|PMP7100D|PER3464|PER3274|PER3574|PER3884|PER5274|PER5474|PMP5097CPRO|PMP5097|PMP7380D|PMP5297C|PMP5297C_QUAD",
"PMP812E|PMP812E3G|PMP812F|PMP810E|PMP880TD|PMT3017|PMT3037|PMT3047|PMT3057|PMT7008|PMT5887|PMT5001|PMT5002"
],
"LenovoTablet": [
"TB-X704L|TB-J606F|TB-X606F|TB-X306X|YT-J706X|TB128FU",
"YT3-X50M|YT-X705F|YT-X703F|YT-X703L|YT-X705L|YT-X705X|TB2-X30F|TB2-X30L|TB2-X30M|A2107A-F|A2107A-H|TB3-730F|TB3-730M|TB3-730X|TB-7504F|TB-7504X|TB-X704F|TB-X104F|TB3-X70F|TB-X705F|TB-8504F|TB3-X70L|TB3-710F",
"TB-X103F|TB-X304X|TB-X304F|TB-X304L|TB-X505F|TB-X505L|TB-X505X|TB-X605F|TB-X605L|TB-8703F|TB-8703X|TB-8703N|TB-8704N|TB-8704F|TB-8704X|TB-8704V|TB-7304F|TB-7304I|TB-7304X|Tab2A7-10F|Tab2A7-20F|TB2-X30L|YT3-X50L|YT3-X50F",
"Lenovo TAB|Idea(Tab|Pad)( A1|A10| K1|)|ThinkPad([ ]+)?Tablet|YT3-850M|YT3-X90L|YT3-X90F|YT3-X90X|Lenovo.*(S2109|S2110|S5000|S6000|K3011|A3000|A3500|A1000|A2107|A2109|A1107|A5500|A7600|B6000|B8000|B8080)(-|)(FL|F|HV|H|)"
],
"DellTablet": "Venue 11|Venue 8|Venue 7|Dell Streak 10|Dell Streak 7",
"XiaomiTablet": "21051182G",
"YarvikTablet": [
"Android.*\\b(TAB10-400|TAB10-410|TAB13-201|TAB274EUK|TAB275EUK|TAB374EUK|TAB462EUK|TAB474EUK|TAB9-200)\\b",
"Android.*\\b(TAB07-200|TAB07-201-3G|TAB07-210|TAB07-211|TAB07-212|TAB07-214|TAB07-220|TAB07-400|TAB07-485|TAB08-150|TAB08-200|TAB08-201-3G|TAB08-201-30|TAB09-100|TAB09-211|TAB09-410|TAB10-150|TAB10-201|TAB10-211)\\b",
"Android.*\\b(TAB210|TAB211|TAB224|TAB250|TAB260|TAB264|TAB310|TAB360|TAB364|TAB410|TAB411|TAB420|TAB424|TAB450|TAB460|TAB461|TAB464|TAB465|TAB467|TAB468|TAB07-100|TAB07-101|TAB07-150|TAB07-151|TAB07-152)\\b"
],
"MedionTablet": "Android.*\\bOYO\\b|LIFE.*(P9212|P9514|P9516|S9512)|LIFETAB",
"ArnovaTablet": "97G4|AN10G2|AN7bG3|AN7fG3|AN8G3|AN8cG3|AN7G3|AN9G3|AN7dG3|AN7dG3ST|AN7dG3ChildPad|AN10bG3|AN10bG3DT|AN9G2",
"IntensoTablet": "INM8002KP|INM1010FP|INM805ND|Intenso Tab|TAB1004",
"IRUTablet": "M702pro",
"MegafonTablet": "MegaFon V9|\\bZTE V9\\b|Android.*\\bMT7A\\b",
"EbodaTablet": "E-Boda (Supreme|Impresspeed|Izzycomm|Essential)",
"AllViewTablet": "Allview.*(Viva|Alldro|City|Speed|All TV|Frenzy|Quasar|Shine|TX1|AX1|AX2)",
"ArchosTablet": "\\b(101G9|80G9|A101IT)\\b|Qilive 97R|Archos5|\\bARCHOS (70|79|80|90|97|101|FAMILYPAD|)(b|c|)(G10| Cobalt| TITANIUM(HD|)| Xenon| Neon|XSK| 2| XS 2| PLATINUM| CARBON|GAMEPAD)\\b",
"AinolTablet": "NOVO7|NOVO8|NOVO10|Novo7Aurora|Novo7Basic|NOVO7PALADIN|novo9-Spark",
"NokiaLumiaTablet": "Lumia 2520",
"SonyTablet": [
"EBRD1101|EBRD1102|EBRD1201|SGP351|SGP341|SGP511|SGP512|SGP521|SGP541|SGP551|SGP621|SGP641|SGP612|SOT31|SGP771|SGP611|SGP612|SGP712",
"Sony.*Tablet|Xperia Tablet|Sony Tablet S|SO-03E|SGPT12|SGPT13|SGPT114|SGPT121|SGPT122|SGPT123|SGPT111|SGPT112|SGPT113|SGPT131|SGPT132|SGPT133|SGPT211|SGPT212|SGPT213|SGP311|SGP312|SGP321"
],
"PhilipsTablet": "\\b(PI2010|PI3000|PI3100|PI3105|PI3110|PI3205|PI3210|PI3900|PI4010|PI7000|PI7100)\\b",
"CubeTablet": "Android.*(K8GT|U9GT|U10GT|U16GT|U17GT|U18GT|U19GT|U20GT|U23GT|U30GT)|CUBE U8GT",
"CobyTablet": "MID1042|MID1045|MID1125|MID1126|MID7012|MID7014|MID7015|MID7034|MID7035|MID7036|MID7042|MID7048|MID7127|MID8042|MID8048|MID8127|MID9042|MID9740|MID9742|MID7022|MID7010",
"MIDTablet": [
"M9701|M9000|M9100|M806|M1052|M806|T703|MID701|MID713|MID710|MID727|MID760|MID830|MID728|MID933|MID125|MID810|MID732|MID120|MID930|MID800",
"MID731|MID900|MID100|MID820|MID735|MID980|MID130|MID833|MID737|MID960|MID135|MID860|MID736|MID140|MID930|MID835|MID733|MID4X10"
],
"MSITablet": "MSI \\b(Primo 73K|Primo 73L|Primo 81L|Primo 77|Primo 93|Primo 75|Primo 76|Primo 73|Primo 81|Primo 91|Primo 90|Enjoy 71|Enjoy 7|Enjoy 10)\\b",
"SMiTTablet": "Android.*(\\bMID\\b|MID-560|MTV-T1200|MTV-PND531|MTV-P1101|MTV-PND530)",
"RockChipTablet": "Android.*(RK2818|RK2808A|RK2918|RK3066)|RK2738|RK2808A",
"FlyTablet": "IQ310|Fly Vision",
"bqTablet": "Android.*(bq)?.*\\b(Elcano|Curie|Edison|Maxwell|Kepler|Pascal|Tesla|Hypatia|Platon|Newton|Livingstone|Cervantes|Avant|Aquaris ([E|M]10|M8))\\b|Maxwell.*Lite|Maxwell.*Plus",
"HuaweiTablet": "MediaPad|MediaPad 7 Youth|IDEOS S7|S7-201c|S7-202u|S7-101|S7-103|S7-104|S7-105|S7-106|S7-201|S7-Slim|M2-A01L|BAH-L09|BAH-W09|AGS-L09|CMR-AL19|KOB2-L09|BG2-U01|BG2-W09|BG2-U03|AGS-W09",
"NecTablet": "\\bN-06D|\\bN-08D",
"PantechTablet": "Pantech.*P4100",
"BronchoTablet": "Broncho.*(N701|N708|N802|a710)",
"VersusTablet": "TOUCHPAD.*[78910]|\\bTOUCHTAB\\b",
"ZyncTablet": "z1000|Z99 2G|z930|z990|z909|Z919|z900",
"PositivoTablet": "TB07STA|TB10STA|TB07FTA|TB10FTA",
"NabiTablet": "Android.*\\bNabi",
"KoboTablet": "Kobo Touch|\\bK080\\b|\\bVox\\b Build|\\bArc\\b Build",
"DanewTablet": "DSlide.*\\b(700|701R|702|703R|704|802|970|971|972|973|974|1010|1012)\\b",
"TexetTablet": [
"TB-840HD|TB-760HD|TB-750HD|TB-740HD|TB-730HD|TB-722HD|TB-720HD|TB-700HD|TB-500HD|TB-470HD|TB-431HD|TB-430HD|TB-506|TB-504|TB-446|TB-436|TB-416|TB-146SE|TB-126SE",
"TB-719A|TB-823A|TB-805A|TB-723A|TB-715A|TB-707A|TB-705A|TB-709A|TB-711A|TB-890HD|TB-880HD|TB-790HD|TB-780HD|TB-770HD|TB-721HD|TB-710HD|TB-434HD|TB-860HD",
"TM-7011|TM-7010|TM-7023|TM-7025|TM-7037W|TM-7038W|TM-7027W|TM-9720|TM-9725|TM-9737W|TM-1020|TM-9738W|TM-9740|TM-9743W|TB-807A|TB-771A|TB-727A|TB-725A",
"NaviPad|TB-772A|TM-7045|TM-7055|TM-9750|TM-7016|TM-7024|TM-7026|TM-7041|TM-7043|TM-7047|TM-8041|TM-9741|TM-9747|TM-9748|TM-9751|TM-7022|TM-7021|TM-7020"
],
"PlaystationTablet": "Playstation.*(Portable|Vita)",
"TrekstorTablet": "ST10416-1|VT10416-1|ST70408-1|ST702xx-1|ST702xx-2|ST80208|ST97216|ST70104-2|VT10416-2|ST10216-2A|SurfTab",
"PyleAudioTablet": "\\b(PTBL10CEU|PTBL10C|PTBL72BC|PTBL72BCEU|PTBL7CEU|PTBL7C|PTBL92BC|PTBL92BCEU|PTBL9CEU|PTBL9CUK|PTBL9C)\\b",
"AdvanTablet": "Android.* \\b(E3A|T3X|T5C|T5B|T3E|T3C|T3B|T1J|T1F|T2A|T1H|T1i|E1C|T1-E|T5-A|T4|E1-B|T2Ci|T1-B|T1-D|O1-A|E1-A|T1-A|T3A|T4i)\\b ",
"DanyTechTablet": "Genius Tab G3|Genius Tab S2|Genius Tab Q3|Genius Tab G4|Genius Tab Q4|Genius Tab G-II|Genius TAB GII|Genius TAB GIII|Genius Tab S1",
"GalapadTablet": "Android [0-9.]+; [a-z-]+; \\bG1\\b",
"MicromaxTablet": "Funbook|Micromax.*\\b(P250|P560|P360|P362|P600|P300|P350|P500|P275)\\b",
"KarbonnTablet": "Android.*\\b(A39|A37|A34|ST8|ST10|ST7|Smart Tab3|Smart Tab2)\\b",
"AllFineTablet": "Fine7 Genius|Fine7 Shine|Fine7 Air|Fine8 Style|Fine9 More|Fine10 Joy|Fine11 Wide",
"PROSCANTablet": [
"\\b(PLT8088|PLT8223G|PLT8234G|PLT8235G|PLT8816K|PLT9011|PLT9045K|PLT9233G|PLT9735|PLT9760G|PLT9770G)\\b",
"\\b(PLT7045KB|PLT7071KG|PLT7072|PLT7223G|PLT7225G|PLT7777G|PLT7810K|PLT7849G|PLT7851G|PLT7852G|PLT8015|PLT8031|PLT8034|PLT8036|PLT8080K|PLT8082)\\b",
"\\b(PEM63|PLT1023G|PLT1041|PLT1044|PLT1044G|PLT1091|PLT4311|PLT4311PL|PLT4315|PLT7030|PLT7033|PLT7033D|PLT7035|PLT7035D|PLT7044K|PLT7045K)\\b"
],
"YONESTablet": "BQ1078|BC1003|BC1077|RK9702|BC9730|BC9001|IT9001|BC7008|BC7010|BC708|BC728|BC7012|BC7030|BC7027|BC7026",
"ChangJiaTablet": [
"TPC10101|TPC10103|TPC10106|TPC10111|TPC10203|TPC10205|TPC10503",
"TPC8203|TPC8205|TPC8503|TPC9106|TPC9701|TPC97101|TPC97103|TPC97105|TPC97106|TPC97111|TPC97113|TPC97203|TPC97603|TPC97809|TPC97205",
"TPC7102|TPC7103|TPC7105|TPC7106|TPC7107|TPC7201|TPC7203|TPC7205|TPC7210|TPC7708|TPC7709|TPC7712|TPC7110|TPC8101|TPC8103|TPC8105|TPC8106"
],
"GUTablet": "TX-A1301|TX-M9002|Q702|kf026",
"PointOfViewTablet": [
"TAB-PL1015|TAB-P1025|TAB-PI1045|TAB-P1325|TAB-PROTAB[0-9]+|TAB-PROTAB25|TAB-PROTAB26|TAB-PROTAB27|TAB-PROTAB26XL|TAB-PROTAB2-IPS9|TAB-PROTAB30-IPS9|TAB-PROTAB25XXL|TAB-PROTAB26-IPS10|TAB-PROTAB30-IPS10",
"TAB-P506|TAB-navi-7-3G-M|TAB-P517|TAB-P-527|TAB-P701|TAB-P703|TAB-P721|TAB-P731N|TAB-P741|TAB-P825|TAB-P905|TAB-P925|TAB-PR945"
],
"OvermaxTablet": "OV-(SteelCore|NewBase|Basecore|Baseone|Exellen|Quattor|EduTab|Solution|ACTION|BasicTab|TeddyTab|MagicTab|Stream|TB-08|TB-09)|Qualcore 1027",
"HCLTablet": "HCL.*Tablet|Connect-3G-2.0|Connect-2G-2.0|ME Tablet U1|ME Tablet U2|ME Tablet G1|ME Tablet X1|ME Tablet Y2|ME Tablet Sync",
"DPSTablet": "DPS Dream 9|DPS Dual 7",
"VistureTablet": "V97 HD|i75 3G|Visture V4( HD)?|Visture V5( HD)?|Visture V10",
"CrestaTablet": "CTP(-)?810|CTP(-)?818|CTP(-)?828|CTP(-)?838|CTP(-)?888|CTP(-)?978|CTP(-)?980|CTP(-)?987|CTP(-)?988|CTP(-)?989",
"MediatekTablet": "\\bMT8125|MT8389|MT8135|MT8377\\b",
"ConcordeTablet": "Concorde([ ]+)?Tab|ConCorde ReadMan",
"GoCleverTablet": [
"TAB T72|TAB R83|TAB R974|TAB R973|TAB A101|TAB A103|TAB A104|TAB A104.2|R105BK|M713G|A972BK|TAB A971|TAB R974.2|TAB R104|TAB R83.3|TAB A1042",
"TAB R70|TAB R76.2|TAB R106|TAB R83.2|TAB M813G|TAB I721|GCTA722|TAB I70|TAB I71|TAB S73|TAB R73|TAB R74|TAB R93|TAB R75|TAB R76.1|TAB A73|TAB A93|TAB A93.2",
"GOCLEVER TAB|A7GOCLEVER|M1042|M7841|M742|R1042BK|R1041|TAB A975|TAB A7842|TAB A741|TAB A741L|TAB M723G|TAB M721|TAB A1021|TAB I921|TAB R721|TAB I720|TAB T76"
],
"ModecomTablet": [
"FreeTAB 9000|FreeTAB 7.4|FreeTAB 7004|FreeTAB 7800|FreeTAB 2096|FreeTAB 7.5|FreeTAB 1014|FreeTAB 1001 |FreeTAB 8001|FreeTAB 9706|FreeTAB 9702",
"FreeTAB 7003|FreeTAB 7002|FreeTAB 1002|FreeTAB 7801|FreeTAB 1331|FreeTAB 1004|FreeTAB 8002|FreeTAB 8014|FreeTAB 9704|FreeTAB 1003"
],
"VoninoTablet": [
"\\b(Argus[ _]?S|Diamond[ _]?79HD|Emerald[ _]?78E|Luna[ _]?70C|Onyx[ _]?S|Onyx[ _]?Z|Orin[ _]?HD|Orin[ _]?S|Otis[ _]?S|SpeedStar[ _]?S|Magnet[ _]?M9|Primus[ _]?94[ _]?3G|Primus[ _]?94HD|Primus[ _]?QS)\\b",
"\\b(Android.*\\bQ8\\b|Sirius[ _]?EVO[ _]?QS|Sirius[ _]?QS|Spirit[ _]?S)\\b"
],
"ECSTablet": "V07OT2|TM105A|S10OT1|TR10CS1",
"StorexTablet": "eZee[_']?(Tab|Go)[0-9]+|TabLC7|Looney Tunes Tab",
"VodafoneTablet": "SmartTab([ ]+)?[0-9]+|SmartTabII10|SmartTabII7|VF-1497|VFD 1400",
"EssentielBTablet": "Smart[ ']?TAB[ ]+?[0-9]+|Family[ ']?TAB2",
"RossMoorTablet": "RM-790|RM-997|RMD-878G|RMD-974R|RMT-705A|RMT-701|RME-601|RMT-501|RMT-711",
"iMobileTablet": "i-mobile i-note",
"TolinoTablet": "tolino tab [0-9.]+|tolino shine",
"AudioSonicTablet": "\\bC-22Q|T7-QC|T-17B|T-17P\\b",
"AMPETablet": "Android.* A78 ",
"SkkTablet": "Android.* (SKYPAD|PHOENIX|CYCLOPS)",
"TecnoTablet": "TECNO P9|TECNO DP8D",
"JXDTablet": [
"Android.* \\b(F3000|A3300|JXD5000|JXD3000|JXD2000|JXD300B|JXD300|S5800|S7800|S602b|S5110b|S7300|S5300|S602|S603)\\b",
"Android.* \\b(S5100|S5110|S601|S7100a|P3000F|P3000s|P101|P200s|P1000m|P200m|P9100|P1000s|S6600b|S908|P1000|P300|S18|S6600|S9100)\\b"
],
"iJoyTablet": [
"Tablet (Spirit 7|Essentia|Galatea|Fusion|Onix 7|Landa|Titan|Scooby|Deox|Stella|Themis|Argon|Unique 7|Sygnus|Hexen|Finity 7)",
"Tablet (Cream|Cream X2|Jade|Neon 7|Neron 7|Kandy|Scape|Saphyr 7|Rebel|Biox|Rebel|Rebel 8GB|Myst|Draco 7|Myst|Tab7-004|Myst)",
"Tablet (Tadeo Jones|Tablet Boing|Arrow|Draco Dual Cam|Aurix|Mint|Amity|Revolution|Finity 9|Neon 9|T9w|Amity 4GB Dual Cam)",
"Tablet (Stone 4GB|Stone 8GB|Andromeda|Silken|X2|Andromeda II|Halley|Flame|Saphyr 9,7|Touch 8|Planet|Triton|Unique 10|Hexen 10|Memphis 4GB|Memphis 8GB|Onix 10)"
],
"FX2Tablet": "FX2 PAD7|FX2 PAD10",
"XoroTablet": [
"KidsPAD 701|PAD[ ]?712|PAD[ ]?714|PAD[ ]?716|PAD[ ]?717|PAD[ ]?718|PAD[ ]?720|PAD[ ]?721|PAD[ ]?722|PAD[ ]?790",
"PAD[ ]?792|PAD[ ]?900|PAD[ ]?9715D|PAD[ ]?9716DR|PAD[ ]?9718DR|PAD[ ]?9719QR|PAD[ ]?9720QR|TelePAD1030|Telepad1032",
"TelePAD730|TelePAD731|TelePAD732|TelePAD735Q|TelePAD830|TelePAD9730|TelePAD795|MegaPAD 1331|MegaPAD 1851|MegaPAD 2151"
],
"ViewsonicTablet": "ViewPad 10pi|ViewPad 10e|ViewPad 10s|ViewPad E72|ViewPad7|ViewPad E100|ViewPad 7e|ViewSonic VB733|VB100a",
"VerizonTablet": "QTAQZ3|QTAIR7|QTAQTZ3|QTASUN1|QTASUN2|QTAXIA1",
"OdysTablet": "LOOX|XENO10|ODYS[ -](Space|EVO|Xpress|NOON)|\\bXELIO\\b|Xelio10Pro|XELIO7PHONETAB|XELIO10EXTREME|XELIOPT2|NEO_QUAD10",
"CaptivaTablet": "CAPTIVA PAD",
"IconbitTablet": "NetTAB|NT-3702|NT-3702S|NT-3702S|NT-3603P|NT-3603P|NT-0704S|NT-0704S|NT-3805C|NT-3805C|NT-0806C|NT-0806C|NT-0909T|NT-0909T|NT-0907S|NT-0907S|NT-0902S|NT-0902S",
"TeclastTablet": [
"T98 4G|\\bP80\\b|\\bX90HD\\b|X98 Air|X98 Air 3G|\\bX89\\b|P80 3G|\\bX80h\\b|P98 Air|\\bX89HD\\b|P98 3G|\\bP90HD\\b|P89 3G|X98 3G",
"\\bP70h\\b|P79HD 3G|G18d 3G|\\bP79HD\\b|\\bP89s\\b|\\bA88\\b|\\bP10HD\\b|\\bP19HD\\b|G18 3G|\\bP78HD\\b|\\bA78\\b|\\bP75\\b|G17s 3G|G17h 3G",
"\\bP85t\\b|\\bP90\\b|\\bP11\\b|\\bP98t\\b|\\bP98HD\\b|\\bG18d\\b|\\bP85s\\b|\\bP11HD\\b|\\bP88s\\b|\\bA80HD\\b|\\bA80se\\b|\\bA10h\\b|\\bP89\\b",
"\\bP78s\\b|\\bG18\\b|\\bP85\\b|\\bA70h\\b|\\bA70\\b|\\bG17\\b|\\bP18\\b|\\bA80s\\b|\\bA11s\\b|\\bP88HD\\b|\\bA80h\\b|\\bP76s\\b|\\bP76h\\b|\\bP98\\b",
"\\bA10HD\\b|\\bP78\\b|\\bP88\\b|\\bA11\\b|\\bA10t\\b|\\bP76a\\b|\\bP76t\\b|\\bP76e\\b|\\bP85HD\\b|\\bP85a\\b|\\bP86\\b|\\bP75HD\\b|\\bP76v\\b|\\bA12\\b",
"\\bP75a\\b|\\bA15\\b|\\bP76Ti\\b|\\bP81HD\\b|\\bA10\\b|\\bT760VE\\b|\\bT720HD\\b|\\bP76\\b|\\bP73\\b|\\bP71\\b|\\bP72\\b|\\bT720SE\\b|\\bC520Ti\\b|\\bT760\\b|\\bT720VE\\b|T720-3GE|T720-WiFi"
],
"OndaTablet": [
"\\b(V975i|Vi30|VX530|V701|Vi60|V701s|Vi50|V801s|V719|Vx610w|VX610W|V819i|Vi10|VX580W|Vi10|V711s|V813|V811)\\b[\\s]+",
"\\b(V820w|V820|Vi20|V711|VI30W|V712|V891w|V972|V819w|V820w|Vi60|V820w|V711|V813s|V801|V819|V975s|V801|V819)\\b[\\s]+",
"\\b(V819|V818|V811|V712|V975m|V101w|V961w|V812|V818|V971|V971s|V919|V989|V116w|V102w|V973|Vi40)\\b[\\s]+|V10 \\b4G\\b"
],
"JaytechTablet": "TPC-PA762",
"BlaupunktTablet": "Endeavour 800NG|Endeavour 1010",
"DigmaTablet": "\\b(iDx10|iDx9|iDx8|iDx7|iDxD7|iDxD8|iDsQ8|iDsQ7|iDsQ8|iDsD10|iDnD7|3TS804H|iDsQ11|iDj7|iDs10)\\b",
"EvolioTablet": "ARIA_Mini_wifi|Aria[ _]Mini|Evolio X10|Evolio X7|Evolio X8|\\bEvotab\\b|\\bNeura\\b",
"LavaTablet": "QPAD E704|\\bIvoryS\\b|E-TAB IVORY|\\bE-TAB\\b",
"AocTablet": "MW0811|MW0812|MW0922|MTK8382|MW1031|MW0831|MW0821|MW0931|MW0712",
"MpmanTablet": [
"MP11 OCTA|MP10 OCTA|MPQC1114|MPQC1004|MPQC994|MPQC974|MPQC973|MPQC804|MPQC784|MPQC780|\\bMPG7\\b|MPDCG75|MPDCG71",
"MPDC1006|MP101DC|MPDC9000|MPDC905|MPDC706HD|MPDC706|MPDC705|MPDC110|MPDC100|MPDC99|MPDC97|MPDC88|MPDC8|MPDC77",
"MP709|MID701|MID711|MID170|MPDC703|MPQC1010"
],
"CelkonTablet": "CT695|CT888|CT[\\s]?910|CT7 Tab|CT9 Tab|CT3 Tab|CT2 Tab|CT1 Tab|C820|C720|\\bCT-1\\b",
"WolderTablet": [
"miTab \\b(DIAMOND|SPACE|BROOKLYN|NEO|FLY|MANHATTAN|FUNK|EVOLUTION|SKY|GOCAR|IRON|GENIUS|POP|MINT)\\b",
"miTab \\b(EPSILON|BROADWAY|JUMP|HOP|LEGEND|NEW AGE|LINE|ADVANCE|FEEL|FOLLOW|LIKE|LINK|LIVE|THINK|FREEDOM|CHICAGO|CLEVELAND)\\b",
"miTab \\b(BALTIMORE-GH|IOWA|BOSTON|SEATTLE|PHOENIX|DALLAS|IN 101|MasterChef)\\b"
],
"MediacomTablet": "M-MPI10C3G|M-SP10EG|M-SP10EGP|M-SP10HXAH|M-SP7HXAH|M-SP10HXBH|M-SP8HXAH|M-SP8MXA",
"MiTablet": "\\bMI PAD\\b|\\bHM NOTE 1W\\b",
"NibiruTablet": "Nibiru M1|Nibiru Jupiter One",
"NexoTablet": "NEXO NOVA|NEXO 10|NEXO AVIO|NEXO FREE|NEXO GO|NEXO EVO|NEXO 3G|NEXO SMART|NEXO KIDDO|NEXO MOBI",
"LeaderTablet": [
"TBLT10Q|TBLT10I|TBL-10WDKB|TBL-10WDKBO2013|TBL-W230V2|TBL-W450|TBL-W500|SV572|TBLT7I|TBA-AC7-8G",
"TBLT79|TBL-8W16|TBL-10W32|TBL-10WKB|TBL-W100"
],
"UbislateTablet": "UbiSlate[\\s]?7C",
"PocketBookTablet": "Pocketbook",
"KocasoTablet": "\\b(TB-1207)\\b",
"HisenseTablet": "\\b(F5281|E2371)\\b",
"Hudl": "Hudl HT7S3|Hudl 2",
"TelstraTablet": "T-Hub2",
"GenericTablet": [
"Android.*\\b97D\\b|Tablet(?!.*PC)|BNTV250A|MID-WCDMA|LogicPD Zoom2|\\bA7EB\\b|CatNova8|A1_07|CT704|CT1002",
"\\bM721\\b|rk30sdk|\\bEVOTAB\\b|M758A|ET904|ALUMIUM10|Smartfren Tab|Endeavour 1010|Tablet-PC-4|Tagi Tab",
"\\bM6pro\\b|CT1020W|arc 10HD|\\bTP750\\b|\\bQTAQZ3\\b|WVT101|TM1088|KT107"
]
},
"browsers": {
"Chrome": "\\bCrMo\\b|CriOS.*Mobile|Android.*Chrome\/[.0-9]* Mobile",
"Dolfin": "\\bDolfin\\b",
"Opera": "Opera.*Mini|Opera.*Mobi|Android.*Opera|Mobile.*OPR\/[0-9.]+$|Coast\/[0-9.]+",
"Skyfire": "Skyfire",
"Edge": "EdgiOS.*Mobile|Mobile Safari\/[.0-9]* Edge",
"IE": "IEMobile|MSIEMobile",
"Firefox": "fennec|firefox.*maemo|(Mobile|Tablet).*Firefox|Firefox.*Mobile|FxiOS.*Mobile",
"Bolt": "bolt",
"TeaShark": "teashark",
"Blazer": "Blazer",
"Safari": "Version((?!\\bEdgiOS\\b).)*Mobile.*Safari|Safari.*Mobile|MobileSafari",
"WeChat": "\\bMicroMessenger\\b",
"UCBrowser": "UC.*Browser|UCWEB",
"baiduboxapp": "baiduboxapp",
"baidubrowser": "baidubrowser",
"DiigoBrowser": "DiigoBrowser",
"Mercury": "\\bMercury\\b",
"ObigoBrowser": "Obigo",
"NetFront": "NF-Browser",
"GenericBrowser": "NokiaBrowser|OviBrowser|OneBrowser|TwonkyBeamBrowser|SEMC.*Browser|FlyFlow|Minimo|NetFront|Novarra-Vision|MQQBrowser|MicroMessenger",
"PaleMoon": "Android.*PaleMoon|Mobile.*PaleMoon",
"HuaweiBrowser": "HuaweiBrowser"
},
"os": {
"AndroidOS": "Android",
"BlackBerryOS": "blackberry|\\bBB10\\b|rim tablet os",
"PalmOS": "PalmOS|avantgo|blazer|elaine|hiptop|palm|plucker|xiino",
"SymbianOS": "Symbian|SymbOS|Series60|Series40|SYB-[0-9]+|\\bS60\\b",
"WindowsMobileOS": "Windows CE.*(PPC|Smartphone|Mobile|[0-9]{3}x[0-9]{3})|Windows Mobile|Windows Phone [0-9.]+|WCE;",
"WindowsPhoneOS": "Windows Phone 10.0|Windows Phone 8.1|Windows Phone 8.0|Windows Phone OS|XBLWP7|ZuneWP7|Windows NT 6.[23]; ARM;",
"iOS": "\\biPhone.*Mobile|\\biPod|\\biPad|AppleCoreMedia",
"iPadOS": "CPU OS 13",
"SailfishOS": "Sailfish",
"MeeGoOS": "MeeGo",
"MaemoOS": "Maemo",
"JavaOS": "J2ME\/|\\bMIDP\\b|\\bCLDC\\b",
"webOS": "webOS|hpwOS",
"badaOS": "\\bBada\\b",
"BREWOS": "BREW",
"HarmonyOS": "HarmonyOS"
}
}
}
================================================
FILE: README-EXAMPLES.md
================================================
# MobileDetect Usage Examples
This document provides code examples for common MobileDetect usage scenarios.
## Basic Usage
### Installation
```bash
composer require mobiledetect/mobiledetectlib
```
### Simple Detection
```php
use Detection\MobileDetect;
$detect = new MobileDetect();
if ($detect->isMobile()) {
// Any mobile device (phones or tablets)
}
if ($detect->isTablet()) {
// Tablets only
}
if ($detect->isMobile() && !$detect->isTablet()) {
// Phones only
}
```
### Detect Specific Devices
```php
use Detection\MobileDetect;
$detect = new MobileDetect();
// Detect specific platforms
if ($detect->isiOS()) {
// iOS device
}
if ($detect->isAndroidOS()) {
// Android device
}
// Detect specific devices
if ($detect->isiPhone()) {
// iPhone
}
if ($detect->isiPad()) {
// iPad
}
if ($detect->isSamsung()) {
// Samsung device
}
if ($detect->isSamsungTablet()) {
// Samsung tablet
}
```
### Detect Browsers
```php
use Detection\MobileDetect;
$detect = new MobileDetect();
if ($detect->isChrome()) {
// Chrome browser
}
if ($detect->isSafari()) {
// Safari browser
}
if ($detect->isFirefox()) {
// Firefox browser
}
if ($detect->isOpera()) {
// Opera browser
}
if ($detect->isEdge()) {
// Edge browser
}
```
### Get Version Information
```php
use Detection\MobileDetect;
$detect = new MobileDetect();
// Get version as string
$iOSVersion = $detect->version('iOS'); // e.g., "15_0"
// Get version as float
$iOSVersion = $detect->version('iOS', 'float'); // e.g., 15.0
// Get browser versions
$chromeVersion = $detect->version('Chrome');
$safariVersion = $detect->version('Safari');
```
## Advanced Usage
### Manual User-Agent Setting
```php
use Detection\MobileDetect;
// Disable auto-initialization for better performance
$detect = new MobileDetect(null, ['autoInitOfHttpHeaders' => false]);
// Set User-Agent manually
$detect->setUserAgent('Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X)...');
if ($detect->isMobile()) {
// Handle mobile
}
```
### Custom HTTP Headers
```php
use Detection\MobileDetect;
$detect = new MobileDetect();
// Set custom headers (useful for proxy/CDN scenarios)
$detect->setHttpHeaders([
'HTTP_USER_AGENT' => 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X)...',
'HTTP_ACCEPT' => 'text/html,application/xhtml+xml...',
]);
```
### Using the `is()` Method
```php
use Detection\MobileDetect;
$detect = new MobileDetect();
// Generic check using rule name
$detect->is('iOS'); // Same as $detect->isiOS()
$detect->is('iPhone'); // Same as $detect->isiPhone()
$detect->is('Chrome'); // Same as $detect->isChrome()
$detect->is('mobile'); // Same as $detect->isMobile()
$detect->is('tablet'); // Same as $detect->isTablet()
```
### Custom Cache Implementation
```php
use Detection\MobileDetect;
use Psr\SimpleCache\CacheInterface;
// Use any PSR-16 compatible cache
$redisCache = new YourRedisCacheAdapter();
$detect = new MobileDetect($redisCache);
```
### Custom Cache Key Function
```php
use Detection\MobileDetect;
// Custom cache key with salt
$detect = new MobileDetect(null, [
'cacheKeyFn' => fn($key) => sha1($key . 'my-salt'),
]);
// Or use a different hashing algorithm
$detect = new MobileDetect(null, [
'cacheKeyFn' => fn($key) => md5($key),
]);
```
### Custom Cache TTL
```php
use Detection\MobileDetect;
use DateInterval;
// TTL as integer (seconds)
$detect = new MobileDetect(null, [
'cacheTtl' => 3600, // 1 hour
]);
// TTL as DateInterval
$detect = new MobileDetect(null, [
'cacheTtl' => new DateInterval('PT2H'), // 2 hours
]);
```
## Long-Running Processes
When using MobileDetect in CLI scripts, workers, or daemons that process many different User-Agents, you should periodically clean up expired cache entries to prevent memory growth.
### Worker Example
```php
use Detection\MobileDetect;
use Detection\Cache\Cache;
$detect = new MobileDetect();
$cache = $detect->getCache();
$iterationCount = 0;
while ($userAgent = getNextUserAgentFromQueue()) {
$detect->setUserAgent($userAgent);
$isMobile = $detect->isMobile();
$isTablet = $detect->isTablet();
// Process the result...
processDevice($userAgent, $isMobile, $isTablet);
$iterationCount++;
// Periodically clean up expired cache entries
if ($iterationCount % 1000 === 0 && $cache instanceof Cache) {
$evicted = $cache->evictExpired();
echo "Evicted $evicted expired cache entries\n";
}
}
```
### Batch Processing Example
```php
use Detection\MobileDetect;
use Detection\Cache\Cache;
$detect = new MobileDetect();
// Process a large batch of User-Agents
$userAgents = file('user-agents.txt', FILE_IGNORE_NEW_LINES);
foreach ($userAgents as $index => $ua) {
$detect->setUserAgent($ua);
$results[] = [
'ua' => $ua,
'mobile' => $detect->isMobile(),
'tablet' => $detect->isTablet(),
];
}
// Clean up after batch processing
$cache = $detect->getCache();
if ($cache instanceof Cache) {
$cache->evictExpired();
// Or clear entirely if you're done
$cache->clear();
}
```
## Framework Integration
### Laravel Middleware Example
```php
namespace App\Http\Middleware;
use Closure;
use Detection\MobileDetect;
use Illuminate\Http\Request;
class DetectMobileDevice
{
public function handle(Request $request, Closure $next)
{
$detect = new MobileDetect();
$request->attributes->set('is_mobile', $detect->isMobile());
$request->attributes->set('is_tablet', $detect->isTablet());
return $next($request);
}
}
```
### Symfony Service Example
```php
// config/services.yaml
services:
Detection\MobileDetect:
public: true
```
```php
// In a controller
use Detection\MobileDetect;
class MyController
{
public function index(MobileDetect $detect)
{
if ($detect->isMobile()) {
return $this->render('mobile/index.html.twig');
}
return $this->render('desktop/index.html.twig');
}
}
```
## CloudFront Integration
MobileDetect automatically recognizes Amazon CloudFront headers for device detection.
```php
use Detection\MobileDetect;
// When behind CloudFront with device detection enabled,
// these headers are automatically used:
// - HTTP_CLOUDFRONT_IS_MOBILE_VIEWER
// - HTTP_CLOUDFRONT_IS_TABLET_VIEWER
// - HTTP_CLOUDFRONT_IS_DESKTOP_VIEWER
$detect = new MobileDetect();
// Works automatically when CloudFront headers are present
if ($detect->isMobile()) {
// Mobile device detected via CloudFront
}
```
## Debugging
### Get Matching Information
```php
use Detection\MobileDetect;
$detect = new MobileDetect();
$detect->setUserAgent('Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X)...');
$detect->isMobile();
// Get the regex that matched
$matchingRegex = $detect->getMatchingRegex();
// Get the matches array
$matches = $detect->getMatchesArray();
```
### Access Cache Directly
```php
use Detection\MobileDetect;
$detect = new MobileDetect();
// Get the cache instance
$cache = $detect->getCache();
// Check cached keys (for debugging)
if ($cache instanceof \Detection\Cache\Cache) {
$keys = $cache->getKeys();
print_r($keys);
}
```
================================================
FILE: README.md
================================================

MobileDetect, PHP mobile detection class
========================================





Mobile Detect is a lightweight PHP class for detecting mobile devices (including tablets).
It uses the User-Agent string combined with specific HTTP headers to detect the mobile environment.
## Before you install
There are three versions of MobileDetect.
`4.8.x` is the main version that is ALWAYS going to be updated first.
| Version | Tests | Namespace | Code | PHP Version | Status |
|---------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------|------------------------------------------------------------------|-------------|----------------------|
| 2.8.x | [](https://github.com/serbanghita/Mobile-Detect/actions/workflows/test.yml) | `\Mobile_Detect` | [2.8](https://github.com/serbanghita/Mobile-Detect/tree/2.8.x) | \>=5.0,<7.0 | Deprecated |
| 3.74.x | [](https://github.com/serbanghita/Mobile-Detect/actions/workflows/test.yml) | `Detection\MobileDetect` | [3.74](https://github.com/serbanghita/Mobile-Detect/tree/3.74.x) | \>=7.4,<8.0 | LTS |
| 4.8.x | [](https://github.com/serbanghita/Mobile-Detect/actions/workflows/test.yml) | `Detection\MobileDetect` | [4.8](https://github.com/serbanghita/Mobile-Detect/tree/4.8.x) | \>=8.0 | Current, **Recommended** |
## 🤝 Supporting
If you are using Mobile Detect open-source package in your production apps, in presentation demos, hobby projects,
school projects or so, you can sponsor my work by [donating a small amount :+1:](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=mobiledetectlib%40gmail%2ecom&lc=US&item_name=Mobile%20Detect¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted).
I'm currently paying for domains, hosting and spend a lot of my family time to maintain the project and planning the future
releases. I would highly appreciate any money donations.
Special thanks to:
* the community :+1: for donations, submitting patches and issues
* [Gitbook](https://www.gitbook.com/) team for the open-source license for their technical documentation tool.
## 📃 Documentation
The entire documentation is available on Gitbook: [https://docs.mobiledetect.net](https://docs.mobiledetect.net)
## 👾 Demo
Point your device to:
[https://demo.mobiledetect.net](https://demo.mobiledetect.net)
## 🐛 Testing
``` bash
vendor/bin/phpunit -v -c tests/phpunit.xml --coverage-html .coverage
```
## 🤝 Contributing
Please see the [Contribute guide](https://mobile-detect.gitbook.io/home/contribute) for details.
## 🔒 Security
If you discover any security related issues, please email serbanghita@gmail.com instead of using the issue tracker.
## 🎉 Credits
- [Serban Ghita](https://github.com/serbanghita)
- [All Contributors](https://mobile-detect.gitbook.io/home/credits)
================================================
FILE: SECURITY.md
================================================
# Security Policy
## Supported Versions
| Version | Supported |
|---------| ------------------ |
| 2.8.x | :white_check_mark: |
| 3.74.x | :white_check_mark: |
| 4.8.x | :white_check_mark: |
## Reporting a Vulnerability
Please report all vulnerabilities to Serban Ghita `<serbanghita AT gmail DOT com>`. \
The usual response time is 1 week for lower impact security reports.
Also add an issue with explanation and links in order to keep track of the fix.
Thank you!
================================================
FILE: composer.json
================================================
{
"name": "mobiledetect/mobiledetectlib",
"type": "library",
"description": "Mobile_Detect is a lightweight PHP class for detecting mobile devices. It uses the User-Agent string combined with specific HTTP headers to detect the mobile environment.",
"keywords": ["mobile", "mobile detect", "mobile detector", "php mobile detect", "detect mobile devices"],
"homepage": "https://github.com/serbanghita/Mobile-Detect",
"license": "MIT",
"authors": [
{
"name": "Serban Ghita",
"email": "serbanghita@gmail.com",
"homepage": "http://mobiledetect.net",
"role": "Developer"
}
],
"require": {
"php": ">=8.0",
"psr/simple-cache": "^3"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^v3.75.0",
"phpunit/phpunit": "^9.6.22",
"squizlabs/php_codesniffer": "^3.12.1",
"phpbench/phpbench": "^1.2",
"phpstan/phpstan": "^2.1.11"
},
"autoload": {
"psr-4": {
"Detection\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"DetectionTests\\": "tests/"
}
},
"archive": {
"exclude": ["scripts"]
}
}
================================================
FILE: docker/Dockerfile.setup
================================================
FROM composer:latest AS build
WORKDIR /app
COPY . .
COPY ./docker/build.sh .
SHELL ["/bin/bash", "-c"]
RUN chmod +x build.sh
CMD ["./build.sh"]
================================================
FILE: docker/build.sh
================================================
echo "Start building ..."
rm -rf vendor/*
rm -f composer.lock composer.phar
set -xe
# Install composer with dev dependencies so we can run tests.
# Compose installs by default the dev dependencies.
composer install
================================================
FILE: docker-compose.yml
================================================
services:
setup:
build:
context: .
dockerfile: ./docker/Dockerfile.setup
platform: linux/amd64
volumes:
- ./vendor:/app/vendor
# Example: docker compose -p mobile-detect up --build runUnitTests
runUnitTests:
# Need xdebug from this image to run with coverage
# https://hub.docker.com/r/alcohol/php/tags
image: alcohol/php:8.4-xdebug
platform: linux/amd64
depends_on:
setup:
condition: service_completed_successfully
working_dir: /app
environment:
XDEBUG_MODE: coverage
command: >
/bin/sh -c "vendor/bin/phpunit -v -c tests/phpunit.xml --coverage-html .coverage --strict-coverage --stop-on-risky"
volumes:
- .:/app
runPerfTests:
image: php:8.4-alpine
platform: linux/amd64
depends_on:
setup:
condition: service_completed_successfully
working_dir: /app
command: >
/bin/sh -c "vendor/bin/phpbench run tests/benchmark/MobileDetectBench.php --retry-threshold=1 --iterations=10 --revs=1000 --report=aggregate"
volumes:
- .:/app
runLinting:
image: php:8.4-alpine
platform: linux/amd64
depends_on:
setup:
condition: service_completed_successfully
working_dir: /app
command: >
/bin/sh -c "vendor/bin/phpcs; vendor/bin/phpcbf"
volumes:
- .:/app
runQualityCheck:
image: php:8.4-alpine
platform: linux/amd64
depends_on:
setup:
condition: service_completed_successfully
working_dir: /app
command: >
/bin/sh -c "vendor/bin/phpstan analyse --debug --memory-limit=1G --level 3 src tests"
volumes:
- .:/app
# Pre-release validation gate - runs all checks
# Usage: docker compose -p mobile-detect up --build runAll
runAll:
image: php:8.4-alpine
platform: linux/amd64
depends_on:
runLinting:
condition: service_completed_successfully
runQualityCheck:
condition: service_completed_successfully
runUnitTests:
condition: service_completed_successfully
runPerfTests:
condition: service_completed_successfully
command: >
/bin/sh -c "echo '✅ All pre-release checks passed!'"
generateJsonModel:
image: php:8.4-alpine
platform: linux/amd64
depends_on:
runAll:
condition: service_completed_successfully
working_dir: /app
command: >
/bin/sh -c "php ./scripts/export_to_json.php"
volumes:
- .:/app
================================================
FILE: phpbench.json
================================================
{
"$schema":"./vendor/phpbench/phpbench/phpbench.schema.json",
"runner.bootstrap": "vendor/autoload.php"
}
================================================
FILE: scripts/dump_magic_methods.php
================================================
<?php
declare(strict_types=1);
use Detection\MobileDetect;
require __DIR__ . '/../vendor/autoload.php';
$detect = new MobileDetect();
/**
* Dump all methods (+ extended)
* Use this script to generate comments like "@method bool isiPhone()"
* php export/dump_magic_methods.php > methods.txt
*/
foreach ($detect->getRules() as $name => $regex) {
echo "is$name()\n";
}
================================================
FILE: scripts/example.php
================================================
<?php
/**
* Example using composer's autoloader.
*/
use Detection\MobileDetect;
require_once __DIR__ . '/../vendor/autoload.php';
$detect = new MobileDetect();
// This is optional. We scan for known $_SERVER variables.
// See: https://github.com/serbanghita/Mobile-Detect/issues/948#issuecomment-1800271108
$detect->setUserAgent('Mozilla/5.0 (iPad; CPU OS 14_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) GSA/248.1.504392274 Mobile/15E148 Safari/604.1');
$isMobile = false;
try {
$isMobile = $detect->isMobile();
} catch (\Detection\Exception\MobileDetectException $e) {
}
var_dump($isMobile);
$isTablet = false;
try {
$isTablet = $detect->isTablet();
var_dump($isTablet);
} catch (\Detection\Exception\MobileDetectException $e) {
}
var_dump($isTablet);
================================================
FILE: scripts/export_to_json.php
================================================
<?php
/**
* Mobile Detect Library
* - export -
* =====================
*
* Use the resulting JSON export file in other languages
* other than PHP. Always check for 'version' key because
* new major versions can modify the structure of the JSON file.
*
* The result of running this script is the export.json file.
*
* @license Code and contributions have 'MIT License'
* More details: https://github.com/serbanghita/Mobile-Detect/blob/master/LICENSE.txt
*
*/
declare(strict_types=1);
use Detection\MobileDetect;
require __DIR__ . '/../vendor/autoload.php';
$detect = new MobileDetect();
$json = [
// The current version of Mobile Detect class that
// is being exported.
'version' => $detect->getVersion(),
// All headers that trigger 'isMobile' to be 'true',
// before reaching the User-Agent match detection.
'headerMatch' => $detect->getMobileHeaders(),
// All possible User-Agent headers.
'uaHttpHeaders' => $detect->getUaHttpHeaders(),
'cloudFrontHttpHeaders' => $detect->getCloudFrontHttpHeaders(),
// All the regexes that trigger 'isMobile' or 'isTablet'
// to be true.
'uaMatch' => [
// If match is found, triggers 'isMobile' to be true.
'phones' => $detect->getPhoneDevices(),
// Triggers 'isTablet' to be true.
'tablets' => $detect->getTabletDevices(),
// If match is found, triggers 'isMobile' to be true.
'browsers' => $detect->getBrowsers(),
// If match is found, triggers 'isMobile' to be true.
'os' => $detect->getOperatingSystems()
]
];
$fileName = dirname(__FILE__) . '/../MobileDetect.json';
// Write the JSON file to disk.
// You can import this file in your app.
if (
file_put_contents($fileName, json_encode($json, JSON_PRETTY_PRINT))
) {
echo "Done exporting version ". $detect->getVersion() ." to JSON.\nCheck the output at: " . realpath($fileName);
} else {
echo "Failed to write to disk: " . realpath($fileName) ;
}
================================================
FILE: scripts/pre-commit-hook.sh
================================================
#!/bin/sh
# Linting
vendor/bin/phpcs
vendor/bin/phpcbf
# Unit tests
vendor/bin/phpunit -v -c tests/phpunit.xml --coverage-html .coverage
# Performance tests
vendor/bin/phpbench run tests/Benchmark/MobileDetectBench.php --ref=baseline --retry-threshold=1 --iterations=10 --revs=1000 --report=aggregate
================================================
FILE: scripts/test.php
================================================
<?php
use Detection\Exception\MobileDetectException;
use Detection\MobileDetect;
require __DIR__ . '/../vendor/autoload.php';
$detect = new MobileDetect();
$detect->setUserAgent(' Mozilla/5.0 (Linux; U; Android 4.0.4; en-us; SHV-E160K/VI10.1802 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30');
//$detect->setHttpHeaders(array(
// 'HTTP_X_WAP_PROFILE' => '',
// 'HTTP_USER_AGENT' => 'Mozilla/5.0 (Linux; U; Android 4.1.1; cs-cz; HUAWEI G510-0200 Build/HuaweiG510-0200) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30',
//));
try {
var_dump($detect->isMobile());
var_dump($detect->isMobile());
} catch (MobileDetectException $e) {
var_dump($e);
}
try {
var_dump($detect->isTablet());
} catch (MobileDetectException $e) {
var_dump($e);
}
var_dump($detect->getMatchesArray());
////var_dump($detect->version('IE'));
/*********************************
*
* Dump all methods (+ extended)
*
********************************/
//foreach ($detect->getRules() as $name => $regex) {
// echo "is$name()\n";
//}
================================================
FILE: scripts/test_standalone.php
================================================
<?php
use Detection\Exception\MobileDetectException;
use Detection\MobileDetectStandalone;
require_once '../standalone/autoloader.php';
require_once '../src/MobileDetectStandalone.php';
$detection = new MobileDetectStandalone();
$detection->setUserAgent('iPad');
try {
var_dump($detection);
var_dump($detection->isMobile());
var_dump($detection->isTablet());
} catch (MobileDetectException $e) {
print_r($e);
}
================================================
FILE: src/Cache/Cache.php
================================================
<?php
declare(strict_types=1);
namespace Detection\Cache;
use Psr\SimpleCache\CacheInterface;
use DateInterval;
use DateTime;
use function is_int;
use function time;
/**
* In-memory cache implementation of PSR-16
* @See https://www.php-fig.org/psr/psr-16/
*/
class Cache implements CacheInterface
{
protected array $cache = [];
/**
* @inheritdoc
* @throws CacheInvalidArgumentException
*/
public function get(string $key, mixed $default = null): mixed
{
$this->checkKey($key);
if (isset($this->cache[$key])) {
if ($this->cache[$key]['ttl'] === null || $this->cache[$key]['ttl'] > time()) {
return $this->cache[$key]['content'];
}
$this->deleteSingle($key);
}
return $default;
}
/**
* @inheritdoc
* @throws CacheInvalidArgumentException
*/
public function set(string $key, mixed $value, int|DateInterval|null $ttl = null): bool
{
$this->checkKey($key);
// From https://www.php-fig.org/psr/psr-16/ "Definitions" -> "Expiration"
// If a negative or zero TTL is provided, the item MUST be deleted from the cache if it exists, as it is expired already.
if (is_int($ttl) && $ttl <= 0) {
$this->deleteSingle($key);
return false;
}
$ttl = $this->getTTL($ttl);
if ($ttl !== null) {
$ttl = (time() + $ttl);
}
$this->cache[$key] = ['ttl' => $ttl, 'content' => $value];
return true;
}
/** @inheritdoc */
public function delete(string $key): bool
{
$this->checkKey($key);
$this->deleteSingle($key);
return true;
}
/**
* Deletes the cache item from memory.
*
* @param string $key Cache key
* @return void
*/
private function deleteSingle(string $key): void
{
unset($this->cache[$key]);
}
/** @inheritdoc */
public function clear(): bool
{
$this->cache = [];
return true;
}
/**
* @inheritdoc
* @throws CacheInvalidArgumentException
*/
public function has(string $key): bool
{
$this->checkKey($key);
if (isset($this->cache[$key])) {
if ($this->cache[$key]['ttl'] === null || $this->cache[$key]['ttl'] > time()) {
return true;
}
$this->deleteSingle($key);
}
return false;
}
/** @inheritdoc */
public function getMultiple(iterable $keys, mixed $default = null): iterable
{
$data = [];
foreach ($keys as $key) {
$data[$key] = $this->get($key, $default);
}
return $data;
}
/** @inheritdoc */
public function setMultiple(iterable $values, int|DateInterval|null $ttl = null): bool
{
$return = [];
foreach ($values as $key => $value) {
$return[] = $this->set($key, $value, $ttl);
}
return $this->checkReturn($return);
}
/** @inheritdoc */
public function deleteMultiple(iterable $keys): bool
{
foreach ($keys as $key) {
$this->delete($key);
}
return true;
}
/**
* @throws CacheInvalidArgumentException
*/
protected function checkKey(string $key): string
{
if ($key === '' || !preg_match('/^[A-Za-z0-9_.]{1,64}$/', $key)) {
throw new CacheInvalidArgumentException("Invalid key: '$key'. Must be alphanumeric, can contain _ and . and can be maximum of 64 chars.");
}
return $key;
}
/** */
protected function getTTL(DateInterval|int|null $ttl): ?int
{
if ($ttl instanceof DateInterval) {
return (new DateTime())->add($ttl)->getTimestamp() - time();
}
// We treat 0 as a valid value.
if (is_int($ttl)) {
return $ttl;
}
return null;
}
/**
* @param bool[]|int[] $booleans
*/
protected function checkReturn(array $booleans): bool
{
foreach ($booleans as $boolean) {
if (!$boolean) {
return false;
}
}
return true;
}
/**
* Get all cache keys.
*
* @internal Needed for testing purposes.
* @return array{string}
*/
public function getKeys(): array
{
return array_keys($this->cache);
}
/**
* Evict all expired items from the cache.
*
* Useful for long-running processes (CLI scripts, workers, daemons)
* to periodically clean up expired entries and free memory.
*
* @return int Number of items evicted
*/
public function evictExpired(): int
{
$evicted = 0;
$now = time();
foreach ($this->cache as $key => $item) {
if ($item['ttl'] !== null && $item['ttl'] <= $now) {
unset($this->cache[$key]);
$evicted++;
}
}
return $evicted;
}
}
================================================
FILE: src/Cache/CacheException.php
================================================
<?php
declare(strict_types=1);
namespace Detection\Cache;
class CacheException extends \Exception implements \Psr\SimpleCache\CacheException
{
}
================================================
FILE: src/Cache/CacheInvalidArgumentException.php
================================================
<?php
declare(strict_types=1);
namespace Detection\Cache;
use Psr\SimpleCache\InvalidArgumentException;
class CacheInvalidArgumentException extends CacheException implements InvalidArgumentException
{
}
================================================
FILE: src/Exception/MobileDetectException.php
================================================
<?php
declare(strict_types=1);
namespace Detection\Exception;
class MobileDetectException extends \Exception
{
}
================================================
FILE: src/Exception/MobileDetectExceptionCode.php
================================================
<?php
declare(strict_types=1);
namespace Detection\Exception;
class MobileDetectExceptionCode
{
public const INVALID_USER_AGENT_ERR = 0x1;
public const IS_MOBILE_ERR = 0x2;
public const IS_TABLET_ERR = 0x3;
public const IS_MAGIC_ERR = 0x4;
}
================================================
FILE: src/MobileDetect.php
================================================
<?php
/**
* Mobile Detect Library
* Motto: "Every business should have a mobile detection script to detect mobile readers"
*
* Mobile_Detect is a lightweight PHP class for detecting mobile devices (including tablets).
* It uses the User-Agent string combined with specific HTTP headers to detect the mobile environment.
*
* Homepage: http://mobiledetect.net
* GitHub: https://github.com/serbanghita/Mobile-Detect
* README: https://github.com/serbanghita/Mobile-Detect/blob/master/README.md
* CONTRIBUTING: https://github.com/serbanghita/Mobile-Detect/blob/master/docs/CONTRIBUTING.md
* KNOWN LIMITATIONS: https://github.com/serbanghita/Mobile-Detect/blob/master/docs/KNOWN_LIMITATIONS.md
* EXAMPLES: https://github.com/serbanghita/Mobile-Detect/wiki/Code-examples
*
* @license https://github.com/serbanghita/Mobile-Detect/blob/master/LICENSE
* @author Serban Ghita <serbanghita@gmail.com> (since 2012)
* @author Nick Ilyin <nick.ilyin@gmail.com>
* @author: Victor Stanciu <vic.stanciu@gmail.com> (original author)
*
* @version 4.8.10
*/
declare(strict_types=1);
namespace Detection;
use BadMethodCallException;
use Detection\Cache\Cache;
use Detection\Cache\CacheException;
use Detection\Cache\CacheInvalidArgumentException;
use Detection\Exception\MobileDetectException;
use Detection\Exception\MobileDetectExceptionCode;
use Psr\SimpleCache\CacheInterface;
use Psr\SimpleCache\InvalidArgumentException as PsrInvalidArgumentException;
/**
* Auto-generated isXXXX() magic methods.
* php export/dump_magic_methods.php
*
* @method bool isiPhone()
* @method bool isBlackBerry()
* @method bool isPixel()
* @method bool isHTC()
* @method bool isNexus()
* @method bool isDell()
* @method bool isMotorola()
* @method bool isSamsung()
* @method bool isLG()
* @method bool isSony()
* @method bool isAsus()
* @method bool isXiaomi()
* @method bool isNokiaLumia()
* @method bool isMicromax()
* @method bool isPalm()
* @method bool isVertu()
* @method bool isPantech()
* @method bool isFly()
* @method bool isWiko()
* @method bool isiMobile()
* @method bool isSimValley()
* @method bool isWolfgang()
* @method bool isAlcatel()
* @method bool isNintendo()
* @method bool isAmoi()
* @method bool isINQ()
* @method bool isOnePlus()
* @method bool isGenericPhone()
* @method bool isHuawei()
* @method bool isiPad()
* @method bool isNexusTablet()
* @method bool isGoogleTablet()
* @method bool isSamsungTablet()
* @method bool isKindle()
* @method bool isSurfaceTablet()
* @method bool isHPTablet()
* @method bool isAsusTablet()
* @method bool isBlackBerryTablet()
* @method bool isHTCtablet()
* @method bool isMotorolaTablet()
* @method bool isNookTablet()
* @method bool isAcerTablet()
* @method bool isToshibaTablet()
* @method bool isLGTablet()
* @method bool isFujitsuTablet()
* @method bool isPrestigioTablet()
* @method bool isLenovoTablet()
* @method bool isDellTablet()
* @method bool isYarvikTablet()
* @method bool isMedionTablet()
* @method bool isArnovaTablet()
* @method bool isIntensoTablet()
* @method bool isIRUTablet()
* @method bool isMegafonTablet()
* @method bool isEbodaTablet()
* @method bool isAllViewTablet()
* @method bool isArchosTablet()
* @method bool isAinolTablet()
* @method bool isNokiaLumiaTablet()
* @method bool isSonyTablet()
* @method bool isPhilipsTablet()
* @method bool isCubeTablet()
* @method bool isCobyTablet()
* @method bool isMIDTablet()
* @method bool isMSITablet()
* @method bool isSMiTTablet()
* @method bool isRockChipTablet()
* @method bool isFlyTablet()
* @method bool isbqTablet()
* @method bool isHuaweiTablet()
* @method bool isNecTablet()
* @method bool isPantechTablet()
* @method bool isBronchoTablet()
* @method bool isVersusTablet()
* @method bool isZyncTablet()
* @method bool isPositivoTablet()
* @method bool isNabiTablet()
* @method bool isKoboTablet()
* @method bool isDanewTablet()
* @method bool isTexetTablet()
* @method bool isPlaystationTablet()
* @method bool isTrekstorTablet()
* @method bool isPyleAudioTablet()
* @method bool isAdvanTablet()
* @method bool isDanyTechTablet()
* @method bool isGalapadTablet()
* @method bool isMicromaxTablet()
* @method bool isKarbonnTablet()
* @method bool isAllFineTablet()
* @method bool isPROSCANTablet()
* @method bool isYONESTablet()
* @method bool isChangJiaTablet()
* @method bool isGUTablet()
* @method bool isPointOfViewTablet()
* @method bool isOvermaxTablet()
* @method bool isHCLTablet()
* @method bool isDPSTablet()
* @method bool isVistureTablet()
* @method bool isCrestaTablet()
* @method bool isMediatekTablet()
* @method bool isConcordeTablet()
* @method bool isGoCleverTablet()
* @method bool isModecomTablet()
* @method bool isVoninoTablet()
* @method bool isECSTablet()
* @method bool isStorexTablet()
* @method bool isVodafoneTablet()
* @method bool isEssentielBTablet()
* @method bool isRossMoorTablet()
* @method bool isiMobileTablet()
* @method bool isTolinoTablet()
* @method bool isAudioSonicTablet()
* @method bool isAMPETablet()
* @method bool isSkkTablet()
* @method bool isTecnoTablet()
* @method bool isJXDTablet()
* @method bool isiJoyTablet()
* @method bool isFX2Tablet()
* @method bool isXoroTablet()
* @method bool isViewsonicTablet()
* @method bool isVerizonTablet()
* @method bool isOdysTablet()
* @method bool isCaptivaTablet()
* @method bool isIconbitTablet()
* @method bool isTeclastTablet()
* @method bool isOndaTablet()
* @method bool isJaytechTablet()
* @method bool isBlaupunktTablet()
* @method bool isDigmaTablet()
* @method bool isEvolioTablet()
* @method bool isLavaTablet()
* @method bool isAocTablet()
* @method bool isMpmanTablet()
* @method bool isCelkonTablet()
* @method bool isWolderTablet()
* @method bool isMediacomTablet()
* @method bool isMiTablet()
* @method bool isNibiruTablet()
* @method bool isNexoTablet()
* @method bool isLeaderTablet()
* @method bool isUbislateTablet()
* @method bool isPocketBookTablet()
* @method bool isKocasoTablet()
* @method bool isHisenseTablet()
* @method bool isHudl()
* @method bool isTelstraTablet()
* @method bool isGenericTablet()
* @method bool isAndroidOS()
* @method bool isBlackBerryOS()
* @method bool isPalmOS()
* @method bool isSymbianOS()
* @method bool isWindowsMobileOS()
* @method bool isWindowsPhoneOS()
* @method bool isiOS()
* @method bool isiPadOS()
* @method bool isSailfishOS()
* @method bool isMeeGoOS()
* @method bool isMaemoOS()
* @method bool isJavaOS()
* @method bool iswebOS()
* @method bool isbadaOS()
* @method bool isBREWOS()
* @method bool isHarmonyOS()
* @method bool isChrome()
* @method bool isDolfin()
* @method bool isOpera()
* @method bool isSkyfire()
* @method bool isEdge()
* @method bool isIE()
* @method bool isFirefox()
* @method bool isBolt()
* @method bool isTeaShark()
* @method bool isBlazer()
* @method bool isSafari()
* @method bool isWeChat()
* @method bool isUCBrowser()
* @method bool isbaiduboxapp()
* @method bool isbaidubrowser()
* @method bool isDiigoBrowser()
* @method bool isMercury()
* @method bool isObigoBrowser()
* @method bool isNetFront()
* @method bool isGenericBrowser()
* @method bool isPaleMoon()
* @method bool isHuaweiBrowser()
* @method bool isWebKit()
* @method bool isConsole()
* @method bool isWatch()
*/
class MobileDetect
{
/**
* A cache for resolved matches
* Implementation of PSR-16: Common Interface for Caching Libraries
* https://www.php-fig.org/psr/psr-16/
*
* Replace this with your own implementation.
*/
protected CacheInterface $cache;
/**
* Stores the version number of the current release.
*/
protected string $VERSION = '4.8.10';
protected array $config = [
// Auto-initialization on HTTP headers from $_SERVER['HTTP...']
// Disable this if you're going for performance and set the
// User-Agent via $detect->setUserAgent("...").
// @var boolean
'autoInitOfHttpHeaders' => true,
// Maximum HTTP User-Agent value allowed.
// @var int
'maximumUserAgentLength' => 500,
// Function that creates the cache key. e.g. (base64, sha1, custom fn).
// Note: used to be base64 but we went with sha1 because of fixed length.
'cacheKeyFn' => 'sha1',
// Cache TTL
// @var null|int|\DateInterval
'cacheTtl' => 86400,
];
/**
* A frequently used regular expression to extract version #s.
*/
protected const VERSION_REGEX = '([\w._\+]+)';
/**
* A type for the version() method indicating a string return value.
*/
private const VERSION_TYPE_STRING = 'text';
/**
* A type for the version() method indicating a float return value.
*/
private const VERSION_TYPE_FLOAT = 'float';
/**
* The User-Agent HTTP header is stored in here.
* @var string|null
*/
protected ?string $userAgent = null;
/**
* HTTP headers in the PHP-flavor. So HTTP_USER_AGENT and SERVER_SOFTWARE.
* @var array
*/
protected array $httpHeaders = [];
/**
* CloudFront headers. E.g. CloudFront-Is-Desktop-Viewer, CloudFront-Is-Mobile-Viewer & CloudFront-Is-Tablet-Viewer.
* @var array
*/
protected static array $knownCloudFrontHeaders = [
'HTTP_CLOUDFRONT_IS_MOBILE_VIEWER',
'HTTP_CLOUDFRONT_IS_TABLET_VIEWER',
'HTTP_CLOUDFRONT_IS_DESKTOP_VIEWER'
];
protected static string $cloudFrontUA = 'Amazon CloudFront';
/**
* The matching regex string. Used only for debugging.
* @var string
*/
protected string $matchingRegex = "";
/**
* The matches extracted from the regex expression. Used only for debugging.
* @var array
*/
protected array $matchesArray = [];
/**
* HTTP headers that trigger the 'isMobile' detection to be true.
* @var array
*/
protected static array $knownMobilePositiveHeaders = [
'HTTP_ACCEPT' => [
'matches' => [
// Opera Mini
// @reference: http://dev.opera.com/articles/view/opera-binary-markup-language/
'application/x-obml2d',
// BlackBerry devices.
'application/vnd.rim.html',
'text/vnd.wap.wml',
'application/vnd.wap.xhtml+xml'
]],
'HTTP_X_WAP_PROFILE' => null,
'HTTP_X_WAP_CLIENTID' => null,
'HTTP_WAP_CONNECTION' => null,
'HTTP_PROFILE' => null,
// Reported by Opera on Nokia devices (e.g. C3).
'HTTP_X_OPERAMINI_PHONE_UA' => null,
'HTTP_X_NOKIA_GATEWAY_ID' => null,
'HTTP_X_ORANGE_ID' => null,
'HTTP_X_VODAFONE_3GPDPCONTEXT' => null,
'HTTP_X_HUAWEI_USERID' => null,
// Reported by Windows Smartphones.
'HTTP_UA_OS' => null,
// Reported by Verizon, Vodafone proxy system.
'HTTP_X_MOBILE_GATEWAY' => null,
// Seen this on HTC Sensation. SensationXE_Beats_Z715e.
'HTTP_X_ATT_DEVICEID' => null,
// Seen this on a HTC.
'HTTP_UA_CPU' => ['matches' => ['ARM']],
// See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-CH-UA-Mobile
// "?1" means that the device wants a "mobile" experience.
'Sec-CH-UA-Mobile' => ['matches' => ['?1']],
];
/**
* List of mobile devices (phones).
* @var array
*/
protected static array $phoneDevices = [
'iPhone' => '\biPhone\b|\biPod\b', // |\biTunes
'BlackBerry' => 'BlackBerry|\bBB10\b|rim[0-9]+|\b(BBA100|BBB100|BBD100|BBE100|BBF100|STH100)\b-[0-9]+',
'Pixel' => '; \bPixel\b',
'HTC' => [
'HTC|HTC.*(Sensation|Evo|Vision|Explorer|6800|8100|8900|A7272|S510e|C110e|Legend|Desire|T8282)',
'APX515CKT|Qtek9090|APA9292KT|HD_mini|Sensation.*Z710e|PG86100|Z715e|Desire.*(A8181|HD)|ADR6200',
'ADR6400L|ADR6425|001HT|Inspire 4G|Android.*\bEVO\b|T-Mobile G1|Z520m|Android [0-9.]+; Pixel'
],
'Nexus' => 'Nexus One|Nexus S|Galaxy.*Nexus|Android.*Nexus.*Mobile|Nexus 4|Nexus 5|Nexus 5X|Nexus 6',
// @todo: Is 'Dell Streak' a tablet or a phone? ;)
'Dell' => 'Dell[;]? (Streak|Aero|Venue|Venue Pro|Flash|Smoke|Mini 3iX)|XCD28|XCD35|\b001DL\b|\b101DL\b|\bGS01\b',
'Motorola' => [
'Motorola|DROIDX|DROID BIONIC|\bDroid\b.*Build|Android.*Xoom|HRI39|MOT-|A1260|A1680|A555|A853|A855|A953|A955',
'A956|Motorola.*ELECTRIFY|Motorola.*i1|i867|i940|MB200|MB300|MB501|MB502|MB508|MB511|MB520|MB525|MB526|MB611',
'MB612|MB632|MB810|MB855|MB860|MB861|MB865|MB870|ME501|ME502|ME511|ME525|ME600|ME632|ME722|ME811|ME860|ME863',
'ME865|MT620|MT710|MT716|MT720|MT810|MT870|MT917|Motorola.*TITANIUM|WX435|WX445|XT300|XT301|XT311|XT316|XT317',
'XT319|XT320|XT390|XT502|XT530|XT531|XT532|XT535|XT603|XT610|XT611|XT615|XT681|XT701|XT702|XT711|XT720|XT800',
'XT806|XT860|XT862|XT875|XT882|XT883|XT894|XT901|XT907|XT909|XT910|XT912|XT928|XT926|XT915|XT919|XT925|XT1021|\bMoto E\b|XT1068|XT1092|XT1052',
],
'Samsung' => [
'\bSamsung\b|SM-G950F|SM-G955F|SM-G9250|GT-19300|SGH-I337|BGT-S5230|GT-B2100|GT-B2700|GT-B2710|GT-B3210|GT-B3310',
'SM-F946B|SM-A127F',
'SM-S908E|SM-G955N|SM-S918U1|SM-G998B|SM-G970N|SM-G973U|SM-S901U|SM-A515F|SM-S901E|SM-G980F|SM-S901B',
'GT-B3410|GT-B3730|GT-B3740|GT-B5510|GT-B5512|GT-B5722|GT-B6520|GT-B7300|GT-B7320|GT-B7330|GT-B7350|GT-B7510',
'GT-B7722|GT-B7800|GT-C3010|GT-C3011|GT-C3060|GT-C3200|GT-C3212|GT-C3212I|GT-C3262|GT-C3222|GT-C3300|GT-C3300K',
'GT-C3303|GT-C3303K|GT-C3310|GT-C3322|GT-C3330|GT-C3350|GT-C3500|GT-C3510|GT-C3530|GT-C3630|GT-C3780|GT-C5010',
'GT-C5212|GT-C6620|GT-C6625|GT-C6712|GT-E1050|GT-E1070|GT-E1075|GT-E1080|GT-E1081|GT-E1085|GT-E1087|GT-E1100',
'GT-E1107|GT-E1110|GT-E1120|GT-E1125|GT-E1130|GT-E1160|GT-E1170|GT-E1175|GT-E1180|GT-E1182|GT-E1200|GT-E1210',
'GT-E1225|GT-E1230|GT-E1390|GT-E2100|GT-E2120|GT-E2121|GT-E2152|GT-E2220|GT-E2222|GT-E2230|GT-E2232|GT-E2250',
'GT-E2370|GT-E2550|GT-E2652|GT-E3210|GT-E3213|GT-I5500|GT-I5503|GT-I5700|GT-I5800|GT-I5801|GT-I6410|GT-I6420',
'GT-I7110|GT-I7410|GT-I7500|GT-I8000|GT-I8150|GT-I8160|GT-I8190|GT-I8320|GT-I8330|GT-I8350|GT-I8530|GT-I8700',
'GT-I8703|GT-I8910|GT-I9000|GT-I9001|GT-I9003|GT-I9010|GT-I9020|GT-I9023|GT-I9070|GT-I9082|GT-I9100|GT-I9103',
'GT-I9220|GT-I9250|GT-I9300|GT-I9305|GT-I9500|GT-I9505|GT-M3510|GT-M5650|GT-M7500|GT-M7600|GT-M7603|GT-M8800',
'GT-M8910|GT-N7000|GT-S3110|GT-S3310|GT-S3350|GT-S3353|GT-S3370|GT-S3650|GT-S3653|GT-S3770|GT-S3850|GT-S5210',
'GT-S5220|GT-S5229|GT-S5230|GT-S5233|GT-S5250|GT-S5253|GT-S5260|GT-S5263|GT-S5270|GT-S5300|GT-S5330|GT-S5350',
'GT-S5360|GT-S5363|GT-S5369|GT-S5380|GT-S5380D|GT-S5560|GT-S5570|GT-S5600|GT-S5603|GT-S5610|GT-S5620|GT-S5660',
'GT-S5670|GT-S5690|GT-S5750|GT-S5780|GT-S5830|GT-S5839|GT-S6102|GT-S6500|GT-S7070|GT-S7200|GT-S7220|GT-S7230',
'GT-S7233|GT-S7250|GT-S7500|GT-S7530|GT-S7550|GT-S7562|GT-S7710|GT-S8000|GT-S8003|GT-S8500|GT-S8530|GT-S8600',
'SCH-A310|SCH-A530|SCH-A570|SCH-A610|SCH-A630|SCH-A650|SCH-A790|SCH-A795|SCH-A850|SCH-A870|SCH-A890|SCH-A930',
'SCH-A950|SCH-A970|SCH-A990|SCH-I100|SCH-I110|SCH-I400|SCH-I405|SCH-I500|SCH-I510|SCH-I515|SCH-I600|SCH-I730',
'SCH-I760|SCH-I770|SCH-I830|SCH-I910|SCH-I920|SCH-I959|SCH-LC11|SCH-N150|SCH-N300|SCH-R100|SCH-R300|SCH-R351',
'SCH-R400|SCH-R410|SCH-T300|SCH-U310|SCH-U320|SCH-U350|SCH-U360|SCH-U365|SCH-U370|SCH-U380|SCH-U410|SCH-U430',
'SCH-U450|SCH-U460|SCH-U470|SCH-U490|SCH-U540|SCH-U550|SCH-U620|SCH-U640|SCH-U650|SCH-U660|SCH-U700|SCH-U740',
'SCH-U750|SCH-U810|SCH-U820|SCH-U900|SCH-U940|SCH-U960|SCS-26UC|SGH-A107|SGH-A117|SGH-A127|SGH-A137|SGH-A157',
'SGH-A167|SGH-A177|SGH-A187|SGH-A197|SGH-A227|SGH-A237|SGH-A257|SGH-A437|SGH-A517|SGH-A597|SGH-A637|SGH-A657',
'SGH-A667|SGH-A687|SGH-A697|SGH-A707|SGH-A717|SGH-A727|SGH-A737|SGH-A747|SGH-A767|SGH-A777|SGH-A797|SGH-A817',
'SGH-A827|SGH-A837|SGH-A847|SGH-A867|SGH-A877|SGH-A887|SGH-A897|SGH-A927|SGH-B100|SGH-B130|SGH-B200|SGH-B220',
'SGH-C100|SGH-C110|SGH-C120|SGH-C130|SGH-C140|SGH-C160|SGH-C170|SGH-C180|SGH-C200|SGH-C207|SGH-C210|SGH-C225',
'SGH-C230|SGH-C417|SGH-C450|SGH-D307|SGH-D347|SGH-D357|SGH-D407|SGH-D415|SGH-D780|SGH-D807|SGH-D980|SGH-E105',
'SGH-E200|SGH-E315|SGH-E316|SGH-E317|SGH-E335|SGH-E590|SGH-E635|SGH-E715|SGH-E890|SGH-F300|SGH-F480|SGH-I200',
'SGH-I300|SGH-I320|SGH-I550|SGH-I577|SGH-I600|SGH-I607|SGH-I617|SGH-I627|SGH-I637|SGH-I677|SGH-I700|SGH-I717',
'SGH-I727|SGH-i747M|SGH-I777|SGH-I780|SGH-I827|SGH-I847|SGH-I857|SGH-I896|SGH-I897|SGH-I900|SGH-I907|SGH-I917',
'SGH-I927|SGH-I937|SGH-I997|SGH-J150|SGH-J200|SGH-L170|SGH-L700|SGH-M110|SGH-M150|SGH-M200|SGH-N105|SGH-N500',
'SGH-N600|SGH-N620|SGH-N625|SGH-N700|SGH-N710|SGH-P107|SGH-P207|SGH-P300|SGH-P310|SGH-P520|SGH-P735|SGH-P777',
'SGH-Q105|SGH-R210|SGH-R220|SGH-R225|SGH-S105|SGH-S307|SGH-T109|SGH-T119|SGH-T139|SGH-T209|SGH-T219|SGH-T229',
'SGH-T239|SGH-T249|SGH-T259|SGH-T309|SGH-T319|SGH-T329|SGH-T339|SGH-T349|SGH-T359|SGH-T369|SGH-T379|SGH-T409',
'SGH-T429|SGH-T439|SGH-T459|SGH-T469|SGH-T479|SGH-T499|SGH-T509|SGH-T519|SGH-T539|SGH-T559|SGH-T589|SGH-T609',
'SGH-T619|SGH-T629|SGH-T639|SGH-T659|SGH-T669|SGH-T679|SGH-T709|SGH-T719|SGH-T729|SGH-T739|SGH-T746|SGH-T749',
'SGH-T759|SGH-T769|SGH-T809|SGH-T819|SGH-T839|SGH-T919|SGH-T929|SGH-T939|SGH-T959|SGH-T989|SGH-U100|SGH-U200',
'SGH-U800|SGH-V205|SGH-V206|SGH-X100|SGH-X105|SGH-X120|SGH-X140|SGH-X426|SGH-X427|SGH-X475|SGH-X495|SGH-X497',
'SGH-X507|SGH-X600|SGH-X610|SGH-X620|SGH-X630|SGH-X700|SGH-X820|SGH-X890|SGH-Z130|SGH-Z150|SGH-Z170|SGH-ZX10',
'SGH-ZX20|SHW-M110|SPH-A120|SPH-A400|SPH-A420|SPH-A460|SPH-A500|SPH-A560|SPH-A600|SPH-A620|SPH-A660|SPH-A700',
'SPH-A740|SPH-A760|SPH-A790|SPH-A800|SPH-A820|SPH-A840|SPH-A880|SPH-A900|SPH-A940|SPH-A960|SPH-D600|SPH-D700',
'SPH-D710|SPH-D720|SPH-I300|SPH-I325|SPH-I330|SPH-I350|SPH-I500|SPH-I600|SPH-I700|SPH-L700|SPH-M100|SPH-M220',
'SPH-M240|SPH-M300|SPH-M305|SPH-M320|SPH-M330|SPH-M350|SPH-M360|SPH-M370|SPH-M380|SPH-M510|SPH-M540|SPH-M550',
'SPH-M560|SPH-M570|SPH-M580|SPH-M610|SPH-M620|SPH-M630|SPH-M800|SPH-M810|SPH-M850|SPH-M900|SPH-M910|SPH-M920',
'SPH-M930|SPH-N100|SPH-N200|SPH-N240|SPH-N300|SPH-N400|SPH-Z400|SWC-E100|SCH-i909|GT-N7100|GT-N7105|SCH-I535',
'SM-N900A|SGH-I317|SGH-T999L|GT-S5360B|GT-I8262|GT-S6802|GT-S6312|GT-S6310|GT-S5312|GT-S5310|GT-I9105|GT-I8510',
'GT-S6790N|SM-G7105|SM-N9005|GT-S5301|GT-I9295|GT-I9195|SM-C101|GT-S7392|GT-S7560|GT-B7610|GT-I5510|GT-S7582',
'GT-S7530E|GT-I8750|SM-G9006V|SM-G9008V|SM-G9009D|SM-G900A|SM-G900D|SM-G900F|SM-G900H|SM-G900I|SM-G900J|SM-G900K',
'SM-G900L|SM-G900M|SM-G900P|SM-G900R4|SM-G900S|SM-G900T|SM-G900V|SM-G900W8|SHV-E160K|SCH-P709|SCH-P729|SM-T2558',
'GT-I9205|SM-G9350|SM-J120F|SM-G920F|SM-G920V|SM-G930F|SM-N910C|SM-A310F|GT-I9190|SM-J500FN|SM-G903F|SM-J330F',
'SM-G610F|SM-G981B|SM-G892A|SM-A530F|SM-G988N|SM-G781B|SM-A805N|SM-G965F',
],
'LG' => [
'\bLG\b;|LG[- ]?(C800|C900|E400|E610|E900|E-900|F160|F180K|F180L|F180S|730|855|L160|LS740|LS840|LS970|LU6200)',
'LG[- ]?(MS690|MS695|MS770|MS840|MS870|MS910|P500|P700|P705|VM696|AS680|AS695|AX840|C729|E970|GS505|272|C395|E739BK)',
'LG[- ]?(E960|L55C|L75C|LS696|LS860|P769BK|P350|P500|P509|P870|UN272|US730|VS840|VS950|LN272|LN510|LS670|LS855|LW690)',
'LG[- ]?(MN270|MN510|P509|P769|P930|UN200|UN270|UN510|UN610|US670|US740|US760|UX265|UX840|VN271|VN530|VS660|VS700|VS740)',
'LG[- ]?(VS750|VS910|VS920|VS930|VX9200|VX11000|AX840A|LW770|P506|P925|P999|E612|D955|D802|MS323|M257)|LM-G710',
],
'Sony' => [
'SonyST|SonyLT|SonyEricsson|SonyEricssonLT15iv|LT18i|E10i|LT28h|LT26w|SonyEricssonMT27i',
'C5303|C6902|C6903|C6906|C6943|D2533|SOV34|601SO|F8332',
],
'Asus' => 'Asus.*Galaxy|PadFone.*Mobile|ASUS_Z01QD|ASUS_X00TD',
'Xiaomi' => [
'^(?!.*\bx11\b).*xiaomi.*$|POCOPHONE F1|\bMI\b 8|\bMi\b 10|Redmi Note 9S|Redmi 5A|Redmi Note 5A Prime|Redmi Note 7 Pro',
'N2G47H|M2001J2G|M2001J2I|M1805E10A|M2004J11G|M1902F1G|M2002J9G|M2004J19G|M2003J6A1G|M2012K11C|M2007J1SC',
],
'NokiaLumia' => 'Lumia [0-9]{3,4}',
// http://www.micromaxinfo.com/mobiles/smartphones
// Added because the codes might conflict with Acer Tablets.
'Micromax' => 'Micromax.*\b(A210|A92|A88|A72|A111|A110Q|A115|A116|A110|A90S|A26|A51|A35|A54|A25|A27|A89|A68|A65|A57|A90)\b',
// @todo Complete the regex.
'Palm' => 'PalmSource|Palm', // avantgo|blazer|elaine|hiptop|plucker|xiino ;
// Just for fun ;)
'Vertu' => 'Vertu|Vertu.*Ltd|Vertu.*Ascent|Vertu.*Ayxta|Vertu.*Constellation(F|Quest)?|Vertu.*Monika|Vertu.*Signature',
// http://www.pantech.co.kr/en/prod/prodList.do?gbrand=VEGA (PANTECH)
// Most of the VEGA devices are legacy. PANTECH seem to be newer devices based on Android.
'Pantech' => [
'PANTECH|IM-A850S|IM-A840S|IM-A830L|IM-A830K|IM-A830S|IM-A820L|IM-A810K|IM-A810S|IM-A800S|IM-T100K|IM-A725L',
'IM-A780L|IM-A775C|IM-A770K|IM-A760S|IM-A750K|IM-A740S|IM-A730S|IM-A720L|IM-A710K|IM-A690L|IM-A690S|IM-A650S',
'IM-A630K|IM-A600S|VEGA PTL21|PT003|P8010|ADR910L|P6030|P6020|P9070|P4100|P9060|P5000|CDM8992|TXT8045|ADR8995',
'IS11PT|P2030|P6010|P8000|PT002|IS06|CDM8999|P9050|PT001|TXT8040|P2020|P9020|P2000|P7040|P7000|C790',
],
// http://www.fly-phone.com/devices/smartphones/ ; Included only smartphones.
'Fly' => 'IQ230|IQ444|IQ450|IQ440|IQ442|IQ441|IQ245|IQ256|IQ236|IQ255|IQ235|IQ245|IQ275|IQ240|IQ285|IQ280|IQ270|IQ260|IQ250',
// http://fr.wikomobile.com
'Wiko' => [
'KITE 4G|HIGHWAY|GETAWAY|STAIRWAY|DARKSIDE|DARKFULL|DARKNIGHT|DARKMOON|SLIDE|WAX 4G|RAINBOW|BLOOM|SUNSET|GOA(?!nna)|LENNY',
'BARRY|IGGY|OZZY|CINK FIVE|CINK PEAX|CINK PEAX 2|CINK SLIM|CINK SLIM 2|CINK +|CINK KING|CINK PEAX|CINK SLIM|SUBLIM'
],
'iMobile' => 'i-mobile (IQ|i-STYLE|idea|ZAA|Hitz)',
// Added simvalley mobile just for fun. They have some interesting devices.
// http://www.simvalley.fr/telephonie---gps-_22_telephonie-mobile_telephones_.html
'SimValley' => '\b(SP-80|XT-930|SX-340|XT-930|SX-310|SP-360|SP60|SPT-800|SP-120|SPT-800|SP-140|SPX-5|SPX-8|SP-100|SPX-8|SPX-12)\b',
// Wolfgang - a brand that is sold by Aldi supermarkets.
// http://www.wolfgangmobile.com/
'Wolfgang' => 'AT-B24D|AT-AS50HD|AT-AS40W|AT-AS55HD|AT-AS45q2|AT-B26D|AT-AS50Q',
'Alcatel' => 'Alcatel',
'Nintendo' => 'Nintendo (3DS|Switch)',
// http://en.wikipedia.org/wiki/Amoi
'Amoi' => 'Amoi',
// http://en.wikipedia.org/wiki/INQ
'INQ' => 'INQ',
'OnePlus' => 'ONEPLUS|CPH2663',
// @Tapatalk is a mobile app; http://support.tapatalk.com/threads/smf-2-0-2-os-and-browser-detection-plugin-and-tapatalk.15565/#post-79039
'GenericPhone' => 'Tapatalk|PDA;|SAGEM|\bmmp\b|pocket|\bpsp\b|symbian|Smartphone|smartfon|treo|up.browser|up.link|vodafone|\bwap\b|nokia|Series40|Series60|S60|SonyEricsson|N900|MAUI.*WAP.*Browser',
'Huawei' => 'HMSCore|Huawei',
];
/**
* List of tablet devices.
* @var array
*/
protected static array $tabletDevices = [
// @todo: check for mobile friendly emails topic.
'iPad' => 'iPad|iPad.*Mobile',
// Removed |^.*Android.*Nexus(?!(?:Mobile).)*$
// @see #442
// @todo Merge NexusTablet into GoogleTablet.
'NexusTablet' => 'Android.*Nexus[\s]+(7|9|10)',
// https://en.wikipedia.org/wiki/Pixel_C
'GoogleTablet' => 'Android.*Pixel C',
'SamsungTablet' => [
'SM-X926B|SM-X620|SM-X526B|SM-X520|SM-X626B|SM-X920|SM-X820|SM-X826B|SM-P625|SM-P620|SM-X306B|SM-T730|SM-T976B|SM-T875|SM-T575|SM-T545',
'SM-X210R|SM-X216R|SM-X356B|SM-T860X|SM-T636B|SM-T509|SM-T503|SM-T720X|SM-T570|SM-T540|SM-T510X|SM-T830X|SM-T820X|SM-T710X|SM-T810X|SM-T365|SM-T550X|SM-T116',
'SM-X616B|SM-X610|SM-X516B|SM-X910|SM-X916B|SM-X816B|SM-X810|SM-X710|SM-X716B|SM-X510|SM-P619|SM-T225|SM-T225N|SM-T736B|SM-T505|SM-T733|SM-X205|SM-X210|SM-X216B',
'SM-X700|SM-X706|SM-X706B|SM-X706U|SM-X706N|SM-X800|SM-X806|SM-X806B|SM-X806U|SM-X806N|SM-X900|SM-X906|SM-X906B|SM-X906U|SM-X906N|SM-P613|SM-X110|SM-X115',
'SM-T970|SM-T380|SM-T5950|SM-T905|SM-T231|SM-T500|SM-T860|SM-T536|SM-T837A|SM-X200|SM-T220|SM-T870|SM-X906C', // SCH-P709|SCH-P729|SM-T2558|GT-I9205 - Samsung Mega - treat them like a regular phone.
'SM-T815Y|SM-T585|SM-T285|SM-T825|SM-W708|SM-T835|SM-T830|SM-T837V|SM-T720|SM-T510|SM-T387V|SM-P610|SM-T290|SM-T515|SM-T590|SM-T595|SM-T725|SM-T817P|SM-P585N0|SM-T395|SM-T295|SM-T865|SM-P610N|SM-P615',
'SM-T560|SM-T670|SM-T677|SM-T377|SM-T567|SM-T357T|SM-T555|SM-T561|SM-T713|SM-T719|SM-T813|SM-T819|SM-T580|SM-T355Y?|SM-T280|SM-T817A|SM-T820|SM-W700|SM-P580|SM-T587|SM-P350|SM-P555M|SM-P355M|SM-T113NU',
'SM-T807P|SM-P607T|SM-T217T|SM-T337T|SM-T807T|SM-T116NQ|SM-T116BU|SM-P550|SM-T350|SM-T550|SM-T9000|SM-P9000|SM-T705Y|SM-T805|GT-P3113|SM-T710|SM-T810|SM-T815|SM-T360|SM-T533|SM-T113|SM-T335|SM-T715',
'SM-P900X|SM-T210X|SM-T230|SM-T230X|SM-T325|GT-P7503|SM-T531|SM-T330|SM-T530|SM-T705|SM-T705C|SM-T535|SM-T331|SM-T800|SM-T700|SM-T537|SM-T807|SM-P907A|SM-T337A|SM-T537A|SM-T707A|SM-T807A|SM-T237',
'GT-N5120|SM-P905|SM-T111|SM-T2105|SM-T315|SM-T320|SM-T320X|SM-T321|SM-T520|SM-T525|SM-T530NU|SM-T230NU|SM-T330NU|SM-T900|XE500T1C|SM-P605V|SM-P905V|SM-T337V|SM-T537V|SM-T707V|SM-T807V|SM-P600X',
'GT-P5210X|SM-T311|SM-T310|SM-T310X|SM-T210|SM-T210R|SM-T211|SM-P600|SM-P601|SM-P605|SM-P900|SM-P901|SM-T217|SM-T217A|SM-T217S|SM-P6000|SM-T3100|SGH-I467|XE500|SM-T110|GT-P5220|GT-I9200X|GT-N5110X',
'SHW-M180S|SHW-M180W|SHW-M300W|SHW-M305W|SHW-M380K|SHW-M380S|SHW-M380W|SHW-M430W|SHW-M480K|SHW-M480S|SHW-M480W|SHW-M485W|SHW-M486W|SHW-M500W|GT-I9228|SCH-P739|SCH-I925|GT-I9200|GT-P5200|GT-P5210',
'GT-P5113|GT-P8110|GT-N8010|GT-N8005|GT-N8020|GT-P1013|GT-P6201|GT-P7501|GT-N5100|GT-N5105|GT-N5110|SHV-E140K|SHV-E140L|SHV-E140S|SHV-E150S|SHV-E230K|SHV-E230L|SHV-E230S|SHW-M180K|SHW-M180L',
'SGH-T849|SGH-T859|SGH-T869|SPH-P100|GT-P3100|GT-P3108|GT-P3110|GT-P5100|GT-P5110|GT-P6200|GT-P7320|GT-P7511|GT-N8000|GT-P8510|SGH-I497|SPH-P500|SGH-T779|SCH-I705|SCH-I915|GT-N8013|GT-P3113',
'SAMSUNG.*Tablet|Galaxy.*Tab|SC-01C|GT-P1000|GT-P1003|GT-P1010|GT-P3105|GT-P6210|GT-P6800|GT-P6810|GT-P7100|GT-P7300|GT-P7310|GT-P7500|GT-P7510|SCH-I800|SCH-I815|SCH-I905|SGH-I957|SGH-I987|SM-X300|SM-T630',
],
// http://docs.aws.amazon.com/silk/latest/developerguide/user-agent.html
'Kindle' => 'Kindle|Silk.*Accelerated|Android.*\b(KFOT|KFTT|KFJWI|KFJWA|KFOTE|KFSOWI|KFTHWI|KFTHWA|KFAPWI|KFAPWA|WFJWAE|KFSAWA|KFSAWI|KFASWI|KFARWI|KFFOWI|KFGIWI|KFMEWI)\b|Android.*Silk/[0-9.]+ like Chrome/[0-9.]+ (?!Mobile)',
// Only the Surface tablets with Windows RT are considered mobile.
// http://msdn.microsoft.com/en-us/library/ie/hh920767(v=vs.85).aspx
'SurfaceTablet' => 'Windows NT [0-9.]+; ARM;.*(Tablet|ARMBJS)',
// http://shopping1.hp.com/is-bin/INTERSHOP.enfinity/WFS/WW-USSMBPublicStore-Site/en_US/-/USD/ViewStandardCatalog-Browse?CatalogCategoryID=JfIQ7EN5lqMAAAEyDcJUDwMT
'HPTablet' => 'HP Slate (7|8|10)|HP ElitePad 900|hp-tablet|EliteBook.*Touch|HP 8|Slate 21|HP SlateBook 10',
// Watch out for PadFone, see #132.
// http://www.asus.com/de/Tablets_Mobile/Memo_Pad_Products/
'AsusTablet' => [
'ME181C|P01Y|PO1MA|P01Z|\bP027\b|\bP024\b|\bP00C\b',
'\bK00C\b|\bK00E\b|\bK00L\b|TX201LA|ME176C|ME102A|\bM80TA\b|ME372CL|ME560CG|ME372CG|ME302KL| K01A | K010 | K011 | K017 | K01E |ME572C|ME103K|ME170C|ME171C|\bME70C\b|ME581C|ME581CL|ME8510C',
'^.*PadFone((?!Mobile).)*$|Transformer|TF101|TF101G|TF300T|TF300TG|TF300TL|TF700T|TF700KL|TF701T|TF810C|ME171|ME301T|ME302C|ME371MG|ME370T|ME372MG|ME172V|ME173X|ME400C|Slider SL101|\bK00F\b',
],
'BlackBerryTablet' => 'PlayBook|RIM Tablet',
'HTCtablet' => 'HTC_Flyer_P512|HTC Flyer|HTC Jetstream|HTC-P715a|HTC EVO View 4G|PG41200|PG09410',
'MotorolaTablet' => 'xoom|sholest|MZ615|MZ605|MZ505|MZ601|MZ602|MZ603|MZ604|MZ606|MZ607|MZ608|MZ609|MZ615|MZ616|MZ617',
'NookTablet' => 'Android.*Nook|NookColor|nook browser|BNRV200|BNRV200A|BNTV250|BNTV250A|BNTV400|BNTV600|LogicPD Zoom2',
// http://www.acer.ro/ac/ro/RO/content/drivers
// http://www.packardbell.co.uk/pb/en/GB/content/download (Packard Bell is part of Acer)
// http://us.acer.com/ac/en/US/content/group/tablets
// http://www.acer.de/ac/de/DE/content/models/tablets/
// Can conflict with Micromax and Motorola phones codes.
'AcerTablet' => [
'Android.*; \b(A100|A101|A110|A200|A210|A211|A500|A501|A510|A511|A700|A701|W500|W500P|W501|W501P|W510|W511|W700|G100|G100W|B1-A71|B1-710|B1-711|A1-810|A1-811|A1-830)\b',
'W3-810|\bA3-A10\b|\bA3-A11\b|\bA3-A20\b|\bA3-A30|A3-A40'
],
// http://eu.computers.toshiba-europe.com/innovation/family/Tablets/1098744/banner_id/tablet_footerlink/
// http://us.toshiba.com/tablets/tablet-finder
// http://www.toshiba.co.jp/regza/tablet/
'ToshibaTablet' => 'Android.*(AT100|AT105|AT200|AT205|AT270|AT275|AT300|AT305|AT1S5|AT500|AT570|AT700|AT830)|TOSHIBA.*FOLIO',
// http://www.nttdocomo.co.jp/english/service/developer/smart_phone/technical_info/spec/index.html
// http://www.lg.com/us/tablets
'LGTablet' => '\bL-06C|LG-V909|LG-V900|LG-V700|LG-V510|LG-V500|LG-V410|LG-V400|LG-VK810\b',
'FujitsuTablet' => 'Android.*\b(F-01D|F-02F|F-05E|F-10D|M532|Q572)\b',
// Prestigio Tablets http://www.prestigio.com/support
'PrestigioTablet' => [
'PMP3170B|PMP3270B|PMP3470B|PMP7170B|PMP3370B|PMP3570C|PMP5870C|PMP3670B|PMP5570C|PMP5770D|PMP3970B|PMP3870C|PMP5580C|PMP5880D|PMP5780D|PMP5588C|PMP7280C',
'PMP7280C3G|PMP7280|PMP7880D|PMP5597D|PMP5597|PMP7100D|PER3464|PER3274|PER3574|PER3884|PER5274|PER5474|PMP5097CPRO|PMP5097|PMP7380D|PMP5297C|PMP5297C_QUAD',
'PMP812E|PMP812E3G|PMP812F|PMP810E|PMP880TD|PMT3017|PMT3037|PMT3047|PMT3057|PMT7008|PMT5887|PMT5001|PMT5002',
],
// http://support.lenovo.com/en_GB/downloads/default.page?#
'LenovoTablet' => [
'TB-X704L|TB-J606F|TB-X606F|TB-X306X|YT-J706X|TB128FU',
'YT3-X50M|YT-X705F|YT-X703F|YT-X703L|YT-X705L|YT-X705X|TB2-X30F|TB2-X30L|TB2-X30M|A2107A-F|A2107A-H|TB3-730F|TB3-730M|TB3-730X|TB-7504F|TB-7504X|TB-X704F|TB-X104F|TB3-X70F|TB-X705F|TB-8504F|TB3-X70L|TB3-710F',
'TB-X103F|TB-X304X|TB-X304F|TB-X304L|TB-X505F|TB-X505L|TB-X505X|TB-X605F|TB-X605L|TB-8703F|TB-8703X|TB-8703N|TB-8704N|TB-8704F|TB-8704X|TB-8704V|TB-7304F|TB-7304I|TB-7304X|Tab2A7-10F|Tab2A7-20F|TB2-X30L|YT3-X50L|YT3-X50F',
'Lenovo TAB|Idea(Tab|Pad)( A1|A10| K1|)|ThinkPad([ ]+)?Tablet|YT3-850M|YT3-X90L|YT3-X90F|YT3-X90X|Lenovo.*(S2109|S2110|S5000|S6000|K3011|A3000|A3500|A1000|A2107|A2109|A1107|A5500|A7600|B6000|B8000|B8080)(-|)(FL|F|HV|H|)',
],
// http://www.dell.com/support/home/us/en/04/Products/tab_mob/tablets
'DellTablet' => 'Venue 11|Venue 8|Venue 7|Dell Streak 10|Dell Streak 7',
'XiaomiTablet' => '21051182G',
// http://www.yarvik.com/en/matrix/tablets/
'YarvikTablet' => [
'Android.*\b(TAB10-400|TAB10-410|TAB13-201|TAB274EUK|TAB275EUK|TAB374EUK|TAB462EUK|TAB474EUK|TAB9-200)\b',
'Android.*\b(TAB07-200|TAB07-201-3G|TAB07-210|TAB07-211|TAB07-212|TAB07-214|TAB07-220|TAB07-400|TAB07-485|TAB08-150|TAB08-200|TAB08-201-3G|TAB08-201-30|TAB09-100|TAB09-211|TAB09-410|TAB10-150|TAB10-201|TAB10-211)\b',
'Android.*\b(TAB210|TAB211|TAB224|TAB250|TAB260|TAB264|TAB310|TAB360|TAB364|TAB410|TAB411|TAB420|TAB424|TAB450|TAB460|TAB461|TAB464|TAB465|TAB467|TAB468|TAB07-100|TAB07-101|TAB07-150|TAB07-151|TAB07-152)\b',
],
'MedionTablet' => 'Android.*\bOYO\b|LIFE.*(P9212|P9514|P9516|S9512)|LIFETAB',
'ArnovaTablet' => '97G4|AN10G2|AN7bG3|AN7fG3|AN8G3|AN8cG3|AN7G3|AN9G3|AN7dG3|AN7dG3ST|AN7dG3ChildPad|AN10bG3|AN10bG3DT|AN9G2',
// http://www.intenso.de/kategorie_en.php?kategorie=33
// @todo: http://www.nbhkdz.com/read/b8e64202f92a2df129126bff.html - investigate
'IntensoTablet' => 'INM8002KP|INM1010FP|INM805ND|Intenso Tab|TAB1004',
// IRU.ru Tablets http://www.iru.ru/catalog/soho/planetable/
'IRUTablet' => 'M702pro',
'MegafonTablet' => 'MegaFon V9|\bZTE V9\b|Android.*\bMT7A\b',
// http://www.e-boda.ro/tablete-pc.html
'EbodaTablet' => 'E-Boda (Supreme|Impresspeed|Izzycomm|Essential)',
// http://www.allview.ro/produse/droseries/lista-tablete-pc/
'AllViewTablet' => 'Allview.*(Viva|Alldro|City|Speed|All TV|Frenzy|Quasar|Shine|TX1|AX1|AX2)',
// http://wiki.archosfans.com/index.php?title=Main_Page
// @note Rewrite the regex format after we add more UAs.
'ArchosTablet' => '\b(101G9|80G9|A101IT)\b|Qilive 97R|Archos5|\bARCHOS (70|79|80|90|97|101|FAMILYPAD|)(b|c|)(G10| Cobalt| TITANIUM(HD|)| Xenon| Neon|XSK| 2| XS 2| PLATINUM| CARBON|GAMEPAD)\b',
// http://www.ainol.com/plugin.php?identifier=ainol&module=product
'AinolTablet' => 'NOVO7|NOVO8|NOVO10|Novo7Aurora|Novo7Basic|NOVO7PALADIN|novo9-Spark',
'NokiaLumiaTablet' => 'Lumia 2520',
// @todo: inspect http://esupport.sony.com/US/p/select-system.pl?DIRECTOR=DRIVER
// Readers http://www.atsuhiro-me.net/ebook/sony-reader/sony-reader-web-browser
// http://www.sony.jp/support/tablet/
'SonyTablet' => [
'EBRD1101|EBRD1102|EBRD1201|SGP351|SGP341|SGP511|SGP512|SGP521|SGP541|SGP551|SGP621|SGP641|SGP612|SOT31|SGP771|SGP611|SGP612|SGP712',
'Sony.*Tablet|Xperia Tablet|Sony Tablet S|SO-03E|SGPT12|SGPT13|SGPT114|SGPT121|SGPT122|SGPT123|SGPT111|SGPT112|SGPT113|SGPT131|SGPT132|SGPT133|SGPT211|SGPT212|SGPT213|SGP311|SGP312|SGP321',
],
// http://www.support.philips.com/support/catalog/worldproducts.jsp?userLanguage=en&userCountry=cn&categoryid=3G_LTE_TABLET_SU_CN_CARE&title=3G%20tablets%20/%20LTE%20range&_dyncharset=UTF-8
'PhilipsTablet' => '\b(PI2010|PI3000|PI3100|PI3105|PI3110|PI3205|PI3210|PI3900|PI4010|PI7000|PI7100)\b',
// db + http://www.cube-tablet.com/buy-products.html
'CubeTablet' => 'Android.*(K8GT|U9GT|U10GT|U16GT|U17GT|U18GT|U19GT|U20GT|U23GT|U30GT)|CUBE U8GT',
// http://www.cobyusa.com/?p=pcat&pcat_id=3001
'CobyTablet' => 'MID1042|MID1045|MID1125|MID1126|MID7012|MID7014|MID7015|MID7034|MID7035|MID7036|MID7042|MID7048|MID7127|MID8042|MID8048|MID8127|MID9042|MID9740|MID9742|MID7022|MID7010',
// http://www.match.net.cn/products.asp
'MIDTablet' => [
'M9701|M9000|M9100|M806|M1052|M806|T703|MID701|MID713|MID710|MID727|MID760|MID830|MID728|MID933|MID125|MID810|MID732|MID120|MID930|MID800',
'MID731|MID900|MID100|MID820|MID735|MID980|MID130|MID833|MID737|MID960|MID135|MID860|MID736|MID140|MID930|MID835|MID733|MID4X10',
],
// http://www.msi.com/support
// @todo Research the Windows Tablets.
'MSITablet' => 'MSI \b(Primo 73K|Primo 73L|Primo 81L|Primo 77|Primo 93|Primo 75|Primo 76|Primo 73|Primo 81|Primo 91|Primo 90|Enjoy 71|Enjoy 7|Enjoy 10)\b',
// @todo http://www.kyoceramobile.com/support/drivers/
// 'KyoceraTablet' => null,
// @todo http://intexuae.com/index.php/category/mobile-devices/tablets-products/
// 'IntextTablet' => null,
// http://pdadb.net/index.php?m=pdalist&list=SMiT (NoName Chinese Tablets)
// http://www.imp3.net/14/show.php?itemid=20454
'SMiTTablet' => 'Android.*(\bMID\b|MID-560|MTV-T1200|MTV-PND531|MTV-P1101|MTV-PND530)',
// http://www.rock-chips.com/index.php?do=prod&pid=2
'RockChipTablet' => 'Android.*(RK2818|RK2808A|RK2918|RK3066)|RK2738|RK2808A',
// http://www.fly-phone.com/devices/tablets/ ; http://www.fly-phone.com/service/
'FlyTablet' => 'IQ310|Fly Vision',
// http://www.bqreaders.com/gb/tablets-prices-sale.html
'bqTablet' => 'Android.*(bq)?.*\b(Elcano|Curie|Edison|Maxwell|Kepler|Pascal|Tesla|Hypatia|Platon|Newton|Livingstone|Cervantes|Avant|Aquaris ([E|M]10|M8))\b|Maxwell.*Lite|Maxwell.*Plus',
// http://www.huaweidevice.com/worldwide/productFamily.do?method=index&directoryId=5011&treeId=3290
// http://www.huaweidevice.com/worldwide/downloadCenter.do?method=index&directoryId=3372&treeId=0&tb=1&type=software (including legacy tablets)
'HuaweiTablet' => 'MediaPad|MediaPad 7 Youth|IDEOS S7|S7-201c|S7-202u|S7-101|S7-103|S7-104|S7-105|S7-106|S7-201|S7-Slim|M2-A01L|BAH-L09|BAH-W09|AGS-L09|CMR-AL19|KOB2-L09|BG2-U01|BG2-W09|BG2-U03|AGS-W09',
// Nec or Medias Tab
'NecTablet' => '\bN-06D|\bN-08D',
// Pantech Tablets: http://www.pantechusa.com/phones/
'PantechTablet' => 'Pantech.*P4100',
// Broncho Tablets: http://www.broncho.cn/ (hard to find)
'BronchoTablet' => 'Broncho.*(N701|N708|N802|a710)',
// http://versusuk.com/support.html
'VersusTablet' => 'TOUCHPAD.*[78910]|\bTOUCHTAB\b',
// http://www.zync.in/index.php/our-products/tablet-phablets
'ZyncTablet' => 'z1000|Z99 2G|z930|z990|z909|Z919|z900', // Removed "z999" because of https://github.com/serbanghita/Mobile-Detect/issues/717
// http://www.positivoinformatica.com.br/www/pessoal/tablet-ypy/
'PositivoTablet' => 'TB07STA|TB10STA|TB07FTA|TB10FTA',
// https://www.nabitablet.com/
'NabiTablet' => 'Android.*\bNabi',
'KoboTablet' => 'Kobo Touch|\bK080\b|\bVox\b Build|\bArc\b Build',
// French Danew Tablets http://www.danew.com/produits-tablette.php
'DanewTablet' => 'DSlide.*\b(700|701R|702|703R|704|802|970|971|972|973|974|1010|1012)\b',
// Texet Tablets and Readers http://www.texet.ru/tablet/
'TexetTablet' => [
'TB-840HD|TB-760HD|TB-750HD|TB-740HD|TB-730HD|TB-722HD|TB-720HD|TB-700HD|TB-500HD|TB-470HD|TB-431HD|TB-430HD|TB-506|TB-504|TB-446|TB-436|TB-416|TB-146SE|TB-126SE',
'TB-719A|TB-823A|TB-805A|TB-723A|TB-715A|TB-707A|TB-705A|TB-709A|TB-711A|TB-890HD|TB-880HD|TB-790HD|TB-780HD|TB-770HD|TB-721HD|TB-710HD|TB-434HD|TB-860HD',
'TM-7011|TM-7010|TM-7023|TM-7025|TM-7037W|TM-7038W|TM-7027W|TM-9720|TM-9725|TM-9737W|TM-1020|TM-9738W|TM-9740|TM-9743W|TB-807A|TB-771A|TB-727A|TB-725A',
'NaviPad|TB-772A|TM-7045|TM-7055|TM-9750|TM-7016|TM-7024|TM-7026|TM-7041|TM-7043|TM-7047|TM-8041|TM-9741|TM-9747|TM-9748|TM-9751|TM-7022|TM-7021|TM-7020',
],
// Avoid detecting 'PLAYSTATION 3' as mobile.
'PlaystationTablet' => 'Playstation.*(Portable|Vita)',
// http://www.trekstor.de/surftabs.html
'TrekstorTablet' => 'ST10416-1|VT10416-1|ST70408-1|ST702xx-1|ST702xx-2|ST80208|ST97216|ST70104-2|VT10416-2|ST10216-2A|SurfTab',
// http://www.pyleaudio.com/Products.aspx?%2fproducts%2fPersonal-Electronics%2fTablets
'PyleAudioTablet' => '\b(PTBL10CEU|PTBL10C|PTBL72BC|PTBL72BCEU|PTBL7CEU|PTBL7C|PTBL92BC|PTBL92BCEU|PTBL9CEU|PTBL9CUK|PTBL9C)\b',
// http://www.advandigital.com/index.php?link=content-product&jns=JP001
// because of the short codenames we have to include whitespaces to reduce the possible conflicts.
'AdvanTablet' => 'Android.* \b(E3A|T3X|T5C|T5B|T3E|T3C|T3B|T1J|T1F|T2A|T1H|T1i|E1C|T1-E|T5-A|T4|E1-B|T2Ci|T1-B|T1-D|O1-A|E1-A|T1-A|T3A|T4i)\b ',
// http://www.danytech.com/category/tablet-pc
'DanyTechTablet' => 'Genius Tab G3|Genius Tab S2|Genius Tab Q3|Genius Tab G4|Genius Tab Q4|Genius Tab G-II|Genius TAB GII|Genius TAB GIII|Genius Tab S1',
// http://www.galapad.net/product.html ; https://github.com/serbanghita/Mobile-Detect/issues/761
'GalapadTablet' => 'Android [0-9.]+; [a-z-]+; \bG1\b',
// http://www.micromaxinfo.com/tablet/funbook
'MicromaxTablet' => 'Funbook|Micromax.*\b(P250|P560|P360|P362|P600|P300|P350|P500|P275)\b',
// http://www.karbonnmobiles.com/products_tablet.php
'KarbonnTablet' => 'Android.*\b(A39|A37|A34|ST8|ST10|ST7|Smart Tab3|Smart Tab2)\b',
// http://www.myallfine.com/Products.asp
'AllFineTablet' => 'Fine7 Genius|Fine7 Shine|Fine7 Air|Fine8 Style|Fine9 More|Fine10 Joy|Fine11 Wide',
// http://www.proscanvideo.com/products-search.asp?itemClass=TABLET&itemnmbr=
'PROSCANTablet' => [
'\b(PLT8088|PLT8223G|PLT8234G|PLT8235G|PLT8816K|PLT9011|PLT9045K|PLT9233G|PLT9735|PLT9760G|PLT9770G)\b',
'\b(PLT7045KB|PLT7071KG|PLT7072|PLT7223G|PLT7225G|PLT7777G|PLT7810K|PLT7849G|PLT7851G|PLT7852G|PLT8015|PLT8031|PLT8034|PLT8036|PLT8080K|PLT8082)\b',
'\b(PEM63|PLT1023G|PLT1041|PLT1044|PLT1044G|PLT1091|PLT4311|PLT4311PL|PLT4315|PLT7030|PLT7033|PLT7033D|PLT7035|PLT7035D|PLT7044K|PLT7045K)\b',
],
// http://www.yonesnav.com/products/products.php
'YONESTablet' => 'BQ1078|BC1003|BC1077|RK9702|BC9730|BC9001|IT9001|BC7008|BC7010|BC708|BC728|BC7012|BC7030|BC7027|BC7026',
// http://www.cjshowroom.com/eproducts.aspx?classcode=004001001
// China manufacturer makes tablets for different small brands (eg. http://www.zeepad.net/index.html)
'ChangJiaTablet' => [
'TPC10101|TPC10103|TPC10106|TPC10111|TPC10203|TPC10205|TPC10503',
'TPC8203|TPC8205|TPC8503|TPC9106|TPC9701|TPC97101|TPC97103|TPC97105|TPC97106|TPC97111|TPC97113|TPC97203|TPC97603|TPC97809|TPC97205',
'TPC7102|TPC7103|TPC7105|TPC7106|TPC7107|TPC7201|TPC7203|TPC7205|TPC7210|TPC7708|TPC7709|TPC7712|TPC7110|TPC8101|TPC8103|TPC8105|TPC8106',
],
// http://www.gloryunion.cn/products.asp
// http://www.allwinnertech.com/en/apply/mobile.html
// http://www.ptcl.com.pk/pd_content.php?pd_id=284 (EVOTAB)
// @todo: Softwiner tablets?
// aka. Cute or Cool tablets. Not sure yet, must research to avoid collisions.
'GUTablet' => 'TX-A1301|TX-M9002|Q702|kf026', // A12R|D75A|D77|D79|R83|A95|A106C|R15|A75|A76|D71|D72|R71|R73|R77|D82|R85|D92|A97|D92|R91|A10F|A77F|W71F|A78F|W78F|W81F|A97F|W91F|W97F|R16G|C72|C73E|K72|K73|R96G
// http://www.pointofview-online.com/showroom.php?shop_mode=product_listing&category_id=118
'PointOfViewTablet' => [
'TAB-PL1015|TAB-P1025|TAB-PI1045|TAB-P1325|TAB-PROTAB[0-9]+|TAB-PROTAB25|TAB-PROTAB26|TAB-PROTAB27|TAB-PROTAB26XL|TAB-PROTAB2-IPS9|TAB-PROTAB30-IPS9|TAB-PROTAB25XXL|TAB-PROTAB26-IPS10|TAB-PROTAB30-IPS10',
'TAB-P506|TAB-navi-7-3G-M|TAB-P517|TAB-P-527|TAB-P701|TAB-P703|TAB-P721|TAB-P731N|TAB-P741|TAB-P825|TAB-P905|TAB-P925|TAB-PR945',
],
// http://www.overmax.pl/pl/katalog-produktow,p8/tablety,c14/
// @todo: add more tests.
'OvermaxTablet' => 'OV-(SteelCore|NewBase|Basecore|Baseone|Exellen|Quattor|EduTab|Solution|ACTION|BasicTab|TeddyTab|MagicTab|Stream|TB-08|TB-09)|Qualcore 1027',
// http://hclmetablet.com/India/index.php
'HCLTablet' => 'HCL.*Tablet|Connect-3G-2.0|Connect-2G-2.0|ME Tablet U1|ME Tablet U2|ME Tablet G1|ME Tablet X1|ME Tablet Y2|ME Tablet Sync',
// http://www.edigital.hu/Tablet_es_e-book_olvaso/Tablet-c18385.html
'DPSTablet' => 'DPS Dream 9|DPS Dual 7',
// http://www.visture.com/index.asp
'VistureTablet' => 'V97 HD|i75 3G|Visture V4( HD)?|Visture V5( HD)?|Visture V10',
// http://www.mijncresta.nl/tablet
'CrestaTablet' => 'CTP(-)?810|CTP(-)?818|CTP(-)?828|CTP(-)?838|CTP(-)?888|CTP(-)?978|CTP(-)?980|CTP(-)?987|CTP(-)?988|CTP(-)?989',
// MediaTek - http://www.mediatek.com/_en/01_products/02_proSys.php?cata_sn=1&cata1_sn=1&cata2_sn=309
'MediatekTablet' => '\bMT8125|MT8389|MT8135|MT8377\b',
// Concorde tab
'ConcordeTablet' => 'Concorde([ ]+)?Tab|ConCorde ReadMan',
// GoClever Tablets - http://www.goclever.com/uk/products,c1/tablet,c5/
'GoCleverTablet' => [
'TAB T72|TAB R83|TAB R974|TAB R973|TAB A101|TAB A103|TAB A104|TAB A104.2|R105BK|M713G|A972BK|TAB A971|TAB R974.2|TAB R104|TAB R83.3|TAB A1042',
'TAB R70|TAB R76.2|TAB R106|TAB R83.2|TAB M813G|TAB I721|GCTA722|TAB I70|TAB I71|TAB S73|TAB R73|TAB R74|TAB R93|TAB R75|TAB R76.1|TAB A73|TAB A93|TAB A93.2',
'GOCLEVER TAB|A7GOCLEVER|M1042|M7841|M742|R1042BK|R1041|TAB A975|TAB A7842|TAB A741|TAB A741L|TAB M723G|TAB M721|TAB A1021|TAB I921|TAB R721|TAB I720|TAB T76',
],
// Modecom Tablets - http://www.modecom.eu/tablets/portal/
'ModecomTablet' => [
'FreeTAB 9000|FreeTAB 7.4|FreeTAB 7004|FreeTAB 7800|FreeTAB 2096|FreeTAB 7.5|FreeTAB 1014|FreeTAB 1001 |FreeTAB 8001|FreeTAB 9706|FreeTAB 9702',
'FreeTAB 7003|FreeTAB 7002|FreeTAB 1002|FreeTAB 7801|FreeTAB 1331|FreeTAB 1004|FreeTAB 8002|FreeTAB 8014|FreeTAB 9704|FreeTAB 1003',
],
// Vonino Tablets
'VoninoTablet' => [
'\b(Argus[ _]?S|Diamond[ _]?79HD|Emerald[ _]?78E|Luna[ _]?70C|Onyx[ _]?S|Onyx[ _]?Z|Orin[ _]?HD|Orin[ _]?S|Otis[ _]?S|SpeedStar[ _]?S|Magnet[ _]?M9|Primus[ _]?94[ _]?3G|Primus[ _]?94HD|Primus[ _]?QS)\b',
'\b(Android.*\bQ8\b|Sirius[ _]?EVO[ _]?QS|Sirius[ _]?QS|Spirit[ _]?S)\b',
],
// ECS Tablets - http://www.ecs.com.tw/ECSWebSite/Product/Product_Tablet_List.aspx?CategoryID=14&MenuID=107&childid=M_107&LanID=0
'ECSTablet' => 'V07OT2|TM105A|S10OT1|TR10CS1',
// Storex Tablets - http://storex.fr/espace_client/support.html
// @note: no need to add all the tablet codes since they are guided by the first regex.
'StorexTablet' => 'eZee[_\']?(Tab|Go)[0-9]+|TabLC7|Looney Tunes Tab',
// Generic Vodafone tablets.
'VodafoneTablet' => 'SmartTab([ ]+)?[0-9]+|SmartTabII10|SmartTabII7|VF-1497|VFD 1400',
// French tablets - Essentiel B http://www.boulanger.fr/tablette_tactile_e-book/tablette_tactile_essentiel_b/cl_68908.htm?multiChoiceToDelete=brand&mc_brand=essentielb
// Aka: http://www.essentielb.fr/
'EssentielBTablet' => 'Smart[ \']?TAB[ ]+?[0-9]+|Family[ \']?TAB2',
// Ross & Moor - http://ross-moor.ru/
'RossMoorTablet' => 'RM-790|RM-997|RMD-878G|RMD-974R|RMT-705A|RMT-701|RME-601|RMT-501|RMT-711',
// i-mobile http://product.i-mobilephone.com/Mobile_Device
'iMobileTablet' => 'i-mobile i-note',
// http://www.tolino.de/de/vergleichen/
'TolinoTablet' => 'tolino tab [0-9.]+|tolino shine',
// AudioSonic - a Kmart brand
// http://www.kmart.com.au/webapp/wcs/stores/servlet/Search?langId=-1&storeId=10701&catalogId=10001&categoryId=193001&pageSize=72¤tPage=1&searchCategory=193001%2b4294965664&sortBy=p_MaxPrice%7c1
'AudioSonicTablet' => '\bC-22Q|T7-QC|T-17B|T-17P\b',
// AMPE Tablets - http://www.ampe.com.my/product-category/tablets/
// @todo: add them gradually to avoid conflicts.
'AMPETablet' => 'Android.* A78 ',
// Skk Mobile - http://skkmobile.com.ph/product_tablets.php
'SkkTablet' => 'Android.* (SKYPAD|PHOENIX|CYCLOPS)',
// Tecno Mobile (only tablet) - http://www.tecno-mobile.com/index.php/product?filterby=smart&list_order=all&page=1
'TecnoTablet' => 'TECNO P9|TECNO DP8D',
// JXD (consoles & tablets) - http://jxd.hk/products.asp?selectclassid=009008&clsid=3
'JXDTablet' => [
'Android.* \b(F3000|A3300|JXD5000|JXD3000|JXD2000|JXD300B|JXD300|S5800|S7800|S602b|S5110b|S7300|S5300|S602|S603)\b',
'Android.* \b(S5100|S5110|S601|S7100a|P3000F|P3000s|P101|P200s|P1000m|P200m|P9100|P1000s|S6600b|S908|P1000|P300|S18|S6600|S9100)\b',
],
// i-Joy tablets - http://www.i-joy.es/en/cat/products/tablets/
'iJoyTablet' => [
'Tablet (Spirit 7|Essentia|Galatea|Fusion|Onix 7|Landa|Titan|Scooby|Deox|Stella|Themis|Argon|Unique 7|Sygnus|Hexen|Finity 7)',
'Tablet (Cream|Cream X2|Jade|Neon 7|Neron 7|Kandy|Scape|Saphyr 7|Rebel|Biox|Rebel|Rebel 8GB|Myst|Draco 7|Myst|Tab7-004|Myst)',
'Tablet (Tadeo Jones|Tablet Boing|Arrow|Draco Dual Cam|Aurix|Mint|Amity|Revolution|Finity 9|Neon 9|T9w|Amity 4GB Dual Cam)',
'Tablet (Stone 4GB|Stone 8GB|Andromeda|Silken|X2|Andromeda II|Halley|Flame|Saphyr 9,7|Touch 8|Planet|Triton|Unique 10|Hexen 10|Memphis 4GB|Memphis 8GB|Onix 10)',
],
// http://www.intracon.eu/tablet
'FX2Tablet' => 'FX2 PAD7|FX2 PAD10',
// http://www.xoro.de/produkte/
// @note: Might be the same brand with 'Simply tablets'
'XoroTablet' => [
'KidsPAD 701|PAD[ ]?712|PAD[ ]?714|PAD[ ]?716|PAD[ ]?717|PAD[ ]?718|PAD[ ]?720|PAD[ ]?721|PAD[ ]?722|PAD[ ]?790',
'PAD[ ]?792|PAD[ ]?900|PAD[ ]?9715D|PAD[ ]?9716DR|PAD[ ]?9718DR|PAD[ ]?9719QR|PAD[ ]?9720QR|TelePAD1030|Telepad1032',
'TelePAD730|TelePAD731|TelePAD732|TelePAD735Q|TelePAD830|TelePAD9730|TelePAD795|MegaPAD 1331|MegaPAD 1851|MegaPAD 2151',
],
// http://www1.viewsonic.com/products/computing/tablets/
'ViewsonicTablet' => 'ViewPad 10pi|ViewPad 10e|ViewPad 10s|ViewPad E72|ViewPad7|ViewPad E100|ViewPad 7e|ViewSonic VB733|VB100a',
// https://www.verizonwireless.com/tablets/verizon/
'VerizonTablet' => 'QTAQZ3|QTAIR7|QTAQTZ3|QTASUN1|QTASUN2|QTAXIA1',
// http://www.odys.de/web/internet-tablet_en.html
'OdysTablet' => 'LOOX|XENO10|ODYS[ -](Space|EVO|Xpress|NOON)|\bXELIO\b|Xelio10Pro|XELIO7PHONETAB|XELIO10EXTREME|XELIOPT2|NEO_QUAD10',
// http://www.captiva-power.de/products.html#tablets-en
'CaptivaTablet' => 'CAPTIVA PAD',
// IconBIT - http://www.iconbit.com/products/tablets/
'IconbitTablet' => 'NetTAB|NT-3702|NT-3702S|NT-3702S|NT-3603P|NT-3603P|NT-0704S|NT-0704S|NT-3805C|NT-3805C|NT-0806C|NT-0806C|NT-0909T|NT-0909T|NT-0907S|NT-0907S|NT-0902S|NT-0902S',
// http://www.teclast.com/topic.php?channelID=70&topicID=140&pid=63
'TeclastTablet' => [
'T98 4G|\bP80\b|\bX90HD\b|X98 Air|X98 Air 3G|\bX89\b|P80 3G|\bX80h\b|P98 Air|\bX89HD\b|P98 3G|\bP90HD\b|P89 3G|X98 3G',
'\bP70h\b|P79HD 3G|G18d 3G|\bP79HD\b|\bP89s\b|\bA88\b|\bP10HD\b|\bP19HD\b|G18 3G|\bP78HD\b|\bA78\b|\bP75\b|G17s 3G|G17h 3G',
'\bP85t\b|\bP90\b|\bP11\b|\bP98t\b|\bP98HD\b|\bG18d\b|\bP85s\b|\bP11HD\b|\bP88s\b|\bA80HD\b|\bA80se\b|\bA10h\b|\bP89\b',
'\bP78s\b|\bG18\b|\bP85\b|\bA70h\b|\bA70\b|\bG17\b|\bP18\b|\bA80s\b|\bA11s\b|\bP88HD\b|\bA80h\b|\bP76s\b|\bP76h\b|\bP98\b',
'\bA10HD\b|\bP78\b|\bP88\b|\bA11\b|\bA10t\b|\bP76a\b|\bP76t\b|\bP76e\b|\bP85HD\b|\bP85a\b|\bP86\b|\bP75HD\b|\bP76v\b|\bA12\b',
'\bP75a\b|\bA15\b|\bP76Ti\b|\bP81HD\b|\bA10\b|\bT760VE\b|\bT720HD\b|\bP76\b|\bP73\b|\bP71\b|\bP72\b|\bT720SE\b|\bC520Ti\b|\bT760\b|\bT720VE\b|T720-3GE|T720-WiFi',
],
// Onda - http://www.onda-tablet.com/buy-android-onda.html?dir=desc&limit=all&order=price
'OndaTablet' => [
'\b(V975i|Vi30|VX530|V701|Vi60|V701s|Vi50|V801s|V719|Vx610w|VX610W|V819i|Vi10|VX580W|Vi10|V711s|V813|V811)\b[\s]+',
'\b(V820w|V820|Vi20|V711|VI30W|V712|V891w|V972|V819w|V820w|Vi60|V820w|V711|V813s|V801|V819|V975s|V801|V819)\b[\s]+',
'\b(V819|V818|V811|V712|V975m|V101w|V961w|V812|V818|V971|V971s|V919|V989|V116w|V102w|V973|Vi40)\b[\s]+|V10 \b4G\b',
],
'JaytechTablet' => 'TPC-PA762',
'BlaupunktTablet' => 'Endeavour 800NG|Endeavour 1010',
// http://www.digma.ru/support/download/
// @todo: Ebooks also (if requested)
'DigmaTablet' => '\b(iDx10|iDx9|iDx8|iDx7|iDxD7|iDxD8|iDsQ8|iDsQ7|iDsQ8|iDsD10|iDnD7|3TS804H|iDsQ11|iDj7|iDs10)\b',
// http://www.evolioshop.com/ro/tablete-pc.html
// http://www.evolio.ro/support/downloads_static.html?cat=2
// @todo: Research some more
'EvolioTablet' => 'ARIA_Mini_wifi|Aria[ _]Mini|Evolio X10|Evolio X7|Evolio X8|\bEvotab\b|\bNeura\b',
// @todo http://www.lavamobiles.com/tablets-data-cards
'LavaTablet' => 'QPAD E704|\bIvoryS\b|E-TAB IVORY|\bE-TAB\b',
// http://www.breezetablet.com/
'AocTablet' => 'MW0811|MW0812|MW0922|MTK8382|MW1031|MW0831|MW0821|MW0931|MW0712',
// http://www.mpmaneurope.com/en/products/internet-tablets-14/android-tablets-14/
'MpmanTablet' => [
'MP11 OCTA|MP10 OCTA|MPQC1114|MPQC1004|MPQC994|MPQC974|MPQC973|MPQC804|MPQC784|MPQC780|\bMPG7\b|MPDCG75|MPDCG71',
'MPDC1006|MP101DC|MPDC9000|MPDC905|MPDC706HD|MPDC706|MPDC705|MPDC110|MPDC100|MPDC99|MPDC97|MPDC88|MPDC8|MPDC77',
'MP709|MID701|MID711|MID170|MPDC703|MPQC1010',
],
// https://www.celkonmobiles.com/?_a=categoryphones&sid=2
'CelkonTablet' => 'CT695|CT888|CT[\s]?910|CT7 Tab|CT9 Tab|CT3 Tab|CT2 Tab|CT1 Tab|C820|C720|\bCT-1\b',
// http://www.wolderelectronics.com/productos/manuales-y-guias-rapidas/categoria-2-miTab
'WolderTablet' => [
'miTab \b(DIAMOND|SPACE|BROOKLYN|NEO|FLY|MANHATTAN|FUNK|EVOLUTION|SKY|GOCAR|IRON|GENIUS|POP|MINT)\b',
'miTab \b(EPSILON|BROADWAY|JUMP|HOP|LEGEND|NEW AGE|LINE|ADVANCE|FEEL|FOLLOW|LIKE|LINK|LIVE|THINK|FREEDOM|CHICAGO|CLEVELAND)\b',
'miTab \b(BALTIMORE-GH|IOWA|BOSTON|SEATTLE|PHOENIX|DALLAS|IN 101|MasterChef)\b',
],
'MediacomTablet' => 'M-MPI10C3G|M-SP10EG|M-SP10EGP|M-SP10HXAH|M-SP7HXAH|M-SP10HXBH|M-SP8HXAH|M-SP8MXA',
// http://www.mi.com/en
'MiTablet' => '\bMI PAD\b|\bHM NOTE 1W\b',
// http://www.nbru.cn/index.html
'NibiruTablet' => 'Nibiru M1|Nibiru Jupiter One',
// http://navroad.com/products/produkty/tablety/
// http://navroad.com/products/produkty/tablety/
'NexoTablet' => 'NEXO NOVA|NEXO 10|NEXO AVIO|NEXO FREE|NEXO GO|NEXO EVO|NEXO 3G|NEXO SMART|NEXO KIDDO|NEXO MOBI',
// http://leader-online.com/new_site/product-category/tablets/
// http://www.leader-online.net.au/List/Tablet
'LeaderTablet' => [
'TBLT10Q|TBLT10I|TBL-10WDKB|TBL-10WDKBO2013|TBL-W230V2|TBL-W450|TBL-W500|SV572|TBLT7I|TBA-AC7-8G',
'TBLT79|TBL-8W16|TBL-10W32|TBL-10WKB|TBL-W100',
],
// http://www.datawind.com/ubislate/
'UbislateTablet' => 'UbiSlate[\s]?7C',
// http://www.pocketbook-int.com/ru/support
'PocketBookTablet' => 'Pocketbook',
// http://www.kocaso.com/product_tablet.html
'KocasoTablet' => '\b(TB-1207)\b',
// http://global.hisense.com/product/asia/tablet/Sero7/201412/t20141215_91832.htm
'HisenseTablet' => '\b(F5281|E2371)\b',
// http://www.tesco.com/direct/hudl/
'Hudl' => 'Hudl HT7S3|Hudl 2',
// http://www.telstra.com.au/home-phone/thub-2/
'TelstraTablet' => 'T-Hub2',
'GenericTablet' => [
'Android.*\b97D\b|Tablet(?!.*PC)|BNTV250A|MID-WCDMA|LogicPD Zoom2|\bA7EB\b|CatNova8|A1_07|CT704|CT1002',
'\bM721\b|rk30sdk|\bEVOTAB\b|M758A|ET904|ALUMIUM10|Smartfren Tab|Endeavour 1010|Tablet-PC-4|Tagi Tab',
'\bM6pro\b|CT1020W|arc 10HD|\bTP750\b|\bQTAQZ3\b|WVT101|TM1088|KT107',
],
];
/**
* List of mobile Operating Systems.
* @var array
*/
protected static array $operatingSystems = [
'AndroidOS' => 'Android',
'BlackBerryOS' => 'blackberry|\bBB10\b|rim tablet os',
'PalmOS' => 'PalmOS|avantgo|blazer|elaine|hiptop|palm|plucker|xiino',
'SymbianOS' => 'Symbian|SymbOS|Series60|Series40|SYB-[0-9]+|\bS60\b',
// @reference: http://en.wikipedia.org/wiki/Windows_Mobile
'WindowsMobileOS' => 'Windows CE.*(PPC|Smartphone|Mobile|[0-9]{3}x[0-9]{3})|Windows Mobile|Windows Phone [0-9.]+|WCE;',
// @reference: http://en.wikipedia.org/wiki/Windows_Phone
// http://wifeng.cn/?r=blog&a=view&id=106
// http://nicksnettravels.builttoroam.com/post/2011/01/10/Bogus-Windows-Phone-7-User-Agent-String.aspx
// http://msdn.microsoft.com/library/ms537503.aspx
// https://msdn.microsoft.com/en-us/library/hh869301(v=vs.85).aspx
'WindowsPhoneOS' => 'Windows Phone 10.0|Windows Phone 8.1|Windows Phone 8.0|Windows Phone OS|XBLWP7|ZuneWP7|Windows NT 6.[23]; ARM;',
'iOS' => '\biPhone.*Mobile|\biPod|\biPad|AppleCoreMedia',
// https://en.wikipedia.org/wiki/IPadOS
'iPadOS' => 'CPU OS 13',
// @reference https://en.m.wikipedia.org/wiki/Sailfish_OS
// https://sailfishos.org/
'SailfishOS' => 'Sailfish',
// http://en.wikipedia.org/wiki/MeeGo
// @todo: research MeeGo in UAs
'MeeGoOS' => 'MeeGo',
// http://en.wikipedia.org/wiki/Maemo
// @todo: research Maemo in UAs
'MaemoOS' => 'Maemo',
'JavaOS' => 'J2ME/|\bMIDP\b|\bCLDC\b', // '|Java/' produces bug #135
'webOS' => 'webOS|hpwOS',
'badaOS' => '\bBada\b',
'BREWOS' => 'BREW',
'HarmonyOS' => 'HarmonyOS',
];
/**
* List of mobile User Agents.
*
* IMPORTANT: This is a list of mobile browsers only.
* Since Mobile Detect 2.x.x, this list supports mobile browsers only.
* Mobile Detect was never designed to detect all browsers.
* @var array
*/
protected static array $browsers = [
//'Vivaldi' => 'Vivaldi',
// @reference: https://developers.google.com/chrome/mobile/docs/user-agent
'Chrome' => '\bCrMo\b|CriOS.*Mobile|Android.*Chrome/[.0-9]* Mobile',
'Dolfin' => '\bDolfin\b',
'Opera' => 'Opera.*Mini|Opera.*Mobi|Android.*Opera|Mobile.*OPR/[0-9.]+$|Coast/[0-9.]+',
'Skyfire' => 'Skyfire',
// Added "Edge on iOS" https://github.com/serbanghita/Mobile-Detect/issues/764
'Edge' => 'EdgiOS.*Mobile|Mobile Safari/[.0-9]* Edge',
'IE' => 'IEMobile|MSIEMobile', // |Trident/[.0-9]+
'Firefox' => 'fennec|firefox.*maemo|(Mobile|Tablet).*Firefox|Firefox.*Mobile|FxiOS.*Mobile',
'Bolt' => 'bolt',
'TeaShark' => 'teashark',
'Blazer' => 'Blazer',
// @reference: http://developer.apple.com/library/safari/#documentation/AppleApplications/Reference/SafariWebContent/OptimizingforSafarioniPhone/OptimizingforSafarioniPhone.html#//apple_ref/doc/uid/TP40006517-SW3
// Excluded "Edge on iOS" https://github.com/serbanghita/Mobile-Detect/issues/764
'Safari' => 'Version((?!\bEdgiOS\b).)*Mobile.*Safari|Safari.*Mobile|MobileSafari',
// http://en.wikipedia.org/wiki/Midori_(web_browser)
//'Midori' => 'midori',
//'Tizen' => 'Tizen',
'WeChat' => '\bMicroMessenger\b',
'UCBrowser' => 'UC.*Browser|UCWEB',
'baiduboxapp' => 'baiduboxapp',
'baidubrowser' => 'baidubrowser',
// https://github.com/serbanghita/Mobile-Detect/issues/7
'DiigoBrowser' => 'DiigoBrowser',
// http://www.puffinbrowser.com/index.php
// https://github.com/serbanghita/Mobile-Detect/issues/752
// 'Puffin' => 'Puffin',
// http://mercury-browser.com/index.html
'Mercury' => '\bMercury\b',
// http://en.wikipedia.org/wiki/Obigo_Browser
'ObigoBrowser' => 'Obigo',
// http://en.wikipedia.org/wiki/NetFront
'NetFront' => 'NF-Browser',
// @reference: http://en.wikipedia.org/wiki/Minimo
// http://en.wikipedia.org/wiki/Vision_Mobile_Browser
'GenericBrowser' => 'NokiaBrowser|OviBrowser|OneBrowser|TwonkyBeamBrowser|SEMC.*Browser|FlyFlow|Minimo|NetFront|Novarra-Vision|MQQBrowser|MicroMessenger',
// @reference: https://en.wikipedia.org/wiki/Pale_Moon_(web_browser)
'PaleMoon' => 'Android.*PaleMoon|Mobile.*PaleMoon',
'HuaweiBrowser' => 'HuaweiBrowser',
];
/**
* All possible HTTP headers that represent the
* User-Agent string.
* @var array
*/
protected static array $knownUserAgentHttpHeaders = [
// The default User-Agent string.
'HTTP_USER_AGENT',
// Header can occur on devices using Opera Mini.
'HTTP_X_OPERAMINI_PHONE_UA',
// Vodafone specific header: http://www.seoprinciple.com/mobile-web-community-still-angry-at-vodafone/24/
'HTTP_X_DEVICE_USER_AGENT',
'HTTP_X_ORIGINAL_USER_AGENT',
'HTTP_X_SKYFIRE_PHONE',
'HTTP_X_BOLT_PHONE_UA',
'HTTP_DEVICE_STOCK_UA',
'HTTP_X_UCBROWSER_DEVICE_UA'
];
/**
* The individual segments that could exist in a User-Agent string. VER refers to the regular
* expression defined in the constant self::VERSION_REGEX.
* @var array
*/
protected static array $properties = [
// Build
'Mobile' => 'Mobile/[VER]',
'Build' => 'Build/[VER]',
'Version' => 'Version/[VER]',
'VendorID' => 'VendorID/[VER]',
// Devices
'iPad' => 'iPad.*CPU[a-z ]+[VER]',
'iPhone' => 'iPhone.*CPU[a-z ]+[VER]',
'iPod' => 'iPod.*CPU[a-z ]+[VER]',
//'BlackBerry' => array('BlackBerry[VER]', 'BlackBerry [VER];'),
'Kindle' => 'Kindle/[VER]',
// Browser
'Chrome' => ['Chrome/[VER]', 'CriOS/[VER]', 'CrMo/[VER]'],
'Coast' => ['Coast/[VER]'],
'Dolfin' => 'Dolfin/[VER]',
// @reference: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent/Firefox
'Firefox' => ['Firefox/[VER]', 'FxiOS/[VER]'],
'Fennec' => 'Fennec/[VER]',
// http://msdn.microsoft.com/en-us/library/ms537503(v=vs.85).aspx
// https://msdn.microsoft.com/en-us/library/ie/hh869301(v=vs.85).aspx
'Edge' => 'Edge/[VER]',
'IE' => ['IEMobile/[VER];', 'IEMobile [VER]', 'MSIE [VER];', 'Trident/[0-9.]+;.*rv:[VER]'],
// http://en.wikipedia.org/wiki/NetFront
'NetFront' => 'NetFront/[VER]',
'NokiaBrowser' => 'NokiaBrowser/[VER]',
'Opera' => [' OPR/[VER]', 'Opera Mini/[VER]', 'Version/[VER]'],
'Opera Mini' => 'Opera Mini/[VER]',
'Opera Mobi' => 'Version/[VER]',
'UCBrowser' => ['UCWEB[VER]', 'UC.*Browser/[VER]'],
'MQQBrowser' => 'MQQBrowser/[VER]',
'MicroMessenger' => 'MicroMessenger/[VER]',
'baiduboxapp' => 'baiduboxapp/[VER]',
'baidubrowser' => 'baidubrowser/[VER]',
'SamsungBrowser' => 'SamsungBrowser/[VER]',
'Iron' => 'Iron/[VER]',
// @note: Safari 7534.48.3 is actually Version 5.1.
// @note: On BlackBerry the Version is overwriten by the OS.
'Safari' => ['Version/[VER]', 'Safari/[VER]'],
'Skyfire' => 'Skyfire/[VER]',
'Tizen' => 'Tizen/[VER]',
'Webkit' => 'webkit[ /][VER]',
'PaleMoon' => 'PaleMoon/[VER]',
'SailfishBrowser' => 'SailfishBrowser/[VER]',
// Engine
'Gecko' => 'Gecko/[VER]',
'Trident' => 'Trident/[VER]',
'Presto' => 'Presto/[VER]',
'Goanna' => 'Goanna/[VER]',
// OS
'iOS' => ' \bi?OS\b [VER][ ;]{1}',
'Android' => 'Android [VER]',
'Sailfish' => 'Sailfish [VER]',
'BlackBerry' => ['BlackBerry[\w]+/[VER]', 'BlackBerry.*Version/[VER]', 'Version/[VER]'],
'BREW' => 'BREW [VER]',
'Java' => 'Java/[VER]',
// @reference: http://windowsteamblog.com/windows_phone/b/wpdev/archive/2011/08/29/introducing-the-ie9-on-windows-phone-mango-user-agent-string.aspx
// @reference: http://en.wikipedia.org/wiki/Windows_NT#Releases
'Windows Phone OS' => ['Windows Phone OS [VER]', 'Windows Phone [VER]'],
'Windows Phone' => 'Windows Phone [VER]',
'Windows CE' => 'Windows CE/[VER]',
// http://social.msdn.microsoft.com/Forums/en-US/windowsdeveloperpreviewgeneral/thread/6be392da-4d2f-41b4-8354-8dcee20c85cd
'Windows NT' => 'Windows NT [VER]',
'Symbian' => ['SymbianOS/[VER]', 'Symbian/[VER]'],
'webOS' => ['webOS/[VER]', 'hpwOS/[VER];'],
];
/**
* Construct an instance of this class.
*/
public function __construct(
?CacheInterface $cache = null,
array $config = [],
) {
// If no custom cache provided then use our own.
$this->cache = $cache ?? new Cache();
// Override config from user.
$this->config = array_merge($this->config, $config);
// Beware that if you use "autoInitOfHttpHeaders: false" and you forget to setUserAgent
// to something other than a string, an MobileDetectException exception will be thrown.
if ($this->config['autoInitOfHttpHeaders']) {
$this->autoInitKnownHttpHeaders();
}
}
/**
* Get the current script version.
* Used in demo.php file.
*
* @return string The version number in semantic version format.
*/
public function getVersion(): string
{
return $this->VERSION;
}
/**
* On startup Mobile Detect library will auto-initiate from the existing
* HTTP headers extracted from $_SERVER.
*
* @return void
*/
public function autoInitKnownHttpHeaders(): void
{
// Go through known HTTP headers that we care about.
// See "4.1.18. Protocol-Specific Meta-Variables" of http://www.faqs.org/rfcs/rfc3875.html
$knownHttpHeaders = array_merge(
array_values(self::$knownUserAgentHttpHeaders),
array_keys(self::$knownMobilePositiveHeaders),
array_values(self::$knownCloudFrontHeaders)
);
// Did not iterate through global $_SERVER to find ['HTTP...'] header values
// because it's very slow and on some servers it can have more than 50 worthless keys.
// $httpHeaders = array_filter($_SERVER, function ($key) {
// return str_starts_with($key, 'HTTP_');
// }, ARRAY_FILTER_USE_KEY);
$httpHeaders = [];
foreach ($knownHttpHeaders as $headerName) {
if (isset($_SERVER[$headerName])) {
$httpHeaders[$headerName] = $_SERVER[$headerName];
}
}
$this->setHttpHeaders($httpHeaders);
// Set the User-Agent even if it's an empty string so that "autoInitOfHttpHeaders" doesn't throw an exception.
// https://github.com/serbanghita/Mobile-Detect/issues/946#issuecomment-1885675939
if (!$this->hasUserAgent()) {
$this->setUserAgent("");
}
}
/**
* Set the HTTP Headers. Must be PHP-flavored. This method will reset existing headers.
*
* @param array $httpHeaders The headers to set. If null, then using PHP's _SERVER to extract
* the headers. The default null is left for backwards compatibility.
*/
public function setHttpHeaders(array $httpHeaders = []): void
{
$this->httpHeaders = $httpHeaders;
// Don't process any further if no actual HTTP headers were set.
if (count($httpHeaders) === 0) {
return;
}
// Setting new HTTP headers automatically resets the User-Agent.
// Set current User-Agent from known User-Agent-like HTTP header(s).
$userAgent = "";
foreach ($this->getUaHttpHeaders() as $altHeader) {
if (!empty($this->httpHeaders[$altHeader])) {
$userAgent .= $this->httpHeaders[$altHeader] . " ";
}
}
if (!empty($userAgent)) {
$this->setUserAgent($userAgent);
}
// Override User-Agent string if 'Amazon Cloudfront' specific HTTP headers are present.
if (
$this->hasHttpHeader(self::$knownCloudFrontHeaders[0]) ||
$this->hasHttpHeader(self::$knownCloudFrontHeaders[1])
) {
$this->setUserAgent(self::$cloudFrontUA);
}
}
/**
* Retrieves the HTTP headers.
*
* @return array
*/
public function getHttpHeaders(): array
{
return $this->httpHeaders;
}
public function hasHttpHeaders(): bool
{
return count($this->httpHeaders) > 0;
}
protected function hasHttpHeader(string $name): bool
{
return !empty($this->httpHeaders[$name]);
}
/**
* Retrieves a particular header. If it doesn't exist, no exception/error is caused.
* Simply null is returned.
*
* @param string $header The name of the header to retrieve. Can be HTTP compliant such as
* "User-Agent" or "X-Device-User-Agent" or can be php-esque with the
* all-caps, HTTP_ prefixed, underscore separated awesomeness.
*
* @return string|null The value of the header.
*/
public function getHttpHeader(string $header): ?string
{
// are we using PHP-flavored headers?
if (!str_contains($header, '_')) {
$header = str_replace('-', '_', $header);
$header = strtoupper($header);
}
// test the alternate, too
$altHeader = 'HTTP_' . $header;
//Test both the regular and the HTTP_ prefix
if (isset($this->httpHeaders[$header])) {
return $this->httpHeaders[$header];
} elseif (isset($this->httpHeaders[$altHeader])) {
return $this->httpHeaders[$altHeader];
}
return null;
}
public function getMobileHeaders(): array
{
return static::$knownMobilePositiveHeaders;
}
/**
* Get all possible HTTP headers that
* can contain the User-Agent string.
*
* @return array List of HTTP headers.
*/
public function getUaHttpHeaders(): array
{
return static::$knownUserAgentHttpHeaders;
}
/**
* Retrieves the HTTP CloudFront headers
* that trigger a mobile detection.
*
* @return array
*/
public function getCloudFrontHttpHeaders(): array
{
return static::$knownCloudFrontHeaders;
}
/**
* Prepare the User-Agent string for matching phase.
*
* @param string $userAgent The User-Agent string.
* @return string
*/
private function prepareUserAgent(string $userAgent): string
{
$userAgent = trim($userAgent);
return substr($userAgent, 0, $this->config['maximumUserAgentLength']);
}
/**
* Set the User-Agent to be used.
*
* @param string $userAgent The User-Agent string.
* @return string
*/
public function setUserAgent(string $userAgent): string
{
$preparedUserAgent = $this->prepareUserAgent($userAgent);
return $this->userAgent = $preparedUserAgent;
}
/**
* Retrieve the User-Agent.
*
* @return string|null The user agent if it's set.
*/
public function getUserAgent(): ?string
{
return $this->userAgent;
}
public function hasUserAgent(): bool
{
return is_string($this->userAgent);
}
public function isUserAgentEmpty(): bool
{
return $this->hasUserAgent() && $this->userAgent === '';
}
public function getMatchingRegex(): ?string
{
return $this->matchingRegex;
}
public function getMatchesArray(): ?array
{
return $this->matchesArray;
}
/**
* Retrieve the list of known phone devices.
*
* @return array List of phone devices.
*/
public static function getPhoneDevices(): array
{
return static::$phoneDevices;
}
/**
* Retrieve the list of known tablet devices.
*
* @return array List of tablet devices.
*/
public static function getTabletDevices(): array
{
return static::$tabletDevices;
}
/**
* Retrieve the list of known browsers. Specifically, the user agents.
*
* @return array List of browsers / user agents.
*/
public static function getBrowsers(): array
{
return static::$browsers;
}
/**
* Method gets the mobile detection rules.
* This method is used for the magic methods $detect->is*().
* Retrieve the current set of rules.
*
* @return array
*/
public function getRules(): array
{
static $rules;
if (!$rules) {
$rules = array_merge(
static::$browsers,
static::$operatingSystems,
static::$phoneDevices,
static::$tabletDevices
);
}
return $rules;
}
/**
* Retrieve the list of mobile operating systems.
*
* @return array The list of mobile operating systems.
*/
public static function getOperatingSystems(): array
{
return static::$operatingSystems;
}
/**
* Check the HTTP headers for signs of mobile.
* This is the fastest mobile check possible; it's used
* inside isMobile() method.
*
* @return bool
*/
public function checkHttpHeadersForMobile(): bool
{
foreach ($this->getMobileHeaders() as $mobileHeader => $matchType) {
if (isset($this->httpHeaders[$mobileHeader])) {
if (isset($matchType['matches']) && is_array($matchType['matches'])) {
foreach ($matchType['matches'] as $_match) {
if (str_contains($this->httpHeaders[$mobileHeader], $_match)) {
return true;
}
}
return false;
} else {
return true;
}
}
}
return false;
}
/**
* Magic overloading method.
*
* @param string $name
* @param array $arguments
* @return bool
* @throws BadMethodCallException when the method doesn't exist and doesn't start with 'is'
* @throws \Exception
*/
public function __call(string $name, array $arguments)
{
// make sure the name starts with 'is', otherwise
if (!str_starts_with($name, 'is')) {
throw new BadMethodCallException("No such method exists: $name");
}
$ruleName = substr($name, 2);
return $this->is($ruleName);
}
/**
* Check if the device is mobile.
* Returns true if any type of mobile device detected, including special ones
* @return bool
* @throws MobileDetectException
*/
public function isMobile(): bool
{
if (!$this->hasUserAgent()) {
throw new MobileDetectException('No valid user-agent has been set.', MobileDetectExceptionCode::INVALID_USER_AGENT_ERR);
}
if ($this->isUserAgentEmpty()) {
return false;
}
// Cache check.
try {
$cacheKey = $this->createCacheKey("mobile");
$cacheItem = $this->cache->get($cacheKey);
if ($cacheItem !== null) {
return $cacheItem;
}
// Special case: Amazon CloudFront mobile viewer
if (
$this->getUserAgent() === self::$cloudFrontUA &&
$this->getHttpHeader('HTTP_CLOUDFRONT_IS_MOBILE_VIEWER') === 'true'
) {
$this->cache->set($cacheKey, true, $this->config['cacheTtl']);
return true;
}
if ($this->hasHttpHeaders() && $this->checkHttpHeadersForMobile()) {
$this->cache->set($cacheKey, true, $this->config['cacheTtl']);
return true;
} else {
$result = $this->matchUserAgentWithFirstFoundMatchingRule();
$this->cache->set($cacheKey, $result, $this->config['cacheTtl']);
return $result;
}
} catch (CacheInvalidArgumentException | CacheException | PsrInvalidArgumentException $e) {
throw new MobileDetectException("Cache problem in isMobile(): {$e->getMessage()}", MobileDetectExceptionCode::IS_MOBILE_ERR, $e);
}
}
/**
* Check if the device is a tablet.
* Return true if any type of tablet device is detected.
* @return bool
* @throws MobileDetectException
*/
public function isTablet(): bool
{
if (!$this->hasUserAgent()) {
throw new MobileDetectException('No user-agent has been set.', MobileDetectExceptionCode::INVALID_USER_AGENT_ERR);
}
if ($this->isUserAgentEmpty()) {
return false;
}
// Cache check.
try {
$cacheKey = $this->createCacheKey("tablet");
$cacheItem = $this->cache->get($cacheKey);
if ($cacheItem !== null) {
return $cacheItem;
}
// Special case: Amazon CloudFront mobile viewer
if (
$this->getUserAgent() === self::$cloudFrontUA &&
$this->getHttpHeader('HTTP_CLOUDFRONT_IS_TABLET_VIEWER') === 'true'
) {
$this->cache->set($cacheKey, true, $this->config['cacheTtl']);
return true;
}
foreach (static::$tabletDevices as $_regex) {
$regexString = $_regex;
// "regex" is array of "strings"
if (is_array($_regex)) {
$regexString = implode("|", $_regex);
}
if ($this->match($regexString, $this->getUserAgent())) {
$this->cache->set($cacheKey, true, $this->config['cacheTtl']);
return true;
}
// if (is_array($_regex)) {
// foreach ($_regex as $regexString) {
// $result = $this->match($regexString, $this->getUserAgent());
// if ($result) {
// $this->cache->set($cacheKey, true, $this->config['cacheTtl']);
// return true;
// }
// }
// } else {
// // assume the regex is a "string"
// if ($this->match($_regex, $this->getUserAgent())) {
// $this->cache->set($cacheKey, true, $this->config['cacheTtl']);
// return true;
// }
// }
}
$this->cache->set($cacheKey, false, $this->config['cacheTtl']);
return false;
} catch (CacheInvalidArgumentException | CacheException | PsrInvalidArgumentException $e) {
throw new MobileDetectException("Cache problem in isTablet(): {$e->getMessage()}", MobileDetectExceptionCode::IS_TABLET_ERR, $e);
}
}
/**
* Checks if a rule (e.g. isIphone, isIOS, etc.) matches its regex against the User-Agent.
*
* @param string $ruleName
* @return bool
* @throws MobileDetectException
*/
public function is(string $ruleName): bool
{
if (!$this->hasUserAgent()) {
throw new MobileDetectException('No user-agent has been set.', MobileDetectExceptionCode::INVALID_USER_AGENT_ERR);
}
if ($this->isUserAgentEmpty()) {
return false;
}
// Cache check.
try {
$cacheKey = $this->createCacheKey($ruleName);
$cacheItem = $this->cache->get($cacheKey);
if ($cacheItem !== null) {
return $cacheItem;
}
$result = $this->matchUserAgentWithRule($ruleName);
// Cache save.
$this->cache->set($cacheKey, $result, $this->config['cacheTtl']);
return $result;
} catch (CacheInvalidArgumentException | CacheException | PsrInvalidArgumentException $e) {
throw new MobileDetectException("Cache problem in is(): {$e->getMessage()}", MobileDetectExceptionCode::IS_MAGIC_ERR, $e);
}
}
/**
* Some detection rules are relative (not standard),
* because of the diversity of devices, vendors and
* their conventions in representing the User-Agent or
* the HTTP headers.
*
* This method will be used to check custom regexes against
* the User-Agent string.
*
* @param string $regex
* @param string $userAgent
* @return bool
*
* @todo: search in the HTTP headers too.
*/
public function match(string $regex, string $userAgent): bool
{
$match = (bool) preg_match(sprintf('#%s#is', $regex), $userAgent, $matches);
// If positive match is found, store the results for debug.
if ($match) {
$this->matchingRegex = $regex;
$this->matchesArray = $matches;
}
return $match;
}
/**
* Find a detection rule that matches the current User-agent.
* @return bool
*/
protected function matchUserAgentWithFirstFoundMatchingRule(): bool
{
// Begin general search.
foreach ($this->getRules() as $_regex) {
if (empty($_regex)) {
continue;
}
// regex is an array of "strings"
if (is_array($_regex)) {
foreach ($_regex as $regexString) {
if ($this->match($regexString, $this->getUserAgent())) {
return true;
}
}
} else {
// assume regex is "string"
if ($this->match($_regex, $this->getUserAgent())) {
return true;
}
}
}
return false;
}
/**
* Search for a certain key in the rules array.
* If the key is found then try to match the corresponding
* regex against the User-Agent.
*
* @param string $ruleName
* @return bool
*/
protected function matchUserAgentWithRule(string $ruleName): bool
{
$result = false;
// Make the keys lowercase, so we can match: isIphone(), isiPhone(), isiphone(), etc.
$ruleName = strtolower($ruleName);
// change the keys to lower case
$_rules = array_change_key_case($this->getRules());
if (false === empty($_rules[$ruleName])) {
$regexString = $_rules[$ruleName];
if (is_array($_rules[$ruleName])) {
$regexString = implode("|", $_rules[$ruleName]);
}
$result = $this->match($regexString, $this->getUserAgent());
// if (is_array($_rules[$ruleName])) {
// foreach($_rules[$ruleName] as $ruleRegex) {
// $result = $this->match($ruleRegex, $this->getUserAgent());
// if ($result) {
// return true;
// }
// }
// } else {
// $result = $this->match($_rules[$ruleName], $this->getUserAgent());
// }
}
return $result;
}
/**
* Prepare the version number.
* @todo Remove the error suppression from str_replace() call.
*
* @param string $ver The string version, like "2.6.21.2152";
* @return float
*/
public function prepareVersionNo(string $ver): float
{
$ver = str_replace(['_', ' ', '/'], '.', $ver);
$arrVer = explode('.', $ver, 2);
if (isset($arrVer[1])) {
$arrVer[1] = @str_replace('.', '', $arrVer[1]); // @todo: treat strings versions.
}
return (float) implode('.', $arrVer);
}
/**
* Check the version of the given property in the User-Agent.
* Will return a float number. (e.g. 2_0 will return 2.0, 4.3.1 will return 4.31)
*
* @param string $propertyName The name of the property. See self::getProperties() array
* keys for all possible properties.
* @param string $type Either self::VERSION_TYPE_STRING to get a string value or
* self::VERSION_TYPE_FLOAT indicating a float value. This parameter
* is optional and defaults to self::VERSION_TYPE_STRING. Passing an
* invalid parameter will default to the type as well.
*
* @return string|float|false The version of the property we are trying to extract.
*/
public function version(string $propertyName, string $type = self::VERSION_TYPE_STRING): float|bool|string
{
if (empty($propertyName) || !$this->hasUserAgent()) {
return false;
}
// set the $type to the default if we don't recognize the type
if ($type !== self::VERSION_TYPE_STRING && $type !== self::VERSION_TYPE_FLOAT) {
$type = self::VERSION_TYPE_STRING;
}
$properties = self::getProperties();
// Check if the property exists in the properties array.
if (true === isset($properties[$propertyName])) {
// Prepare the pattern to be matched.
// Make sure we always deal with an array (string is converted).
$properties[$propertyName] = (array) $properties[$propertyName];
foreach ($properties[$propertyName] as $propertyMatchString) {
$propertyPattern = str_replace('[VER]', self::VERSION_REGEX, $propertyMatchString);
// Identify and extract the version.
preg_match(sprintf('#%s#is', $propertyPattern), $this->userAgent, $match);
if (false === empty($match[1])) {
return ($type == self::VERSION_TYPE_FLOAT ? $this->prepareVersionNo($match[1]) : $match[1]);
}
}
}
return false;
}
public function getCache(): CacheInterface
{
return $this->cache;
}
/**
* Creates the cache key string based on the defined fn.
* Function can be customized in the constructor. See `$config['cacheKeyFn']`.
*
* @throws CacheException
*/
protected function createCacheKey(string $key): string
{
$userAgentKey = $this->hasUserAgent() ? $this->userAgent : '';
$httpHeadersKey = $this->hasHttpHeaders() ? static::flattenHeaders($this->httpHeaders) : '';
$cacheKey = "$key:$userAgentKey:$httpHeadersKey";
$cacheKeyFn = $this->config['cacheKeyFn'];
if (!is_callable($cacheKeyFn)) {
throw new CacheException('cacheKeyFn is not a function.');
}
return call_user_func($cacheKeyFn, $cacheKey);
}
public static function flattenHeaders(array $httpHeaders): string
{
$key = '';
foreach ($httpHeaders as $name => $value) {
$key .= "$name: $value" . PHP_EOL;
}
return trim($key);
}
/**
* Get the properties array.
*
* @return array
*/
public static function getProperties(): array
{
return static::$properties;
}
}
================================================
FILE: src/MobileDetectStandalone.php
================================================
<?php
namespace Detection;
require_once dirname(__FILE__) . '/../standalone/autoloader.php';
class MobileDetectStandalone extends MobileDetect
{
}
================================================
FILE: standalone/autoloader.php
================================================
<?php
$dir = dirname(__FILE__);
spl_autoload_register(function ($class) use ($dir) {
$classMap = [
// "mobiledetect/mobiledetectlib"
"Detection\Cache\Cache" => $dir . "/../src/Cache/Cache.php",
"Detection\Cache\CacheException" => $dir . "/../src/Cache/CacheException.php",
"Detection\Cache\CacheInvalidArgumentException" => $dir . "/../src/Cache/CacheInvalidArgumentException.php",
"Detection\Exception\MobileDetectException" => $dir . "/../src/Exception/MobileDetectException.php",
"Detection\Exception\MobileDetectExceptionCode" => $dir . "/../src/Exception/MobileDetectExceptionCode.php",
"Detection\MobileDetect" => $dir . "/../src/MobileDetect.php",
// "psr/simple-cache"
"Psr\SimpleCache\CacheException" => $dir . "/deps/simple-cache/src/CacheException.php",
"Psr\SimpleCache\CacheInterface" => $dir . "/deps/simple-cache/src/CacheInterface.php",
"Psr\SimpleCache\InvalidArgumentException" => $dir . "/deps/simple-cache/src/InvalidArgumentException.php",
];
$fileFound = $classMap[$class] ?? false;
if ($fileFound) {
require $fileFound;
return true;
}
return false;
});
================================================
FILE: standalone/deps/simple-cache/.editorconfig
================================================
; This file is for unifying the coding style for different editors and IDEs.
; More information at http://editorconfig.org
root = true
[*]
charset = utf-8
indent_size = 4
indent_style = space
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
================================================
FILE: standalone/deps/simple-cache/.gitattributes
================================================
# Path-based git attributes
# https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html
# Ignore all test and documentation with "export-ignore".
/.gitattributes export-ignore
/.gitignore export-ignore
/.travis.yml export-ignore
/phpunit.xml.dist export-ignore
/.scrutinizer.yml export-ignore
/.editorconfig export-ignore
/tests export-ignore
================================================
FILE: standalone/deps/simple-cache/.gitignore
================================================
build
composer.lock
docs
vendor
================================================
FILE: standalone/deps/simple-cache/LICENSE.md
================================================
# The MIT License (MIT)
Copyright (c) 2016 PHP Framework Interoperability Group
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in
> all copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> THE SOFTWARE.
================================================
FILE: standalone/deps/simple-cache/README.md
================================================
PHP FIG Simple Cache PSR
========================
This repository holds all interfaces related to PSR-16.
Note that this is not a cache implementation of its own. It is merely an interface that describes a cache implementation. See [the specification](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-16-simple-cache.md) for more details.
You can find implementations of the specification by looking for packages providing the [psr/simple-cache-implementation](https://packagist.org/providers/psr/simple-cache-implementation) virtual package.
================================================
FILE: standalone/deps/simple-cache/composer.json
================================================
{
"name": "psr/simple-cache",
"description": "Common interfaces for simple caching",
"keywords": ["psr", "psr-16", "cache", "simple-cache", "caching"],
"license": "MIT",
"authors": [
{
"name": "PHP-FIG",
"homepage": "https://www.php-fig.org/"
}
],
"require": {
"php": ">=8.0.0"
},
"autoload": {
"psr-4": {
"Psr\\SimpleCache\\": "src/"
}
},
"extra": {
"branch-alias": {
"dev-master": "3.0.x-dev"
}
}
}
================================================
FILE: standalone/deps/simple-cache/src/CacheException.php
================================================
<?php
namespace Psr\SimpleCache;
/**
* Interface used for all types of exceptions thrown by the implementing library.
*/
interface CacheException extends \Throwable
{
}
================================================
FILE: standalone/deps/simple-cache/src/CacheInterface.php
================================================
<?php
namespace Psr\SimpleCache;
interface CacheInterface
{
/**
* Fetches a value from the cache.
*
* @param string $key The unique key of this item in the cache.
* @param mixed $default Default value to return if the key does not exist.
*
* @return mixed The value of the item from the cache, or $default in case of cache miss.
*
* @throws \Psr\SimpleCache\InvalidArgumentException
* MUST be thrown if the $key string is not a legal value.
*/
public function get(string $key, mixed $default = null): mixed;
/**
* Persists data in the cache, uniquely referenced by a key with an optional expiration TTL time.
*
* @param string $key The key of the item to store.
* @param mixed $value The value of the item to store, must be serializable.
* @param null|int|\DateInterval $ttl Optional. The TTL value of this item. If no value is sent and
* the driver supports TTL then the library may set a default value
* for it or let the driver take care of that.
*
* @return bool True on success and false on failure.
*
* @throws \Psr\SimpleCache\InvalidArgumentException
* MUST be thrown if the $key string is not a legal value.
*/
public function set(string $key, mixed $value, null|int|\DateInterval $ttl = null): bool;
/**
* Delete an item from the cache by its unique key.
*
* @param string $key The unique cache key of the item to delete.
*
* @return bool True if the item was successfully removed. False if there was an error.
*
* @throws \Psr\SimpleCache\InvalidArgumentException
* MUST be thrown if the $key string is not a legal value.
*/
public function delete(string $key): bool;
/**
* Wipes clean the entire cache's keys.
*
* @return bool True on success and false on failure.
*/
public function clear(): bool;
/**
* Obtains multiple cache items by their unique keys.
*
* @param iterable<string> $keys A list of keys that can be obtained in a single operation.
* @param mixed $default Default value to return for keys that do not exist.
*
* @return iterable<string, mixed> A list of key => value pairs. Cache keys that do not exist or are stale will have $default as value.
*
* @throws \Psr\SimpleCache\InvalidArgumentException
* MUST be thrown if $keys is neither an array nor a Traversable,
* or if any of the $keys are not a legal value.
*/
public function getMultiple(iterable $keys, mixed $default = null): iterable;
/**
* Persists a set of key => value pairs in the cache, with an optional TTL.
*
* @param iterable $values A list of key => value pairs for a multiple-set operation.
* @param null|int|\DateInterval $ttl Optional. The TTL value of this item. If no value is sent and
* the driver supports TTL then the library may set a default value
* for it or let the driver take care of that.
*
* @return bool True on success and false on failure.
*
* @throws \Psr\SimpleCache\InvalidArgumentException
* MUST be thrown if $values is neither an array nor a Traversable,
* or if any of the $values are not a legal value.
*/
public function setMultiple(iterable $values, null|int|\DateInterval $ttl = null): bool;
/**
* Deletes multiple cache items in a single operation.
*
* @param iterable<string> $keys A list of string-based keys to be deleted.
*
* @return bool True if the items were successfully removed. False if there was an error.
*
* @throws \Psr\SimpleCache\InvalidArgumentException
* MUST be thrown if $keys is neither an array nor a Traversable,
* or if any of the $keys are not a legal value.
*/
public function deleteMultiple(iterable $keys): bool;
/**
* Determines whether an item is present in the cache.
*
* NOTE: It is recommended that has() is only to be used for cache warming type purposes
* and not to be used within your live applications operations for get/set, as this method
* is subject to a race condition where your has() will return true and immediately after,
* another script can remove it making the state of your app out of date.
*
* @param string $key The cache item key.
*
* @return bool
*
* @throws \Psr\SimpleCache\InvalidArgumentException
* MUST be thrown if the $key string is not a legal value.
*/
public function has(string $key): bool;
}
================================================
FILE: standalone/deps/simple-cache/src/InvalidArgumentException.php
================================================
<?php
namespace Psr\SimpleCache;
/**
* Exception interface for invalid cache arguments.
*
* When an invalid argument is passed it must throw an exception which implements
* this interface
*/
interface InvalidArgumentException extends CacheException
{
}
================================================
FILE: tests/CacheTest.php
================================================
<?php
namespace DetectionTests;
use DateInterval;
use Detection\Cache\Cache;
use Detection\Cache\CacheInvalidArgumentException;
use PHPUnit\Framework\TestCase;
final class CacheTest extends TestCase
{
protected Cache $cache;
protected function setUp(): void
{
$this->cache = new Cache();
}
/**
* @throws CacheInvalidArgumentException
*/
public function testGetInvalidCacheKeyThrowsException()
{
$this->expectException(CacheInvalidArgumentException::class);
$this->cache->get('');
}
/**
* @throws CacheInvalidArgumentException
*/
public function testSetInvalidCacheKeyThrowsException()
{
$this->expectException(CacheInvalidArgumentException::class);
$this->cache->set('', 'a', 100);
}
/**
* @throws CacheInvalidArgumentException
*/
public function testGetNonExistentReturnsNull()
{
$this->assertNull($this->cache->get('random'));
}
/**
* @throws CacheInvalidArgumentException
*/
public function testGetNonExistentReturnsCustomDefault(): void
{
$this->assertEquals('customDefault', $this->cache->get('random', 'customDefault'));
}
/**
* @throws CacheInvalidArgumentException
*/
public function testGetExpiredItemReturnsDefault(): void
{
$this->cache->set('expiring', 'value', 1);
sleep(2);
$this->assertNull($this->cache->get('expiring'));
}
/**
* @throws CacheInvalidArgumentException
*/
public function testGetExpiredItemReturnsCustomDefault(): void
{
$this->cache->set('expiring', 'value', 1);
sleep(2);
$this->assertEquals('fallback', $this->cache->get('expiring', 'fallback'));
}
/**
* @throws CacheInvalidArgumentException
*/
public function testSetGetBooleanValues()
{
$this->cache->set('isMobile', true, 100);
$this->assertTrue($this->cache->get('isMobile'));
$this->cache->set('isTablet', false, 100);
$this->assertFalse($this->cache->get('isTablet'));
}
/**
* @throws CacheInvalidArgumentException
*/
public function testSetGetZeroTTL()
{
$this->cache->set('isMobile', true, 0);
$this->assertNull($this->cache->get('isMobile'));
}
/**
* @throws CacheInvalidArgumentException
*/
public function testSetGetNegativeTTL()
{
$this->cache->set('isMobile', true, -999);
$this->assertNull($this->cache->get('isMobile'));
}
/**
* @throws CacheInvalidArgumentException
*/
public function testSetZeroTTLWithInvalidKeyThrowsException()
{
$this->expectException(CacheInvalidArgumentException::class);
$this->cache->set('', true, 0);
}
/**
* @throws CacheInvalidArgumentException
*/
public function testSetNegativeTTLWithInvalidKeyThrowsException()
{
$this->expectException(CacheInvalidArgumentException::class);
$this->cache->set('', true, -999);
}
/**
* @throws CacheInvalidArgumentException
*/
public function testSetValidTtlAsAnIntegerReturnsTheSetValue()
{
$this->cache->set('isMobile', 'someValue', 1000);
$this->assertEquals('someValue', $this->cache->get('isMobile'));
}
/**
* @throws CacheInvalidArgumentException
*/
public function testSetNullTtlReturnsTheSetValue()
{
$this->cache->set('isMobile', 'abc');
$this->assertEquals('abc', $this->cache->get('isMobile'));
}
/**
* @throws CacheInvalidArgumentException
*/
public function testSetWithDateIntervalTtl(): void
{
$this->cache->set('withInterval', 'intervalValue', new DateInterval('PT1H'));
$this->assertEquals('intervalValue', $this->cache->get('withInterval'));
}
/**
* @throws CacheInvalidArgumentException
*/
public function testDeletionOfValidRecord()
{
$this->cache->set('isMobile', 'a b c', 100);
$this->assertEquals('a b c', $this->cache->get('isMobile'))
gitextract_umzpjg5f/
├── .claude/
│ └── settings.json
├── .editorconfig
├── .gitattributes
├── .github/
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ └── feature_request.md
│ ├── docs/
│ │ └── index.html
│ └── workflows/
│ ├── 4.8.x-test.yml
│ └── website.yml
├── .gitignore
├── .php-cs-fixer.php
├── .phpcs.xml
├── CHANGELOG.md
├── CLAUDE.md
├── CNAME
├── CONTRIBUTING.md
├── DOCKER-COMPOSE.md
├── KNOWN_LIMITATIONS.md
├── LICENSE
├── MobileDetect.json
├── README-EXAMPLES.md
├── README.md
├── SECURITY.md
├── composer.json
├── docker/
│ ├── Dockerfile.setup
│ └── build.sh
├── docker-compose.yml
├── phpbench.json
├── scripts/
│ ├── dump_magic_methods.php
│ ├── example.php
│ ├── export_to_json.php
│ ├── pre-commit-hook.sh
│ ├── test.php
│ └── test_standalone.php
├── src/
│ ├── Cache/
│ │ ├── Cache.php
│ │ ├── CacheException.php
│ │ └── CacheInvalidArgumentException.php
│ ├── Exception/
│ │ ├── MobileDetectException.php
│ │ └── MobileDetectExceptionCode.php
│ ├── MobileDetect.php
│ └── MobileDetectStandalone.php
├── standalone/
│ ├── autoloader.php
│ └── deps/
│ └── simple-cache/
│ ├── .editorconfig
│ ├── .gitattributes
│ ├── .gitignore
│ ├── LICENSE.md
│ ├── README.md
│ ├── composer.json
│ └── src/
│ ├── CacheException.php
│ ├── CacheInterface.php
│ └── InvalidArgumentException.php
└── tests/
├── CacheTest.php
├── MobileDetectExceptionTest.php
├── MobileDetectGeneralTest.php
├── MobileDetectStandaloneTest.php
├── MobileDetectVersionTest.php
├── MobileDetectWithCacheTest.php
├── UserAgentList.inc.php
├── UserAgentTest.php
├── benchmark/
│ └── MobileDetectBench.php
├── bootstrap.php
├── phpunit.xml
├── providers/
│ └── vendors/
│ ├── AOC.php
│ ├── Acer.php
│ ├── Alcatel.php
│ ├── Allview.php
│ ├── Amazon.php
│ ├── Apple.php
│ ├── Archos.php
│ ├── Asus.php
│ ├── Blackberry.php
│ ├── Dell.php
│ ├── Google.php
│ ├── HP.php
│ ├── HTC.php
│ ├── Huawei.php
│ ├── LG.php
│ ├── Lava.php
│ ├── Leader.php
│ ├── Lenovo.php
│ ├── Mi.php
│ ├── Microsoft.php
│ ├── Motorola.php
│ ├── Mpman.php
│ ├── Nexus.php
│ ├── Nokia.php
│ ├── Onda.php
│ ├── Others.php
│ ├── Prestigio.php
│ ├── Samsung.php
│ ├── Sony.php
│ ├── SpecialCases.php
│ ├── Verizon.php
│ ├── Vodafone.php
│ └── ZTE.php
└── ualist.json
SYMBOL INDEX (182 symbols across 18 files)
FILE: src/Cache/Cache.php
class Cache (line 18) | class Cache implements CacheInterface
method get (line 26) | public function get(string $key, mixed $default = null): mixed
method set (line 45) | public function set(string $key, mixed $value, int|DateInterval|null $...
method delete (line 68) | public function delete(string $key): bool
method deleteSingle (line 82) | private function deleteSingle(string $key): void
method clear (line 88) | public function clear(): bool
method has (line 99) | public function has(string $key): bool
method getMultiple (line 115) | public function getMultiple(iterable $keys, mixed $default = null): it...
method setMultiple (line 127) | public function setMultiple(iterable $values, int|DateInterval|null $t...
method deleteMultiple (line 139) | public function deleteMultiple(iterable $keys): bool
method checkKey (line 151) | protected function checkKey(string $key): string
method getTTL (line 162) | protected function getTTL(DateInterval|int|null $ttl): ?int
method checkReturn (line 180) | protected function checkReturn(array $booleans): bool
method getKeys (line 197) | public function getKeys(): array
method evictExpired (line 210) | public function evictExpired(): int
FILE: src/Cache/CacheException.php
class CacheException (line 7) | class CacheException extends \Exception implements \Psr\SimpleCache\Cach...
FILE: src/Cache/CacheInvalidArgumentException.php
class CacheInvalidArgumentException (line 9) | class CacheInvalidArgumentException extends CacheException implements In...
FILE: src/Exception/MobileDetectException.php
class MobileDetectException (line 7) | class MobileDetectException extends \Exception
FILE: src/Exception/MobileDetectExceptionCode.php
class MobileDetectExceptionCode (line 7) | class MobileDetectExceptionCode
FILE: src/MobileDetect.php
class MobileDetect (line 231) | class MobileDetect
method __construct (line 1050) | public function __construct(
method getVersion (line 1072) | public function getVersion(): string
method autoInitKnownHttpHeaders (line 1083) | public function autoInitKnownHttpHeaders(): void
method setHttpHeaders (line 1119) | public function setHttpHeaders(array $httpHeaders = []): void
method getHttpHeaders (line 1155) | public function getHttpHeaders(): array
method hasHttpHeaders (line 1160) | public function hasHttpHeaders(): bool
method hasHttpHeader (line 1165) | protected function hasHttpHeader(string $name): bool
method getHttpHeader (line 1180) | public function getHttpHeader(string $header): ?string
method getMobileHeaders (line 1201) | public function getMobileHeaders(): array
method getUaHttpHeaders (line 1212) | public function getUaHttpHeaders(): array
method getCloudFrontHttpHeaders (line 1223) | public function getCloudFrontHttpHeaders(): array
method prepareUserAgent (line 1234) | private function prepareUserAgent(string $userAgent): string
method setUserAgent (line 1246) | public function setUserAgent(string $userAgent): string
method getUserAgent (line 1257) | public function getUserAgent(): ?string
method hasUserAgent (line 1262) | public function hasUserAgent(): bool
method isUserAgentEmpty (line 1267) | public function isUserAgentEmpty(): bool
method getMatchingRegex (line 1272) | public function getMatchingRegex(): ?string
method getMatchesArray (line 1277) | public function getMatchesArray(): ?array
method getPhoneDevices (line 1287) | public static function getPhoneDevices(): array
method getTabletDevices (line 1297) | public static function getTabletDevices(): array
method getBrowsers (line 1307) | public static function getBrowsers(): array
method getRules (line 1319) | public function getRules(): array
method getOperatingSystems (line 1340) | public static function getOperatingSystems(): array
method checkHttpHeadersForMobile (line 1352) | public function checkHttpHeadersForMobile(): bool
method __call (line 1382) | public function __call(string $name, array $arguments)
method isMobile (line 1400) | public function isMobile(): bool
method isTablet (line 1446) | public function isTablet(): bool
method is (line 1515) | public function is(string $ruleName): bool
method match (line 1558) | public function match(string $regex, string $userAgent): bool
method matchUserAgentWithFirstFoundMatchingRule (line 1574) | protected function matchUserAgentWithFirstFoundMatchingRule(): bool
method matchUserAgentWithRule (line 1608) | protected function matchUserAgentWithRule(string $ruleName): bool
method prepareVersionNo (line 1644) | public function prepareVersionNo(string $ver): float
method version (line 1669) | public function version(string $propertyName, string $type = self::VER...
method getCache (line 1703) | public function getCache(): CacheInterface
method createCacheKey (line 1714) | protected function createCacheKey(string $key): string
method flattenHeaders (line 1730) | public static function flattenHeaders(array $httpHeaders): string
method getProperties (line 1744) | public static function getProperties(): array
FILE: src/MobileDetectStandalone.php
class MobileDetectStandalone (line 7) | class MobileDetectStandalone extends MobileDetect
FILE: standalone/deps/simple-cache/src/CacheException.php
type CacheException (line 8) | interface CacheException extends \Throwable
FILE: standalone/deps/simple-cache/src/CacheInterface.php
type CacheInterface (line 5) | interface CacheInterface
method get (line 18) | public function get(string $key, mixed $default = null): mixed;
method set (line 34) | public function set(string $key, mixed $value, null|int|\DateInterval ...
method delete (line 46) | public function delete(string $key): bool;
method clear (line 53) | public function clear(): bool;
method getMultiple (line 67) | public function getMultiple(iterable $keys, mixed $default = null): it...
method setMultiple (line 83) | public function setMultiple(iterable $values, null|int|\DateInterval $...
method deleteMultiple (line 96) | public function deleteMultiple(iterable $keys): bool;
method has (line 113) | public function has(string $key): bool;
FILE: standalone/deps/simple-cache/src/InvalidArgumentException.php
type InvalidArgumentException (line 11) | interface InvalidArgumentException extends CacheException
FILE: tests/CacheTest.php
class CacheTest (line 10) | final class CacheTest extends TestCase
method setUp (line 13) | protected function setUp(): void
method testGetInvalidCacheKeyThrowsException (line 21) | public function testGetInvalidCacheKeyThrowsException()
method testSetInvalidCacheKeyThrowsException (line 30) | public function testSetInvalidCacheKeyThrowsException()
method testGetNonExistentReturnsNull (line 39) | public function testGetNonExistentReturnsNull()
method testGetNonExistentReturnsCustomDefault (line 47) | public function testGetNonExistentReturnsCustomDefault(): void
method testGetExpiredItemReturnsDefault (line 55) | public function testGetExpiredItemReturnsDefault(): void
method testGetExpiredItemReturnsCustomDefault (line 65) | public function testGetExpiredItemReturnsCustomDefault(): void
method testSetGetBooleanValues (line 75) | public function testSetGetBooleanValues()
method testSetGetZeroTTL (line 87) | public function testSetGetZeroTTL()
method testSetGetNegativeTTL (line 96) | public function testSetGetNegativeTTL()
method testSetZeroTTLWithInvalidKeyThrowsException (line 105) | public function testSetZeroTTLWithInvalidKeyThrowsException()
method testSetNegativeTTLWithInvalidKeyThrowsException (line 114) | public function testSetNegativeTTLWithInvalidKeyThrowsException()
method testSetValidTtlAsAnIntegerReturnsTheSetValue (line 123) | public function testSetValidTtlAsAnIntegerReturnsTheSetValue()
method testSetNullTtlReturnsTheSetValue (line 132) | public function testSetNullTtlReturnsTheSetValue()
method testSetWithDateIntervalTtl (line 141) | public function testSetWithDateIntervalTtl(): void
method testDeletionOfValidRecord (line 150) | public function testDeletionOfValidRecord()
method testDeleteInvalidKeyThrowsException (line 161) | public function testDeleteInvalidKeyThrowsException(): void
method testDeleteNonExistentKeyReturnsTrue (line 170) | public function testDeleteNonExistentKeyReturnsTrue(): void
method testClear (line 178) | public function testClear()
method testGetMultiple (line 190) | public function testGetMultiple(): void
method testSetMultiple (line 208) | public function testSetMultiple(): void
method testDeleteMultiple (line 220) | public function testDeleteMultiple(): void
method testHasReturnsTrueForValidCacheRecord (line 236) | public function testHasReturnsTrueForValidCacheRecord(): void
method testHasReturnsTrueForValidNonNullTtl (line 245) | public function testHasReturnsTrueForValidNonNullTtl(): void
method testHasReturnsFalseForExpiredCacheRecord (line 254) | public function testHasReturnsFalseForExpiredCacheRecord(): void
method testHasReturnsFalseForNonExistentCacheRecord (line 264) | public function testHasReturnsFalseForNonExistentCacheRecord(): void
method testHasThrowsExceptionForInvalidKey (line 272) | public function testHasThrowsExceptionForInvalidKey(): void
method testGetMultipleWithCustomDefault (line 281) | public function testGetMultipleWithCustomDefault(): void
method testSetMultipleReturnsFalseWhenOneFails (line 297) | public function testSetMultipleReturnsFalseWhenOneFails(): void
method testCheckKeyWithInvalidCharactersThrowsException (line 307) | public function testCheckKeyWithInvalidCharactersThrowsException(): void
method testCheckKeyWithSpecialCharsThrowsException (line 316) | public function testCheckKeyWithSpecialCharsThrowsException(): void
method testCheckKeyExceeding64CharsThrowsException (line 325) | public function testCheckKeyExceeding64CharsThrowsException(): void
method testCheckKeyWith64CharsIsValid (line 335) | public function testCheckKeyWith64CharsIsValid(): void
method testEvictExpiredRemovesExpiredItems (line 345) | public function testEvictExpiredRemovesExpiredItems(): void
method testEvictExpiredKeepsNullTtlItems (line 365) | public function testEvictExpiredKeepsNullTtlItems(): void
method testEvictExpiredReturnsZeroWhenNothingToEvict (line 382) | public function testEvictExpiredReturnsZeroWhenNothingToEvict(): void
method testEvictExpiredOnEmptyCache (line 393) | public function testEvictExpiredOnEmptyCache(): void
FILE: tests/MobileDetectExceptionTest.php
class MobileDetectExceptionTest (line 19) | final class MobileDetectExceptionTest extends TestCase
method testIsMobileThrowsExceptionWithCodeWhenNoUserAgent (line 24) | public function testIsMobileThrowsExceptionWithCodeWhenNoUserAgent(): ...
method testIsTabletThrowsExceptionWithCodeWhenNoUserAgent (line 46) | public function testIsTabletThrowsExceptionWithCodeWhenNoUserAgent(): ...
method testIsThrowsExceptionWithCodeWhenNoUserAgent (line 66) | public function testIsThrowsExceptionWithCodeWhenNoUserAgent(): void
method testIsMobileCacheExceptionHasProperCodeAndChain (line 85) | public function testIsMobileCacheExceptionHasProperCodeAndChain(): void
method testIsTabletCacheExceptionHasProperCodeAndChain (line 122) | public function testIsTabletCacheExceptionHasProperCodeAndChain(): void
method testIsMagicCacheExceptionHasProperCodeAndChain (line 151) | public function testIsMagicCacheExceptionHasProperCodeAndChain(): void
method testFullExceptionChainForDebugging (line 183) | public function testFullExceptionChainForDebugging(): void
method testExceptionCodesAreUniqueForCategorization (line 220) | public function testExceptionCodesAreUniqueForCategorization(): void
method testRecommendedExceptionHandlingPattern (line 248) | public function testRecommendedExceptionHandlingPattern(): void
method createFailingCache (line 281) | private function createFailingCache(\Throwable $exceptionToThrow): Cac...
method buildExceptionChain (line 296) | private function buildExceptionChain(\Throwable $exception): array
method getRootCause (line 312) | private function getRootCause(\Throwable $exception): \Throwable
method getErrorTypeFromCode (line 321) | private function getErrorTypeFromCode(int $code): string
FILE: tests/MobileDetectGeneralTest.php
class MobileDetectGeneralTest (line 13) | final class MobileDetectGeneralTest extends TestCase
method testClassExists (line 15) | public function testClassExists()
method testBadMethodCall (line 20) | public function testBadMethodCall()
method testNoUserAgentSetAndAutoInitOfHttpHeadersIsFalse (line 31) | public function testNoUserAgentSetAndAutoInitOfHttpHeadersIsFalse()
method testNoUserAgentSet (line 43) | public function testNoUserAgentSet()
method testEmptyStringAsAUserAgent (line 52) | public function testEmptyStringAsAUserAgent()
method testAutoInitPicksUpKnownHttpHeaders (line 62) | public function testAutoInitPicksUpKnownHttpHeaders()
method testValidHeadersThatDoNotContainHttpUserAgentHeaderButNoUserAgentIsManuallySet (line 75) | public function testValidHeadersThatDoNotContainHttpUserAgentHeaderBut...
method testValidHeadersThatDoNotContainHttpUserAgentHeaderButNoUserAgentIsManuallySetAndAutoInitOfHttpHeadersIsFalse (line 85) | public function testValidHeadersThatDoNotContainHttpUserAgentHeaderBut...
method testValidHeadersThatContainHttpUserAgentHeaderButNoUserAgentIsManuallySet (line 101) | public function testValidHeadersThatContainHttpUserAgentHeaderButNoUse...
method testScriptVersion (line 114) | public function testScriptVersion()
method testBasicMethods (line 126) | public function testBasicMethods()
method headersProvider (line 168) | public function headersProvider(): array
method testHeaders (line 210) | public function testHeaders(array $headers)
method testInvalidHeader (line 233) | public function testInvalidHeader($headers)
method testEmptyHeaders (line 240) | public function testEmptyHeaders()
method userAgentProvider (line 247) | public function userAgentProvider(): array
method testGetUserAgent (line 269) | public function testGetUserAgent($headers, $expectedUserAgent)
method testSetHttpHeaders (line 280) | public function testSetHttpHeaders()
method testSetCfHeaders (line 296) | public function testSetCfHeaders()
method testSetUserAgent (line 333) | public function testSetUserAgent()
method testSetLongUserAgent (line 340) | public function testSetLongUserAgent()
method quickHeadersData (line 349) | public function quickHeadersData(): array
method testQuickHeaders (line 413) | public function testQuickHeaders($headers)
method quickNonMobileHeadersData (line 421) | public function quickNonMobileHeadersData(): array
method testNonMobileQuickHeaders (line 451) | public function testNonMobileQuickHeaders($headers)
method testRules (line 458) | public function testRules()
method testRulesExtended (line 471) | public function testRulesExtended()
FILE: tests/MobileDetectStandaloneTest.php
class MobileDetectStandaloneTest (line 13) | final class MobileDetectStandaloneTest extends TestCase
method testClassExists (line 15) | public function testClassExists()
method testClassWithDefaultCache (line 23) | public function testClassWithDefaultCache(): void
method testClassWithCustomCacheKeyFnInvalidKey (line 35) | public function testClassWithCustomCacheKeyFnInvalidKey(): void
FILE: tests/MobileDetectVersionTest.php
class MobileDetectVersionTest (line 8) | final class MobileDetectVersionTest extends TestCase
method versionDataProvider (line 10) | public function versionDataProvider(): array
method testVersionExtraction (line 55) | public function testVersionExtraction($userAgent, $property, $stringVe...
method crazyVersionNumbers (line 71) | public function crazyVersionNumbers(): array
method testPrepareVersionNo (line 89) | public function testPrepareVersionNo($raw, $expected)
FILE: tests/MobileDetectWithCacheTest.php
class MobileDetectWithCacheTest (line 11) | final class MobileDetectWithCacheTest extends TestCase
method testFlattenHeaders (line 18) | public function testFlattenHeaders()
method testDefaultCacheClassCreatesACacheRecord (line 38) | public function testDefaultCacheClassCreatesACacheRecord()
method testDefaultCacheClassCreatesMultipleCacheRecordsForAllCalls (line 51) | public function testDefaultCacheClassCreatesMultipleCacheRecordsForAll...
method testCustomCacheWithInvalidFnThrowsException (line 86) | public function testCustomCacheWithInvalidFnThrowsException()
method testCustomCacheForConsecutiveCalls (line 100) | public function testCustomCacheForConsecutiveCalls()
method testGetCacheKeyIsUsedInConsecutiveCallsIfFoundIn (line 117) | public function testGetCacheKeyIsUsedInConsecutiveCallsIfFoundIn()
FILE: tests/UserAgentTest.php
class UserAgentTest (line 13) | final class UserAgentTest extends TestCase
method setUp (line 19) | public function setUp(): void
method generateJson (line 24) | public static function generateJson()
method setUpBeforeClass (line 128) | public static function setUpBeforeClass(): void
method userAgentData (line 151) | public function userAgentData(): array
method testUserAgents (line 165) | public function testUserAgents($userAgent, $isMobile, $isTablet, $vers...
method testIsSamsung (line 204) | public function testIsSamsung()
FILE: tests/benchmark/MobileDetectBench.php
class MobileDetectBench (line 14) | final class MobileDetectBench
method benchIsMobileAgainstBestMatch (line 25) | public function benchIsMobileAgainstBestMatch(): void
method benchIsMobileAgainstWorstMatch (line 42) | public function benchIsMobileAgainstWorstMatch(): void
method benchIsTabletAgainstBestMatch (line 58) | public function benchIsTabletAgainstBestMatch(): void
method benchIsTabletAgainstWorstMatch (line 73) | public function benchIsTabletAgainstWorstMatch(): void
method benchIsIOS (line 85) | public function benchIsIOS(): void
method benchIsIpad (line 97) | public function benchIsIpad(): void
method benchIsSamsung (line 109) | public function benchIsSamsung(): void
method benchIsSamsungTablet (line 121) | public function benchIsSamsungTablet(): void
method benchIsMobileCacheKeyFnSha1AgainstBestMatch (line 131) | public function benchIsMobileCacheKeyFnSha1AgainstBestMatch(): void
Condensed preview — 96 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,200K chars).
[
{
"path": ".claude/settings.json",
"chars": 424,
"preview": "{\n \"attribution\": {\n \"commit\": \"\",\n \"pr\": \"\"\n },\n \"permissions\": {\n \"allow\": [\n \"Bash(git:*)\",\n \"B"
},
{
"path": ".editorconfig",
"chars": 127,
"preview": "root = true\n[*]\nend_of_line = lf\ninsert_final_newline = true\n[*.php]\nindent_style = space\nindent_size = 4\nmax_line_lengt"
},
{
"path": ".gitattributes",
"chars": 247,
"preview": "* text=auto eol=lf\n.editorconfig export-ignore\n.gitattributes export-ignore\n/docs/ e"
},
{
"path": ".github/FUNDING.yml",
"chars": 231,
"preview": "github: \"serbanghita\"\ncustom:\n - \"https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=mobiledetectlib%40gmail%"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 483,
"preview": "---\nname: Bug report\nabout: Create a bug report for Mobile Detect\ntitle: \"[bug]\"\nlabels: 'type: bug'\nassignees: serbangh"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.md",
"chars": 552,
"preview": "---\nname: Feature request\nabout: Suggest an idea for Mobile Detect\ntitle: \"[feature]\"\nlabels: feature\nassignees: serbang"
},
{
"path": ".github/docs/index.html",
"chars": 1,
"preview": "\n"
},
{
"path": ".github/workflows/4.8.x-test.yml",
"chars": 1506,
"preview": "name: 4.8.x on PHP 8.x\n\n# Run this workflow every time a new commit pushed to your repository\non:\n # Allows you to run "
},
{
"path": ".github/workflows/website.yml",
"chars": 2017,
"preview": "# Simple workflow for deploying static content to GitHub Pages\nname: website\n\non:\n # Runs on pushes targeting the defau"
},
{
"path": ".gitignore",
"chars": 296,
"preview": "vendor/\nnbproject/\n/*.buildpath\n/*.project\n/.settings\n/error.log\n.idea/\n*.iml\n/coverage\n/phpunit.phar\ncomposer.lock\n/.mo"
},
{
"path": ".php-cs-fixer.php",
"chars": 384,
"preview": "<?php\n\n$finder = PhpCsFixer\\Finder::create()\n ->exclude('docs')\n ->exclude('.github')\n// ->notPath('src/Symfony"
},
{
"path": ".phpcs.xml",
"chars": 784,
"preview": "<?xml version=\"1.0\"?>\n<ruleset xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" name=\"PHP_CodeSniffer\"\n xsi"
},
{
"path": "CHANGELOG.md",
"chars": 4008,
"preview": "# Change log\n\n# 4.8.10\n\n## Fixed\n- [x] `Cache::has()` now properly checks TTL expiration before returning `true` (PSR-16"
},
{
"path": "CLAUDE.md",
"chars": 3497,
"preview": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\nWhen"
},
{
"path": "CNAME",
"chars": 16,
"preview": "mobiledetect.net"
},
{
"path": "CONTRIBUTING.md",
"chars": 3696,
"preview": "# Contributing\nStep-by-step guide to contributing to Mobile Detect library. \\\nBy contributing to Mobile Detect library y"
},
{
"path": "DOCKER-COMPOSE.md",
"chars": 3615,
"preview": "# Docker Compose for Pre-Release Validation\n\nThis document describes the Docker Compose setup for running all necessary "
},
{
"path": "KNOWN_LIMITATIONS.md",
"chars": 1815,
"preview": "**Known limitations**\n\n* Mobile Detect script was designed to detect `mobile` devices. Implicitly other devices are cons"
},
{
"path": "LICENSE",
"chars": 1099,
"preview": "MIT License\n\nCopyright (c) 2021 Şerban Ghiţă, Nick Ilyin and contributors.\n\nPermission is hereby granted, free of charge"
},
{
"path": "MobileDetect.json",
"chars": 38195,
"preview": "{\n \"version\": \"4.8.10\",\n \"headerMatch\": {\n \"HTTP_ACCEPT\": {\n \"matches\": [\n \"appli"
},
{
"path": "README-EXAMPLES.md",
"chars": 7265,
"preview": "# MobileDetect Usage Examples\n\nThis document provides code examples for common MobileDetect usage scenarios.\n\n## Basic U"
},
{
"path": "README.md",
"chars": 4254,
"preview": "\n\nMobileDetect, PHP mobile detection class\n==============="
},
{
"path": "SECURITY.md",
"chars": 486,
"preview": "# Security Policy\n\n## Supported Versions\n\n| Version | Supported |\n|---------| ------------------ |\n| 2.8.x | "
},
{
"path": "composer.json",
"chars": 1226,
"preview": "{\n \"name\": \"mobiledetect/mobiledetectlib\",\n \"type\": \"library\",\n \"description\": \"Mobile_Detect is a lightweight "
},
{
"path": "docker/Dockerfile.setup",
"chars": 144,
"preview": "FROM composer:latest AS build\nWORKDIR /app\nCOPY . .\nCOPY ./docker/build.sh .\nSHELL [\"/bin/bash\", \"-c\"]\nRUN chmod +x buil"
},
{
"path": "docker/build.sh",
"chars": 215,
"preview": "echo \"Start building ...\"\nrm -rf vendor/*\nrm -f composer.lock composer.phar\nset -xe\n# Install composer with dev dependen"
},
{
"path": "docker-compose.yml",
"chars": 2472,
"preview": "services:\n setup:\n build:\n context: .\n dockerfile: ./docker/Dockerfile.setup\n platform: linux/amd64\n "
},
{
"path": "phpbench.json",
"chars": 111,
"preview": "{\n \"$schema\":\"./vendor/phpbench/phpbench/phpbench.schema.json\",\n \"runner.bootstrap\": \"vendor/autoload.php\"\n}\n"
},
{
"path": "scripts/dump_magic_methods.php",
"chars": 379,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nuse Detection\\MobileDetect;\n\nrequire __DIR__ . '/../vendor/autoload.php';\n\n$detect = ne"
},
{
"path": "scripts/example.php",
"chars": 792,
"preview": "<?php\n/**\n * Example using composer's autoloader.\n */\n\nuse Detection\\MobileDetect;\n\nrequire_once __DIR__ . '/../vendor/"
},
{
"path": "scripts/export_to_json.php",
"chars": 2016,
"preview": "<?php\n/**\n * Mobile Detect Library\n * - export -\n * =====================\n *\n * Use the resulting JSON export file in ot"
},
{
"path": "scripts/pre-commit-hook.sh",
"chars": 304,
"preview": "#!/bin/sh\n\n# Linting\nvendor/bin/phpcs\nvendor/bin/phpcbf\n\n# Unit tests\nvendor/bin/phpunit -v -c tests/phpunit.xml --cover"
},
{
"path": "scripts/test.php",
"chars": 1105,
"preview": "<?php\n\nuse Detection\\Exception\\MobileDetectException;\nuse Detection\\MobileDetect;\n\nrequire __DIR__ . '/../vendor/autoloa"
},
{
"path": "scripts/test_standalone.php",
"chars": 430,
"preview": "<?php\nuse Detection\\Exception\\MobileDetectException;\nuse Detection\\MobileDetectStandalone;\n\nrequire_once '../standalone/"
},
{
"path": "src/Cache/Cache.php",
"chars": 5055,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Detection\\Cache;\n\nuse Psr\\SimpleCache\\CacheInterface;\nuse DateInterval;\nuse D"
},
{
"path": "src/Cache/CacheException.php",
"chars": 148,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Detection\\Cache;\n\nclass CacheException extends \\Exception implements \\Psr\\Sim"
},
{
"path": "src/Cache/CacheInvalidArgumentException.php",
"chars": 207,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Detection\\Cache;\n\nuse Psr\\SimpleCache\\InvalidArgumentException;\n\nclass CacheI"
},
{
"path": "src/Exception/MobileDetectException.php",
"chars": 116,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Detection\\Exception;\n\nclass MobileDetectException extends \\Exception\n{\n}\n"
},
{
"path": "src/Exception/MobileDetectExceptionCode.php",
"chars": 261,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Detection\\Exception;\n\nclass MobileDetectExceptionCode\n{\n public const INVA"
},
{
"path": "src/MobileDetect.php",
"chars": 89484,
"preview": "<?php\n\n/**\n * Mobile Detect Library\n * Motto: \"Every business should have a mobile detection script to detect mobile rea"
},
{
"path": "src/MobileDetectStandalone.php",
"chars": 150,
"preview": "<?php\n\nnamespace Detection;\n\nrequire_once dirname(__FILE__) . '/../standalone/autoloader.php';\n\nclass MobileDetectStanda"
},
{
"path": "standalone/autoloader.php",
"chars": 1209,
"preview": "<?php\n$dir = dirname(__FILE__);\n\nspl_autoload_register(function ($class) use ($dir) {\n $classMap = [\n // \"mobi"
},
{
"path": "standalone/deps/simple-cache/.editorconfig",
"chars": 271,
"preview": "; This file is for unifying the coding style for different editors and IDEs.\n; More information at http://editorconfig.o"
},
{
"path": "standalone/deps/simple-cache/.gitattributes",
"chars": 395,
"preview": "# Path-based git attributes\n# https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html\n\n# Ignore all test and"
},
{
"path": "standalone/deps/simple-cache/.gitignore",
"chars": 32,
"preview": "build\ncomposer.lock\ndocs\nvendor\n"
},
{
"path": "standalone/deps/simple-cache/LICENSE.md",
"chars": 1137,
"preview": "# The MIT License (MIT)\n\nCopyright (c) 2016 PHP Framework Interoperability Group\n\n> Permission is hereby granted, free o"
},
{
"path": "standalone/deps/simple-cache/README.md",
"chars": 563,
"preview": "PHP FIG Simple Cache PSR\n========================\n\nThis repository holds all interfaces related to PSR-16.\n\nNote that th"
},
{
"path": "standalone/deps/simple-cache/composer.json",
"chars": 553,
"preview": "{\n \"name\": \"psr/simple-cache\",\n \"description\": \"Common interfaces for simple caching\",\n \"keywords\": [\"psr\", \"ps"
},
{
"path": "standalone/deps/simple-cache/src/CacheException.php",
"chars": 173,
"preview": "<?php\n\nnamespace Psr\\SimpleCache;\n\n/**\n * Interface used for all types of exceptions thrown by the implementing library."
},
{
"path": "standalone/deps/simple-cache/src/CacheInterface.php",
"chars": 4822,
"preview": "<?php\n\nnamespace Psr\\SimpleCache;\n\ninterface CacheInterface\n{\n /**\n * Fetches a value from the cache.\n *\n "
},
{
"path": "standalone/deps/simple-cache/src/InvalidArgumentException.php",
"chars": 260,
"preview": "<?php\n\nnamespace Psr\\SimpleCache;\n\n/**\n * Exception interface for invalid cache arguments.\n *\n * When an invalid argumen"
},
{
"path": "tests/CacheTest.php",
"chars": 10922,
"preview": "<?php\n\nnamespace DetectionTests;\n\nuse DateInterval;\nuse Detection\\Cache\\Cache;\nuse Detection\\Cache\\CacheInvalidArgumentE"
},
{
"path": "tests/MobileDetectExceptionTest.php",
"chars": 12918,
"preview": "<?php\n\nnamespace DetectionTests;\n\nuse Detection\\Cache\\CacheException;\nuse Detection\\Exception\\MobileDetectException;\nuse"
},
{
"path": "tests/MobileDetectGeneralTest.php",
"chars": 16834,
"preview": "<?php\n\nnamespace DetectionTests;\n\nuse Detection\\Exception\\MobileDetectException;\nuse Detection\\MobileDetect;\nuse PHPUnit"
},
{
"path": "tests/MobileDetectStandaloneTest.php",
"chars": 1206,
"preview": "<?php\n\nnamespace DetectionTests;\n\nuse Detection\\Exception\\MobileDetectException;\nuse Detection\\MobileDetectStandalone;\nu"
},
{
"path": "tests/MobileDetectVersionTest.php",
"chars": 3229,
"preview": "<?php\n\nnamespace DetectionTests;\n\nuse Detection\\MobileDetect;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class MobileDetectV"
},
{
"path": "tests/MobileDetectWithCacheTest.php",
"chars": 4292,
"preview": "<?php\n\nnamespace DetectionTests;\n\nuse Detection\\Cache\\Cache;\nuse Detection\\Exception\\MobileDetectException;\nuse Detectio"
},
{
"path": "tests/UserAgentList.inc.php",
"chars": 689,
"preview": "<?php\n\n/**\n * @license MIT License https://github.com/serbanghita/Mobile-Detect/blob/master/LICENSE.txt\n * @link "
},
{
"path": "tests/UserAgentTest.php",
"chars": 6657,
"preview": "<?php\n\nnamespace DetectionTests;\n\nuse Detection\\Exception\\MobileDetectException;\nuse Detection\\MobileDetect;\nuse PHPUnit"
},
{
"path": "tests/benchmark/MobileDetectBench.php",
"chars": 4621,
"preview": "<?php\n\nnamespace DetectionTests\\Benchmark;\n\nuse Detection\\Exception\\MobileDetectException;\nuse Detection\\MobileDetect;\n\n"
},
{
"path": "tests/bootstrap.php",
"chars": 254,
"preview": "<?php\n\nrequire __DIR__ . '/../vendor/autoload.php';\n\nrequire_once dirname(__FILE__) . '/../src/MobileDetect.php';\nrequir"
},
{
"path": "tests/phpunit.xml",
"chars": 839,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phpunit\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n xs"
},
{
"path": "tests/providers/vendors/AOC.php",
"chars": 415,
"preview": "<?php\n\nreturn [\n 'AOC' => [\n 'Mozilla/5.0 (Linux; Android 4.0.4; MW0922 Build/IMM76D) AppleWebKit/537.36 (KHTM"
},
{
"path": "tests/providers/vendors/Acer.php",
"chars": 3331,
"preview": "<?php\n\nreturn [\n 'Acer' => [\n 'Mozilla/5.0 (Linux; U; Android 3.2.1; en-us; A100 Build/HTK55D) AppleWebKit/534"
},
{
"path": "tests/providers/vendors/Alcatel.php",
"chars": 10566,
"preview": "<?php\n\nreturn [\n 'Alcatel' => [\n 'Mozilla/5.0 (Linux; U; Android 2.3.7; en-in; MB525 Build/GWK74; CyanogenMod-"
},
{
"path": "tests/providers/vendors/Allview.php",
"chars": 962,
"preview": " <?php\n return [\n 'Allview' => [\n\n 'Mozilla/5.0 (Linux; U; Android 4.0.4; en-us; ALLVIEW P5 Build/IML74K) Appl"
},
{
"path": "tests/providers/vendors/Amazon.php",
"chars": 4113,
"preview": "<?php\n\nreturn [\n 'Amazon' => [\n 'Mozilla/5.0 (Linux; U; Android 4.0.3; en-us; KFTT Build/IML74K) AppleWebKit/5"
},
{
"path": "tests/providers/vendors/Apple.php",
"chars": 7735,
"preview": "<?php\n\nreturn [\n 'Apple' => [\n 'iTunes/9.1.1' "
},
{
"path": "tests/providers/vendors/Archos.php",
"chars": 5208,
"preview": "<?php\n\nreturn [\n 'Archos' => [\n\n 'Mozilla/5.0 (Linux; Android 6.0; Archos 97c Platinum Build/MRA58K) AppleWebKi"
},
{
"path": "tests/providers/vendors/Asus.php",
"chars": 5906,
"preview": "<?php\n\nreturn [\n 'ASUS' => [\n 'Mozilla/5.0 (Linux; U; Android 3.2.1; en-us; Transformer TF101 Build/HTK75) App"
},
{
"path": "tests/providers/vendors/Blackberry.php",
"chars": 8925,
"preview": "<?php\n\nreturn [\n 'BlackBerry' => [\n 'Mozilla/5.0 (BlackBerry; U; BlackBerry 9300; en) AppleWebKit/534.8+ (KHTM"
},
{
"path": "tests/providers/vendors/Dell.php",
"chars": 1979,
"preview": "<?php\n\nreturn [\n 'Dell' => [\n 'Mozilla/5.0 (Linux; U; Android 1.6; en-gb; Dell Streak Build/Donut AppleWeb"
},
{
"path": "tests/providers/vendors/Google.php",
"chars": 4775,
"preview": "<?php\n\nreturn [\n 'Google' => [\n 'Mozilla/5.0 (Linux; U; Android 2.2; en-us; Nexus One Build/FRF91) AppleWebKit"
},
{
"path": "tests/providers/vendors/HP.php",
"chars": 1694,
"preview": "<?php\n\nreturn [\n 'HP' => [\n 'Mozilla/5.0 (hp-tablet; Linux; hpwOS/3.0.5; U; en-GB) AppleWebKit/534.6 (KHTML, l"
},
{
"path": "tests/providers/vendors/HTC.php",
"chars": 74616,
"preview": "<?php\n\nreturn [\n 'HTC' => [\n 'Mozilla/5.0 (X11; Linux x86_64; Z520m; en-ca) AppleWebKit/534.24 (KHTML, lik"
},
{
"path": "tests/providers/vendors/Huawei.php",
"chars": 7152,
"preview": "<?php\n\nreturn [\n 'Huawei' => [\n 'Mozilla/5.0 (Linux; U; Android 2.1-update1; bg-bg; Ideos S7 Build/ERE27) Appl"
},
{
"path": "tests/providers/vendors/LG.php",
"chars": 6623,
"preview": "<?php\n\nreturn [\n 'LG' => [\n 'Mozilla/5.0 (Linux; U; Android 2.3.6; en-us; LG-VS410PP Build/GRK39F) AppleWebKit"
},
{
"path": "tests/providers/vendors/Lava.php",
"chars": 3469,
"preview": "<?php\n\nreturn [\n 'Lava' => [\n 'Mozilla/5.0 (Linux; U; Android 2.3.6; en-us; Iris 349 Build/MocorDroid2.3.5) Ap"
},
{
"path": "tests/providers/vendors/Leader.php",
"chars": 221,
"preview": "<?php\n\nreturn [\n [\n 'Mozilla/5.0 (Linux; U; Android 4.2.2; en-au; TBLT10Q-32GB Build/JDQ39) AppleWebKit/534.30"
},
{
"path": "tests/providers/vendors/Lenovo.php",
"chars": 14760,
"preview": "<?php\n\nreturn [\n 'Lenovo' => [\n\n 'Mozilla/5.0 (Linux; U; Android 4.0.4; es-es; IdeaTab_A1107 Build/MR1) AppleW"
},
{
"path": "tests/providers/vendors/Mi.php",
"chars": 4705,
"preview": "<?php\n\nreturn [\n 'Mi' => [\n 'Mozilla/5.0 (Linux; U; Android 4.2; xx-xx; HM NOTE 1W Build/JDQ39) AppleWebKit/53"
},
{
"path": "tests/providers/vendors/Microsoft.php",
"chars": 12704,
"preview": "<?php\n\nreturn [\n 'Microsoft' => [\n // @See https://github.com/serbanghita/Mobile-Detect/issues/564\n 'Mo"
},
{
"path": "tests/providers/vendors/Motorola.php",
"chars": 10081,
"preview": "<?php\n\nreturn [\n 'Motorola' => [\n 'MOT-W510/08.11.05R MIB/BER2.2 Profile/MIDP-2.0 Configuration/CLDC-1.1 EGE/1"
},
{
"path": "tests/providers/vendors/Mpman.php",
"chars": 609,
"preview": "<?php\n\nreturn [\n 'Mpman' => [\n 'Mozilla/5.0 (Linux; U; Android 4.2.2; en-us; MPDC703 Build/JDQ39) AppleWebKit/"
},
{
"path": "tests/providers/vendors/Nexus.php",
"chars": 3287,
"preview": "<?php\n\nreturn [\n 'Nexus' => [\n // Tablets\n 'Mozilla/5.0 (Linux; Android 6.0; Nexus 9 Build/MRA58N) Appl"
},
{
"path": "tests/providers/vendors/Nokia.php",
"chars": 21626,
"preview": "<?php\n\nreturn [\n 'Nokia' => [\n 'Nokia200/2.0 (12.04) Profile/MIDP-2.1 Configuration/CLDC-1.1 UCWEB/2.0 (Java; "
},
{
"path": "tests/providers/vendors/Onda.php",
"chars": 1304,
"preview": "<?php\n\nreturn [\n 'Onda' => [\n 'Mozilla/5.0 (Linux; Android 4.2.2; V975i Build/JDQ39) AppleWebKit/537.36 (KHTML"
},
{
"path": "tests/providers/vendors/Others.php",
"chars": 52477,
"preview": "<?php\n\nreturn [\n\n 'AdvanDigital' => [\n 'Mozilla/5.0 (Linux; U; Android 4.2.2; en-us; E1C Build/JDQ39) AppleWeb"
},
{
"path": "tests/providers/vendors/Prestigio.php",
"chars": 1524,
"preview": "<?php\n\nreturn [\n 'Prestigio' => [\n 'Mozilla/5.0 (Linux; U; Android 4.2.2; en-gb; PMP5297C_QUAD Build/JDQ39) Ap"
},
{
"path": "tests/providers/vendors/Samsung.php",
"chars": 68967,
"preview": "<?php\n\nreturn [\n 'Samsung' => [\n 'MQQBrowser/4.0/Mozilla/5.0 (Linux; U; Android 3.2; zh-cn; GT-P6800 Build/HTJ"
},
{
"path": "tests/providers/vendors/Sony.php",
"chars": 19418,
"preview": "<?php\n\nreturn [\n 'Sony' => [\n 'SonyEricssonK800i/R1AA Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC"
},
{
"path": "tests/providers/vendors/SpecialCases.php",
"chars": 27422,
"preview": "<?php\n\nreturn [\n 'Avant' => [\n 'Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; Avant Browser; rv:11.0) like "
},
{
"path": "tests/providers/vendors/Verizon.php",
"chars": 1254,
"preview": "<?php\n\nreturn [\n 'Verizon' => [\n 'Mozilla/5.0 (Linux; Android 5.1.1; QTAQZ3 Build/LMY47V) AppleWebKit/537.36 ("
},
{
"path": "tests/providers/vendors/Vodafone.php",
"chars": 1142,
"preview": "<?php\n\nreturn [\n 'Vodafone' => [\n 'Mozilla/5.0 (Linux; U; Android 3.2; hu-hu; SmartTab10-MSM8260-V02d-Dec02201"
},
{
"path": "tests/providers/vendors/ZTE.php",
"chars": 1272,
"preview": "<?php\n\nreturn [\n 'ZTE' => [\n 'Mozilla/5.0 (Linux; Android 4.0.4; V8200plus Build/IMM76I) AppleWebKit/537.36 (K"
},
{
"path": "tests/ualist.json",
"chars": 499123,
"preview": "{\n \"hash\": \"fed28bf03dc9f17c68fc4c5dec785c2bbf86f320\",\n \"user_agents\": [\n {\n \"vendor\": \"Lava\",\n "
}
]
About this extraction
This page contains the full source code of the serbanghita/Mobile-Detect GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 96 files (1.1 MB), approximately 373.2k tokens, and a symbol index with 182 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.