├── README.md ├── composer.json ├── docs └── .gitkeep ├── init.php ├── lib ├── AutomileClient.php ├── Config.php ├── Endpoints │ ├── Contact.php │ ├── Device.php │ ├── DeviceEvent.php │ ├── ExpenseReport.php │ ├── Fleet.php │ ├── FleetContact.php │ ├── Geofence.php │ ├── Notification.php │ ├── NotificationMessage.php │ ├── Place.php │ ├── PublishSubscribe.php │ ├── Report.php │ ├── SignUp.php │ ├── Task.php │ ├── TaskMessage.php │ ├── Trip.php │ ├── Vehicle.php │ ├── VehicleGeofence.php │ ├── VehicleHealth.php │ ├── VehicleInspection.php │ └── VehiclePlace.php ├── Exceptions │ ├── AutomileException.php │ └── InvalidArgumentException.php ├── HttpClient │ ├── Client │ │ ├── ClientInterface.php │ │ ├── Curl.php │ │ └── CurlAdapter.php │ ├── HttpClientException.php │ ├── Request │ │ ├── HttpRequest.php │ │ └── RequestInterface.php │ └── Response │ │ ├── JsonResponse.php │ │ └── ResponseInterface.php ├── Models │ ├── AmbientAirTemperature.php │ ├── AmbientAirTemperatureRowset.php │ ├── Company.php │ ├── CompanyContact.php │ ├── CompanyContactRowset.php │ ├── CompanyRowset.php │ ├── Contact.php │ ├── ContactRowset.php │ ├── Device.php │ ├── DeviceDtc.php │ ├── DeviceDtcRowset.php │ ├── DeviceEvent.php │ ├── DeviceEventDtc.php │ ├── DeviceEventMil.php │ ├── DeviceEventRowset.php │ ├── DeviceEventStatus.php │ ├── DeviceRowset.php │ ├── ExpenseReport.php │ ├── ExpenseReportEmail.php │ ├── ExpenseReportRow.php │ ├── ExpenseReportRowContent.php │ ├── ExpenseReportRowContentRowset.php │ ├── ExpenseReportRowRowset.php │ ├── ExpenseReportRowset.php │ ├── ExpenseReportsEmail.php │ ├── Geofence.php │ ├── GeofencePolygon.php │ ├── GeofenceReport.php │ ├── GeofenceReportRecord.php │ ├── GeofenceReportRecordRowset.php │ ├── GeofenceRowset.php │ ├── GeographicPosition.php │ ├── ModelAbstract.php │ ├── ModelException.php │ ├── ModelRowsetAbstract.php │ ├── ModelValidatorException.php │ ├── Place.php │ ├── PlaceRowset.php │ ├── PublishSubscribe.php │ ├── PublishSubscribeAuthentication │ │ ├── AuthenticationAbstract.php │ │ ├── Basic.php │ │ ├── Bearer.php │ │ └── Salesforce.php │ ├── PublishSubscribeRowset.php │ ├── Report │ │ ├── CO2.php │ │ ├── CO2Rowset.php │ │ ├── Distance.php │ │ ├── DistanceRowset.php │ │ ├── Fuel.php │ │ ├── FuelRowset.php │ │ ├── IdleTime.php │ │ ├── IdleTimeRowset.php │ │ ├── TravelTime.php │ │ ├── TravelTimeRowset.php │ │ ├── TripEmail.php │ │ ├── TripSummary.php │ │ ├── TripSummaryRowset.php │ │ └── VehicleSummary.php │ ├── Task.php │ ├── TaskMessage.php │ ├── TaskMessageRowset.php │ ├── TaskRowset.php │ ├── Trigger.php │ ├── TriggerMessageHistory.php │ ├── TriggerMessageHistoryRowset.php │ ├── TriggerMute.php │ ├── TriggerRowset.php │ ├── Trip.php │ ├── TripConcatenation.php │ ├── TripGeo.php │ ├── TripGeoRowset.php │ ├── TripNote.php │ ├── TripPID.php │ ├── TripPIDRowset.php │ ├── TripRowset.php │ ├── TripStartEndGeo.php │ ├── TripSynchronized.php │ ├── User.php │ └── Vehicle │ │ ├── BatteryEvent.php │ │ ├── BatteryEventRowset.php │ │ ├── CheckIn.php │ │ ├── Defect.php │ │ ├── DefectAttachment.php │ │ ├── DefectAttachmentRowset.php │ │ ├── DefectComment.php │ │ ├── DefectCommentRowset.php │ │ ├── DefectRowset.php │ │ ├── DefectStatus.php │ │ ├── DefectStatusRowset.php │ │ ├── DtcEvent.php │ │ ├── DtcEventDetail.php │ │ ├── DtcEventDetailRowset.php │ │ ├── EngineCoolantTemperature.php │ │ ├── EngineCoolantTemperatureRowset.php │ │ ├── FuelLevelInput.php │ │ ├── FuelLevelInputRowset.php │ │ ├── Geofence.php │ │ ├── GeofenceRowset.php │ │ ├── Health.php │ │ ├── Inspection.php │ │ ├── InspectionExport.php │ │ ├── InspectionRowset.php │ │ ├── InspectionStatus.php │ │ ├── InspectionStatusRowset.php │ │ ├── MilEvent.php │ │ ├── MilEventRowset.php │ │ ├── Place.php │ │ ├── PlaceRowset.php │ │ ├── RPM.php │ │ ├── RPMRowset.php │ │ ├── Speed.php │ │ ├── SpeedRowset.php │ │ ├── Status.php │ │ ├── StatusRowset.php │ │ ├── Tiny.php │ │ ├── Vehicle2.php │ │ └── Vehicle2Rowset.php ├── OAuth │ ├── Http.php │ ├── OAuthException.php │ └── Token.php ├── Storage │ ├── Filesystem.php │ ├── StorableInterface.php │ ├── StorageException.php │ └── StorageInterface.php └── Types │ ├── AttachmentType.php │ ├── BatteryEventStatusType.php │ ├── ContentType.php │ ├── DatePeriod.php │ ├── DestinationType.php │ ├── DeviceType.php │ ├── FuelType.php │ ├── GeofenceType.php │ ├── PublishSubscribeAuthenticationType.php │ ├── PublishType.php │ ├── TriggerType.php │ ├── TripType.php │ ├── TripTypeTriggerType.php │ ├── Type.php │ ├── VehicleDefectStatusType.php │ └── VehicleDefectType.php ├── phpdoc.dist.xml └── tests ├── Bootstrap.php ├── Functional ├── .gitignore ├── Endpoints │ ├── ReportTest.php │ ├── TripTest.php │ ├── VehicleInspectionTest.php │ └── VehicleTest.php ├── TestAbstract.php ├── TestException.php ├── config.sample.json └── data │ └── .gitignore ├── Integration └── HttpClient │ ├── CurlAdapterMock.php │ └── CurlTest.php ├── Unit ├── HttpClient │ ├── HttpRequestTest.php │ └── JsonResponseTest.php ├── Models │ ├── ModelMock.php │ └── ModelTest.php ├── OAuth │ └── TokenTest.php └── Storage │ ├── FilesystemTest.php │ └── StorableMock.php └── phpunit.xml /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "automile/automile-php", 3 | "description": "Automile API SDK", 4 | "type": "library", 5 | "keywords": ["php", "sdk", "api"], 6 | "homepage": "https://github.com/Automile/automile-php", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Automile", 11 | "email": "support@automile.com", 12 | "homepage": "https://automile.com" 13 | } 14 | ], 15 | "require": { 16 | "php": ">=5.4", 17 | "ext-curl": "*" 18 | }, 19 | "autoload": { 20 | "psr-4": { "Automile\\Sdk\\" : "lib/" } 21 | }, 22 | "scripts": { 23 | "test": [ 24 | "cd tests && ../../vendor/bin/phpunit --testsuite unit", 25 | "cd tests && ../../vendor/bin/phpunit --testsuite integration" 26 | ], 27 | "ftest": [ 28 | "cd tests && ../../vendor/bin/phpunit --testsuite functional" 29 | ] 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /docs/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automile/automile-php/0828bc9027e1b612de135ae3d2d81dcfbd1914aa/docs/.gitkeep -------------------------------------------------------------------------------- /init.php: -------------------------------------------------------------------------------- 1 | _getAll($this->_contactsUri, new ContactRowset()); 25 | } 26 | 27 | /** 28 | * Get a contact by id 29 | * @param int $contactId 30 | * @return ContactModel 31 | */ 32 | public function getContactById($contactId) 33 | { 34 | return $this->_getById($this->_contactsUri, $contactId, new ContactModel()); 35 | } 36 | 37 | /** 38 | * Get the contact representing myself 39 | * @return ContactModel 40 | * @throws AutomileException 41 | */ 42 | public function getMe() 43 | { 44 | $request = Config::getNewRequest(); 45 | $response = Config::getNewResponse(); 46 | $client = Config::getNewHttpClient(); 47 | 48 | $this->_authorizeRequest($request); 49 | 50 | $request->setMethod(Config::METHOD_GET) 51 | ->setUri($this->_contactsUri . '/me'); 52 | 53 | $isSuccessful = $client->send($request, $response); 54 | 55 | if ($isSuccessful) { 56 | return new ContactModel($response->getBody()); 57 | } 58 | 59 | throw new AutomileException($response->getErrorMessage()); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /lib/Endpoints/Device.php: -------------------------------------------------------------------------------- 1 | _getAll($this->_deviceUri, new DeviceRowset()); 24 | } 25 | 26 | /** 27 | * Get details for the device 28 | * @param $id 29 | * @return DeviceModel 30 | */ 31 | public function getDeviceById($id) 32 | { 33 | return $this->_getById($this->_deviceUri, $id, new DeviceModel()); 34 | } 35 | 36 | /** 37 | * Creates a new IMEIConfig and associates it with vehicle 38 | * @param DeviceModel $device 39 | * @return DeviceModel 40 | */ 41 | public function createDevice(DeviceModel $device) 42 | { 43 | return $this->_create($this->_deviceUri, $device); 44 | } 45 | 46 | /** 47 | * Updates the given IMEIConfig id 48 | * @param DeviceModel $device 49 | * @return DeviceModel 50 | * @throws AutomileException 51 | */ 52 | public function editDevice(DeviceModel $device) 53 | { 54 | if (!$device->getIMEIConfigId()) { 55 | throw new AutomileException('Device ID is empty'); 56 | } 57 | 58 | return $this->_edit($this->_deviceUri, $device->getIMEIConfigId(), $device); 59 | } 60 | 61 | /** 62 | * Removes the given IMEI config 63 | * @param int $deviceId 64 | * @return bool 65 | */ 66 | public function deleteDevice($deviceId) 67 | { 68 | return $this->_delete($this->_deviceUri, $deviceId); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /lib/Endpoints/DeviceEvent.php: -------------------------------------------------------------------------------- 1 | _getAll($this->_deviceEventUri, new DeviceEventRowset()); 26 | } 27 | 28 | /** 29 | * Get a specific MIL (Mileage Indicator Lamp) event 30 | * @param int $deviceEventId 31 | * @return DeviceEventMil 32 | */ 33 | public function getDeviceEventMILById($deviceEventId) 34 | { 35 | return $this->_getById($this->_deviceEventUri . '/mil', $deviceEventId, new DeviceEventMil()); 36 | } 37 | 38 | /** 39 | * Get a specific DTC (Diagnostic Trouble Code) event 40 | * @param int $deviceEventId 41 | * @return DeviceEventDtc 42 | */ 43 | public function getDeviceEventDTCById($deviceEventId) 44 | { 45 | return $this->_getById($this->_deviceEventUri . '/dtc', $deviceEventId, new DeviceEventDtc()); 46 | } 47 | 48 | /** 49 | * Gets StatusIMEIevent for given imeiEventId 50 | * @param int $deviceEventId 51 | * @return DeviceEventStatus 52 | */ 53 | public function getDeviceEventStatusById($deviceEventId) 54 | { 55 | return $this->_getById($this->_deviceEventUri . '/status', $deviceEventId, new DeviceEventStatus()); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /lib/Endpoints/ExpenseReport.php: -------------------------------------------------------------------------------- 1 | _getAll($this->_expenseReportUri, new ExpenseReportRowset()); 27 | } 28 | 29 | /** 30 | * Get the details about a specific expense report 31 | * @param int $id 32 | * @return ExpenseReportModel 33 | */ 34 | public function getExpenseReportById($id) 35 | { 36 | return $this->_getById($this->_expenseReportUri, $id, new ExpenseReportModel()); 37 | } 38 | 39 | /** 40 | * Create an expense report 41 | * @param ExpenseReportModel $model 42 | * @return ExpenseReportModel 43 | */ 44 | public function createExpenseReport(ExpenseReportModel $model) 45 | { 46 | return $this->_create($this->_expenseReportUri, $model); 47 | } 48 | 49 | /** 50 | * Updates the given expense report 51 | * @param ExpenseReportModel $model 52 | * @return ExpenseReportModel 53 | */ 54 | public function editExpenseReport(ExpenseReportModel $model) 55 | { 56 | if (!$model->getExpenseReportId()) { 57 | throw new AutomileException('Expense Report ID is empty'); 58 | } 59 | 60 | return $this->_edit($this->_expenseReportUri, $model->getExpenseReportId(), $model); 61 | } 62 | 63 | /** 64 | * @param int $expenseReportId 65 | * @param ExpenseReportEmail $model 66 | * @return bool 67 | * @throws AutomileException 68 | */ 69 | public function emailExpenseReport($expenseReportId, ExpenseReportEmail $model) 70 | { 71 | $request = Config::getNewRequest(); 72 | $response = Config::getNewResponse(); 73 | $client = Config::getNewHttpClient(); 74 | 75 | $this->_authorizeRequest($request); 76 | 77 | $request->setMethod(Config::METHOD_POST) 78 | ->setUri($this->_expenseReportUri . '/export/' . $expenseReportId) 79 | ->setBody($model->toJson()) 80 | ->setContentType('application/json'); 81 | 82 | $isSuccessful = $client->send($request, $response); 83 | 84 | if ($isSuccessful) { 85 | return true; 86 | } 87 | 88 | $errorMessage = $response->getErrorMessage(); 89 | throw new AutomileException($errorMessage ?: "Error code: {$response->getStatusCode()}"); 90 | } 91 | 92 | /** 93 | * @param ExpenseReportsEmail $model 94 | * @return bool 95 | * @throws AutomileException 96 | */ 97 | public function emailExpenseReports(ExpenseReportsEmail $model) 98 | { 99 | $request = Config::getNewRequest(); 100 | $response = Config::getNewResponse(); 101 | $client = Config::getNewHttpClient(); 102 | 103 | $this->_authorizeRequest($request); 104 | 105 | $request->setMethod(Config::METHOD_POST) 106 | ->setUri($this->_expenseReportUri . '/export/') 107 | ->setBody($model->toJson()) 108 | ->setContentType('application/json'); 109 | 110 | $isSuccessful = $client->send($request, $response); 111 | 112 | if ($isSuccessful) { 113 | return true; 114 | } 115 | 116 | $errorMessage = $response->getErrorMessage(); 117 | throw new AutomileException($errorMessage ?: "Error code: {$response->getStatusCode()}"); 118 | } 119 | 120 | /** 121 | * @param int $id 122 | * @return bool 123 | */ 124 | public function deleteExpenseReport($id) 125 | { 126 | return $this->_delete($this->_expenseReportUri, $id); 127 | } 128 | 129 | } 130 | -------------------------------------------------------------------------------- /lib/Endpoints/Fleet.php: -------------------------------------------------------------------------------- 1 | _getAll($this->_fleetUri, new CompanyRowset()); 24 | } 25 | 26 | /** 27 | * Get a company by id 28 | * @param int $id 29 | * @return Company 30 | */ 31 | public function getFleetById($id) 32 | { 33 | return $this->_getById($this->_fleetUri, $id, new Company()); 34 | } 35 | 36 | /** 37 | * Creates a new company and associates it with the user 38 | * @param Company $company 39 | * @return Company 40 | */ 41 | public function createFleet(Company $company) 42 | { 43 | return $this->_create($this->_fleetUri, $company); 44 | } 45 | 46 | /** 47 | * Updates the given company id 48 | * @param Company $company 49 | * @return Company 50 | * @throws AutomileException 51 | */ 52 | public function editFleet(Company $company) 53 | { 54 | if (!$company->getCompanyId()) { 55 | throw new AutomileException('Company ID is empty'); 56 | } 57 | 58 | return $this->_edit($this->_fleetUri, $company->getCompanyId(), $company); 59 | } 60 | 61 | /** 62 | * Removes the given company 63 | * @param int $id 64 | * @return bool 65 | */ 66 | public function deleteFleet($id) 67 | { 68 | return $this->_delete($this->_fleetUri, $id); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /lib/Endpoints/FleetContact.php: -------------------------------------------------------------------------------- 1 | _getById($this->_fleetContactUri, $fleetContactId, new CompanyContact()); 26 | } 27 | 28 | /** 29 | * Get a list of all company contacts that user is associated with 30 | * @return CompanyContactRowset 31 | */ 32 | public function getFleetContacts() 33 | { 34 | return $this->_getAll($this->_fleetContactUri, new CompanyContactRowset()); 35 | } 36 | 37 | /** 38 | * Get all relationships between a specific fleet and it's contacts 39 | * @param int $fleetId 40 | * @return CompanyContactRowset 41 | * @throws AutomileException 42 | */ 43 | public function getFleetContactsByFleetId($fleetId) 44 | { 45 | $request = Config::getNewRequest(); 46 | $response = Config::getNewResponse(); 47 | $client = Config::getNewHttpClient(); 48 | 49 | $this->_authorizeRequest($request); 50 | 51 | $request->setMethod(Config::METHOD_GET) 52 | ->setUri($this->_fleetContactUri) 53 | ->setUriParam('companyId', $fleetId); 54 | 55 | $isSuccessful = $client->send($request, $response); 56 | 57 | if ($isSuccessful) { 58 | return new CompanyContactRowset($response->getBody()); 59 | } 60 | 61 | throw new AutomileException($response->getErrorMessage()); 62 | } 63 | 64 | /** 65 | * Creates a new company contact and associates it with user 66 | * @param CompanyContact $contact 67 | * @return CompanyContact 68 | */ 69 | public function createFleetContact(CompanyContact $contact) 70 | { 71 | return $this->_create($this->_fleetContactUri, $contact); 72 | } 73 | 74 | /** 75 | * Updates the given companycontact id 76 | * @param CompanyContact $contact 77 | * @return CompanyContact 78 | * @throws AutomileException 79 | */ 80 | public function editFleetContact(CompanyContact $contact) 81 | { 82 | if (!$contact->getCompanyContactId()) { 83 | throw new AutomileException('Fleet Contact ID is missing'); 84 | } 85 | 86 | return $this->_edit($this->_fleetContactUri, $contact->getCompanyContactId(), $contact); 87 | } 88 | 89 | /** 90 | * Removes the given company contact 91 | * @param int $id 92 | * @return mixed 93 | */ 94 | public function deleteFleetContact($id) 95 | { 96 | return $this->_delete($this->_fleetContactUri, $id); 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /lib/Endpoints/Geofence.php: -------------------------------------------------------------------------------- 1 | _getAll($this->_geofenceUri, new GeofenceRowset()); 24 | } 25 | 26 | /** 27 | * Get geofence 28 | * @param int $geofenceId 29 | * @return GeofenceModel 30 | */ 31 | public function getGeofenceById($geofenceId) 32 | { 33 | return $this->_getById($this->_geofenceUri, $geofenceId, new GeofenceModel()); 34 | } 35 | 36 | /** 37 | * Creates a new geofence 38 | * @param GeofenceModel $geofence 39 | * @return GeofenceModel 40 | */ 41 | public function createGeofence(GeofenceModel $geofence) 42 | { 43 | return $this->_create($this->_geofenceUri, $geofence); 44 | } 45 | 46 | /** 47 | * Updates the given geofence with new model 48 | * @param GeofenceModel $geofence 49 | * @return GeofenceModel 50 | * @throws AutomileException 51 | */ 52 | public function editGeofence(GeofenceModel $geofence) 53 | { 54 | if (!$geofence->getGeofenceId()) { 55 | throw new AutomileException('Geofence ID is empty'); 56 | } 57 | 58 | return $this->_edit($this->_geofenceUri, $geofence->getGeofenceId(), $geofence); 59 | } 60 | 61 | /** 62 | * Removes the given geofence 63 | * @param int $geofenceId 64 | * @return bool 65 | */ 66 | public function deleteGeofence($geofenceId) 67 | { 68 | return $this->_delete($this->_geofenceUri, $geofenceId); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /lib/Endpoints/NotificationMessage.php: -------------------------------------------------------------------------------- 1 | _getAll($this->_notificationMessagesUri, new TriggerMessageHistoryRowset()); 24 | } 25 | 26 | /** 27 | * Get trigger messages by trigger id 28 | * @param $notificationId 29 | * @return TriggerMessageHistoryRowset 30 | * @throws AutomileException 31 | */ 32 | public function getNotificationMessagesByNotificationId($notificationId) 33 | { 34 | $request = Config::getNewRequest(); 35 | $response = Config::getNewResponse(); 36 | $client = Config::getNewHttpClient(); 37 | 38 | $this->_authorizeRequest($request); 39 | 40 | $request->setMethod(Config::METHOD_GET) 41 | ->setUri($this->_notificationMessagesUri . '/' . (int)$notificationId); 42 | 43 | $isSuccessful = $client->send($request, $response); 44 | 45 | if ($isSuccessful) { 46 | return new TriggerMessageHistoryRowset($response->getBody()); 47 | } 48 | 49 | throw new AutomileException($response->getErrorMessage()); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /lib/Endpoints/Place.php: -------------------------------------------------------------------------------- 1 | _getAll($this->_placeUri, new PlaceRowset()); 24 | } 25 | 26 | /** 27 | * Get place 28 | * @param int $placeId 29 | * @return PlaceModel 30 | */ 31 | public function getPlaceById($placeId) 32 | { 33 | return $this->_getById($this->_placeUri, $placeId, new PlaceModel()); 34 | } 35 | 36 | /** 37 | * Creates a new place 38 | * @param PlaceModel $place 39 | * @return PlaceModel 40 | * @throws AutomileException 41 | */ 42 | public function createPlace(PlaceModel $place) 43 | { 44 | if (!$place->isValid()) { 45 | throw new AutomileException("Model is invalid"); 46 | } 47 | 48 | return $this->_create($this->_placeUri, $place); 49 | } 50 | 51 | /** 52 | * Updates the given place with new model 53 | * @param PlaceModel $place 54 | * @return PlaceModel 55 | * @throws AutomileException 56 | */ 57 | public function editPlace(PlaceModel $place) 58 | { 59 | if (!$place->getPlaceId()) { 60 | throw new AutomileException('Place ID is empty'); 61 | } 62 | 63 | if (!$place->isValid()) { 64 | throw new AutomileException("Model is invalid"); 65 | } 66 | 67 | return $this->_edit($this->_placeUri, $place->getPlaceId(), $place); 68 | } 69 | 70 | /** 71 | * Removes the given company 72 | * @param $placeId 73 | * @return bool 74 | */ 75 | public function deletePlace($placeId) 76 | { 77 | return $this->_delete($this->_placeUri, $placeId); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /lib/Endpoints/SignUp.php: -------------------------------------------------------------------------------- 1 | setUri(self::$_signUpUri) 33 | ->setMethod(Config::METHOD_POST) 34 | ->setContentType('application/x-www-form-urlencoded') 35 | ->setPostParam('email', $email); 36 | 37 | $isSuccessful = $client->send($request, $response); 38 | 39 | if ($isSuccessful) { 40 | return new User($response->getBody()); 41 | } 42 | 43 | throw new AutomileException($response->getErrorMessage()); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /lib/Endpoints/Task.php: -------------------------------------------------------------------------------- 1 | _getAll($this->_taskUri, new TaskRowset()); 24 | } 25 | 26 | /** 27 | * Get task details 28 | * @param $taskId 29 | * @return TaskModel 30 | */ 31 | public function getByTaskId($taskId) 32 | { 33 | return $this->_getById($this->_taskUri, $taskId, new TaskModel()); 34 | } 35 | 36 | /** 37 | * Create a task 38 | * @param TaskModel $task 39 | * @return TaskModel 40 | */ 41 | public function createTask(TaskModel $task) 42 | { 43 | return $this->_create($this->_taskUri, $task); 44 | } 45 | 46 | /** 47 | * Update a task 48 | * @param TaskModel $task 49 | * @return TaskModel 50 | * @throws AutomileException 51 | */ 52 | public function editTask(TaskModel $task) 53 | { 54 | if (!$task->getTaskId()) { 55 | throw new AutomileException("Task ID is missing"); 56 | } 57 | 58 | return $this->_edit($this->_taskUri, $task->getTaskId(), $task); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /lib/Endpoints/TaskMessage.php: -------------------------------------------------------------------------------- 1 | _getById($this->_taskMessageUri, $taskMessageId, new TaskMessageModel()); 25 | } 26 | 27 | /** 28 | * Create a task message 29 | * @param TaskMessageModel $taskMessage 30 | * @return TaskMessageModel 31 | */ 32 | public function createTaskMessage(TaskMessageModel $taskMessage) 33 | { 34 | return $this->_create($this->_taskMessageUri, $taskMessage); 35 | } 36 | 37 | /** 38 | * Mark a task message as read/unread 39 | * @param int $taskMessageId 40 | * @param bool $isRead true to mark as read, false to mark as unread 41 | * @return bool 42 | * @throws AutomileException 43 | */ 44 | public function markReadTaskMessage($taskMessageId, $isRead) 45 | { 46 | $request = Config::getNewRequest(); 47 | $response = Config::getNewResponse(); 48 | $client = Config::getNewHttpClient(); 49 | 50 | $this->_authorizeRequest($request); 51 | 52 | $request->setMethod(Config::METHOD_PUT) 53 | ->setUri($this->_taskMessageUri . '/' . (int)$taskMessageId) 54 | ->setBody(json_encode(['isRead' => (bool)$isRead])) 55 | ->setContentType('application/json'); 56 | 57 | $isSuccessful = $client->send($request, $response); 58 | 59 | if ($isSuccessful) { 60 | return true; 61 | } 62 | 63 | $errorMessage = $response->getErrorMessage(); 64 | throw new AutomileException($errorMessage ?: "Error code: {$response->getStatusCode()}"); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /lib/Endpoints/Vehicle.php: -------------------------------------------------------------------------------- 1 | _getAll($this->_vehicleUri, new Vehicle2Rowset()); 27 | } 28 | 29 | /** 30 | * Get the details about the vehicle 31 | * @param int $id 32 | * @return Vehicle2 33 | */ 34 | public function getVehicleById($id) 35 | { 36 | return $this->_getById($this->_vehicleUri, $id, new Vehicle2()); 37 | } 38 | 39 | /** 40 | * Get position and status of all vehicles that the user has access to 41 | * @return StatusRowset 42 | */ 43 | public function getStatusForVehicles() 44 | { 45 | return $this->_getAll($this->_vehicleUri . '/status', new StatusRowset()); 46 | } 47 | 48 | /** 49 | * Check-in to a vehicle 50 | * @param CheckIn $checkIn 51 | * @return bool 52 | * @throws AutomileException 53 | */ 54 | public function checkInToVehicle(CheckIn $checkIn) 55 | { 56 | $request = Config::getNewRequest(); 57 | $response = Config::getNewResponse(); 58 | $client = Config::getNewHttpClient(); 59 | 60 | $this->_authorizeRequest($request); 61 | 62 | $request->setMethod(Config::METHOD_POST) 63 | ->setUri($this->_vehicleUri . '/checkin') 64 | ->setBody($checkIn->toJson()) 65 | ->setContentType('application/json'); 66 | 67 | $isSuccessful = $client->send($request, $response); 68 | 69 | if ($isSuccessful) { 70 | return true; 71 | } 72 | 73 | $errorMessage = $response->getErrorMessage(); 74 | throw new AutomileException($errorMessage ?: "Error code: {$response->getStatusCode()}"); 75 | } 76 | 77 | /** 78 | * Check-out from a vehicle 79 | * @return bool 80 | * @throws AutomileException 81 | */ 82 | public function checkOut() 83 | { 84 | $request = Config::getNewRequest(); 85 | $response = Config::getNewResponse(); 86 | $client = Config::getNewHttpClient(); 87 | 88 | $this->_authorizeRequest($request); 89 | 90 | $request->setMethod(Config::METHOD_POST) 91 | ->setUri($this->_vehicleUri . '/checkout'); 92 | 93 | $isSuccessful = $client->send($request, $response); 94 | 95 | if ($isSuccessful) { 96 | return true; 97 | } 98 | 99 | $errorMessage = $response->getErrorMessage(); 100 | throw new AutomileException($errorMessage ?: "Error code: {$response->getStatusCode()}"); 101 | } 102 | 103 | /** 104 | * Creates a new vehicle 105 | * @param Vehicle2 $vehicle 106 | * @return Vehicle2 107 | */ 108 | public function createVehicle(Vehicle2 $vehicle) 109 | { 110 | return $this->_create($this->_vehicleUri, $vehicle); 111 | } 112 | 113 | /** 114 | * Removes the given vehicle 115 | * @param int $id 116 | * @return bool 117 | */ 118 | public function deleteVehicle($id) 119 | { 120 | return $this->_delete($this->_vehicleUri, $id); 121 | } 122 | 123 | /** 124 | * Updates the given vehicle with new model 125 | * @param Vehicle2 $vehicle 126 | * @return Vehicle2 127 | * @throws AutomileException 128 | */ 129 | public function editVehicle(Vehicle2 $vehicle) 130 | { 131 | if (!$vehicle->getVehicleId()) { 132 | throw new AutomileException('Vehicle ID is empty'); 133 | } 134 | 135 | return $this->_edit($this->_vehicleUri, $vehicle->getVehicleId(), $vehicle); 136 | } 137 | 138 | } 139 | -------------------------------------------------------------------------------- /lib/Endpoints/VehicleGeofence.php: -------------------------------------------------------------------------------- 1 | _getById($this->_vehicleGeofenceUri, $vehicleGeofenceId, new VehicleGeofenceModel()); 26 | } 27 | 28 | /** 29 | * Get all relationships between geofences and vehicles 30 | * @param int $geofenceId 31 | * @return GeofenceRowset 32 | * @throws AutomileException 33 | */ 34 | public function getVehicleGeofencesByGeofenceId($geofenceId) 35 | { 36 | $request = Config::getNewRequest(); 37 | $response = Config::getNewResponse(); 38 | $client = Config::getNewHttpClient(); 39 | 40 | $this->_authorizeRequest($request); 41 | 42 | $request->setMethod(Config::METHOD_GET) 43 | ->setUri($this->_vehicleGeofenceUri) 44 | ->setUriParam('geofenceId', $geofenceId); 45 | 46 | $isSuccessful = $client->send($request, $response); 47 | 48 | if ($isSuccessful) { 49 | return new GeofenceRowset($response->getBody()); 50 | } 51 | 52 | throw new AutomileException($response->getErrorMessage()); 53 | } 54 | 55 | /** 56 | * Creates a relationship between a vehicle and a geofence and returns the newly created relationship 57 | * @param VehicleGeofenceModel $model 58 | * @return VehicleGeofenceModel 59 | */ 60 | public function createVehicleGeofence(VehicleGeofenceModel $model) 61 | { 62 | return $this->_create($this->_vehicleGeofenceUri, $model); 63 | } 64 | 65 | /** 66 | * Edit a relationship between a vehicle and a geofence 67 | * @param VehicleGeofenceModel $model 68 | * @return VehicleGeofenceModel 69 | * @throws AutomileException 70 | */ 71 | public function editVehicleGeofence(VehicleGeofenceModel $model) 72 | { 73 | if (!$model->getVehicleGeofenceId()) { 74 | throw new AutomileException("VehicleGeofence ID is missing"); 75 | } 76 | 77 | return $this->_edit($this->_vehicleGeofenceUri, $model->getVehicleGeofenceId(), $model); 78 | } 79 | 80 | /** 81 | * Removes association between a vehicle and geofence 82 | * @param int $vehicleGeofenceId 83 | * @return bool 84 | */ 85 | public function deleteVehicleGeofence($vehicleGeofenceId) 86 | { 87 | return $this->_delete($this->_vehicleGeofenceUri, $vehicleGeofenceId); 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /lib/Endpoints/VehicleHealth.php: -------------------------------------------------------------------------------- 1 | _authorizeRequest($request); 31 | 32 | $vehicleId = (int)$vehicleId; 33 | $params = $datePeriod ? "{$datePeriod}/{$vehicleId}" : $vehicleId; 34 | 35 | $request->setMethod(Config::METHOD_GET) 36 | ->setUri($this->_vehicleHealthUri . '/' . $params); 37 | 38 | $isSuccessful = $client->send($request, $response); 39 | 40 | if ($isSuccessful) { 41 | return new VehicleHealthModel($response->getBody()); 42 | } 43 | 44 | throw new AutomileException($response->getErrorMessage()); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /lib/Endpoints/VehiclePlace.php: -------------------------------------------------------------------------------- 1 | _getById($this->_vehiclePlaceUri, $vehiclePlaceId, new VehiclePlaceModel()); 26 | } 27 | 28 | /** 29 | * Get all relationships between places and vehicles 30 | * @param int $placeId 31 | * @return PlaceRowset 32 | * @throws AutomileException 33 | */ 34 | public function getVehiclePlacesByPlaceId($placeId) 35 | { 36 | $request = Config::getNewRequest(); 37 | $response = Config::getNewResponse(); 38 | $client = Config::getNewHttpClient(); 39 | 40 | $this->_authorizeRequest($request); 41 | 42 | $request->setMethod(Config::METHOD_GET) 43 | ->setUri($this->_vehiclePlaceUri) 44 | ->setUriParam('placeId', $placeId); 45 | 46 | $isSuccessful = $client->send($request, $response); 47 | 48 | if ($isSuccessful) { 49 | return new PlaceRowset($response->getBody()); 50 | } 51 | 52 | throw new AutomileException($response->getErrorMessage()); 53 | } 54 | 55 | /** 56 | * Creates a relationship between a vehicle and a place and returns the newly created relationship 57 | * @param VehiclePlaceModel $vehiclePlace 58 | * @return VehiclePlaceModel 59 | */ 60 | public function createVehiclePlace(VehiclePlaceModel $vehiclePlace) 61 | { 62 | return $this->_create($this->_vehiclePlaceUri, $vehiclePlace); 63 | } 64 | 65 | /** 66 | * Edit the relationship between the vehicle and the place 67 | * @param VehiclePlaceModel $vehiclePlace 68 | * @return VehiclePlaceModel 69 | * @throws AutomileException 70 | */ 71 | public function editVehiclePlace(VehiclePlaceModel $vehiclePlace) 72 | { 73 | if (!$vehiclePlace->getVehiclePlaceId()) { 74 | throw new AutomileException("VehiclePlace ID is missing"); 75 | } 76 | 77 | return $this->_edit($this->_vehiclePlaceUri, $vehiclePlace->getVehiclePlaceId(), $vehiclePlace); 78 | } 79 | 80 | /** 81 | * Removes association between a vehicle and place 82 | * @param int $vehiclePlaceId 83 | * @return bool 84 | */ 85 | public function deleteVehiclePlace($vehiclePlaceId) 86 | { 87 | return $this->_delete($this->_vehiclePlaceUri, $vehiclePlaceId); 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /lib/Exceptions/AutomileException.php: -------------------------------------------------------------------------------- 1 | _headers) ? $this->_headers[$header] : null; 30 | } 31 | 32 | /** 33 | * @return array 34 | */ 35 | public function getHeaders() 36 | { 37 | return $this->_headers; 38 | } 39 | 40 | /** 41 | * @param string $headers 42 | * @return ResponseInterface 43 | */ 44 | public function setHeaders($headers) 45 | { 46 | $headers = explode(self::LINE_SEPARATOR, trim($headers)); 47 | 48 | foreach ($headers as $i => $line) { 49 | if (strpos($line, ': ')) { 50 | list ($key, $value) = explode(': ', $line); 51 | $this->_headers[$key] = $value; 52 | } 53 | } 54 | 55 | return $this; 56 | } 57 | 58 | /** 59 | * @return int 60 | */ 61 | public function getStatusCode() 62 | { 63 | return $this->_statusCode; 64 | } 65 | 66 | /** 67 | * @param int $code 68 | * @return ResponseInterface 69 | */ 70 | public function setStatusCode($code) 71 | { 72 | $this->_statusCode = $code; 73 | return $this; 74 | } 75 | 76 | /** 77 | * @param bool $raw get raw body instead of a parsed object 78 | * @return string|\stdClass 79 | */ 80 | public function getBody($raw = false) 81 | { 82 | return $raw ? $this->_body : json_decode($this->_body); 83 | } 84 | 85 | /** 86 | * @param string $body 87 | * @return ResponseInterface 88 | */ 89 | public function setBody($body) 90 | { 91 | $this->_body = $body; 92 | return $this; 93 | } 94 | 95 | /** 96 | * @return bool 97 | */ 98 | public function isSuccessful() 99 | { 100 | return in_array($this->getStatusCode(), [self::SUCCESS_CODE, self::CREATED_CODE]); 101 | } 102 | 103 | /** 104 | * @return string|null 105 | */ 106 | public function getRedirect() 107 | { 108 | return self::CREATED_CODE == 201 ? $this->getHeader('Location') : null; 109 | } 110 | 111 | /** 112 | * @return bool 113 | */ 114 | public function isCompleted() 115 | { 116 | return (bool)$this->getStatusCode(); 117 | } 118 | 119 | /** 120 | * @return string|null 121 | */ 122 | public function getErrorMessage() 123 | { 124 | if ($this->isSuccessful()) { 125 | return null; 126 | } 127 | 128 | $body = $this->getBody(); 129 | if ($body) { 130 | return empty($body->Message) ? null : $body->Message; 131 | } else { 132 | return $this->getBody(true); 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /lib/HttpClient/Response/ResponseInterface.php: -------------------------------------------------------------------------------- 1 | _properties['RecordTimeStamp'] = $date; 31 | 32 | return $this; 33 | } 34 | 35 | /** 36 | * convert the model to an array 37 | * @return array 38 | */ 39 | public function toArray() 40 | { 41 | $values = parent::toArray(); 42 | 43 | if (!empty($values['RecordTimeStamp'])) { 44 | $values['RecordTimeStamp'] = $values['RecordTimeStamp']->format('c'); 45 | } 46 | 47 | return $values; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /lib/Models/AmbientAirTemperatureRowset.php: -------------------------------------------------------------------------------- 1 | _properties['Dtcs'] = $dtcs; 65 | return $this; 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /lib/Models/DeviceEventMil.php: -------------------------------------------------------------------------------- 1 | _properties['ExpenseReportRows'] = $rows; 42 | return $this; 43 | } 44 | 45 | /** 46 | * @param string|\DateTime $dateTime a DateTime object or date in string representation 47 | * @return ExpenseReport 48 | */ 49 | public function setExpenseReportDateUtc ($dateTime) 50 | { 51 | if (!$dateTime instanceof \DateTime) { 52 | $dateTime = new \DateTime($dateTime, new \DateTimeZone('UTC')); 53 | } 54 | $this->_properties['ExpenseReportDateUtc'] = $dateTime; 55 | 56 | return $this; 57 | } 58 | 59 | /** 60 | * @return \DateTime 61 | */ 62 | public function getExpenseReportDateUtc() 63 | { 64 | return empty($this->_properties['ExpenseReportDateUtc']) ? null : $this->_properties['ExpenseReportDateUtc']; 65 | } 66 | 67 | /** 68 | * convert the model to an array 69 | * @return array 70 | */ 71 | public function toArray() 72 | { 73 | $values = parent::toArray(); 74 | 75 | if (!empty($values['ExpenseReportDateUtc'])) { 76 | $values['ExpenseReportDateUtc'] = $values['ExpenseReportDateUtc']->format('c'); 77 | } 78 | 79 | return $values; 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /lib/Models/ExpenseReportEmail.php: -------------------------------------------------------------------------------- 1 | _properties['ExpenseReportRowContent'] = $rows; 62 | return $this; 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /lib/Models/ExpenseReportRowContent.php: -------------------------------------------------------------------------------- 1 | _properties['FromDate'] = $date; 39 | 40 | return $this; 41 | } 42 | 43 | /** 44 | * @param \DateTime $date 45 | * @return ExpenseReportsEmail 46 | */ 47 | public function setToDate(\DateTime $date) 48 | { 49 | if (!$date instanceof \DateTime) { 50 | $date = new \DateTime($date); 51 | } 52 | $this->_properties['ToDate'] = $date; 53 | 54 | return $this; 55 | } 56 | 57 | /** 58 | * @return array 59 | */ 60 | public function toArray() 61 | { 62 | $values = parent::toArray(); 63 | 64 | if (!empty($values['FromDate'])) { 65 | $values['FromDate'] = $values['FromDate']->format('c'); 66 | } 67 | 68 | if (!empty($values['ToDate'])) { 69 | $values['ToDate'] = $values['ToDate']->format('c'); 70 | } 71 | 72 | return $values; 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /lib/Models/Geofence.php: -------------------------------------------------------------------------------- 1 | _properties['GeofencePolygon'] = $rows; 39 | 40 | return $this; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /lib/Models/GeofencePolygon.php: -------------------------------------------------------------------------------- 1 | parent::toArray()]; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /lib/Models/GeofenceReport.php: -------------------------------------------------------------------------------- 1 | _properties['FromDate'] = $date; 38 | 39 | return $this; 40 | } 41 | 42 | /** 43 | * @param string|\DateTime $date 44 | * @return GeofenceReport 45 | */ 46 | public function setToDate($date) 47 | { 48 | if (!$date instanceof \DateTime) { 49 | $date = new \DateTime($date, new \DateTimeZone('UTC')); 50 | } 51 | $this->_properties['ToDate'] = $date; 52 | 53 | return $this; 54 | } 55 | 56 | /** 57 | * @param array|object $rows 58 | * @return GeofenceReport 59 | */ 60 | public function setResult($rows) 61 | { 62 | if (!is_object($rows) || !$rows instanceof GeofenceReportRecordRowset) { 63 | $rows = new GeofenceReportRecordRowset($rows); 64 | } 65 | 66 | $this->_properties['Result'] = $rows; 67 | return $this; 68 | } 69 | 70 | /** 71 | * convert the model to an array 72 | * @return array 73 | */ 74 | public function toArray() 75 | { 76 | $values = parent::toArray(); 77 | 78 | if (!empty($values['FromDate'])) { 79 | $values['FromDate'] = $values['FromDate']->format('c'); 80 | } 81 | 82 | if (!empty($values['ToDate'])) { 83 | $values['ToDate'] = $values['ToDate']->format('c'); 84 | } 85 | 86 | return $values; 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /lib/Models/GeofenceReportRecord.php: -------------------------------------------------------------------------------- 1 | _properties['VehicleTinyModel'] = $model; 52 | return $this; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /lib/Models/GeofenceReportRecordRowset.php: -------------------------------------------------------------------------------- 1 | 90 || $lat < -90) { 28 | throw new InvalidArgumentException("Latitude should be in range [-90, 90]"); 29 | } 30 | 31 | $this->_properties['Latitude'] = $lat; 32 | 33 | return $this; 34 | } 35 | 36 | /** 37 | * @param float $lng 38 | * @return GeographicPosition 39 | * @throws InvalidArgumentException 40 | */ 41 | public function setLongitude($lng) 42 | { 43 | if ($lng > 180 || $lng < -180) { 44 | throw new InvalidArgumentException("Longitude should be in range [-180, 180]"); 45 | } 46 | 47 | $this->_properties['Longitude'] = $lng; 48 | 49 | return $this; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /lib/Models/ModelAbstract.php: -------------------------------------------------------------------------------- 1 | _setProperties($properties); 29 | } 30 | 31 | /** 32 | * @param array|object $properties array or object to load properties from 33 | * @return ModelAbstract 34 | */ 35 | public function reset($properties = []) 36 | { 37 | $this->_properties = []; 38 | $this->_setProperties($properties); 39 | return $this; 40 | } 41 | 42 | /** 43 | * @param array|object $properties array or object to load properties from 44 | * @return ModelAbstract 45 | */ 46 | protected function _setProperties($properties) 47 | { 48 | $properties = (array)$properties; 49 | foreach ($properties as $key => $value) { 50 | if (!in_array($key, $this->_allowedProperties)) { 51 | continue; 52 | } 53 | 54 | $methodName = 'set' . ucfirst($key); 55 | if (method_exists($this, $methodName)) { 56 | $this->$methodName($value); 57 | } else { 58 | $this->_properties[$key] = $value; 59 | } 60 | } 61 | 62 | return $this; 63 | } 64 | 65 | /** 66 | * @param string $method 67 | * @param array $args 68 | * @return ModelAbstract|string|null 69 | * @throws ModelException 70 | */ 71 | public function __call($method, array $args) 72 | { 73 | $prefix = substr($method, 0, 3); 74 | $property = substr($method, 3); 75 | 76 | if ($prefix && $property) { 77 | if (in_array($property, $this->_allowedProperties)) { 78 | switch ($prefix) { 79 | case 'set': 80 | if (count($args) == 1) { 81 | $this->_properties[$property] = reset($args); 82 | return $this; 83 | } 84 | break; 85 | case 'get': 86 | return is_null($this->_properties[$property]) ? null : $this->_properties[$property]; 87 | } 88 | } 89 | } 90 | 91 | throw new \BadMethodCallException("Method '{$method}' not found"); 92 | } 93 | 94 | /** 95 | * JSON-encode the model to be sent to the API 96 | * @return string 97 | */ 98 | public function toJson() 99 | { 100 | return json_encode($this->toArray()); 101 | } 102 | 103 | /** 104 | * convert the model to an array 105 | * @return array 106 | */ 107 | public function toArray() 108 | { 109 | $values = []; 110 | 111 | foreach ($this->_allowedProperties as $key) { 112 | $methodName = 'get' . ucfirst($key); 113 | 114 | if (method_exists($this, $methodName)) { 115 | $values[$key] = $this->$methodName(); 116 | } elseif (array_key_exists($key, $this->_properties)) { 117 | $values[$key] = $this->_properties[$key]; 118 | } 119 | 120 | if (!empty($values[$key]) && ($values[$key] instanceof ModelAbstract || $values[$key] instanceof ModelRowsetAbstract)) { 121 | $values[$key] = $values[$key]->toArray(); 122 | } 123 | } 124 | 125 | return $values; 126 | } 127 | 128 | } 129 | -------------------------------------------------------------------------------- /lib/Models/ModelException.php: -------------------------------------------------------------------------------- 1 | _properties['PositionPoint'] = $property; 45 | return $this; 46 | } 47 | 48 | /** 49 | * @return bool 50 | * @throws ModelValidatorException 51 | */ 52 | public function isValid() 53 | { 54 | if ($this->getTripTypeTrigger() == TripTypeTriggerType::DRIVES_BETWEEN && !$this->getDrivesBetweenAnotherPlaceId()) { 55 | throw new ModelValidatorException('You need to enter the second place when you select the drives between type, use the DrivesBetweenAnotherPlaceId property'); 56 | } 57 | 58 | if ($this->getTripTypeTrigger() != TripTypeTriggerType::DRIVES_BETWEEN && $this->getDrivesBetweenAnotherPlaceId()) { 59 | throw new ModelValidatorException("You can't use DrivesBetweenAnotherPlaceId property if the TripTypeTrigger is null or isnt't equal to DrivesBetween type"); 60 | } 61 | 62 | return true; 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /lib/Models/PlaceRowset.php: -------------------------------------------------------------------------------- 1 | _properties['DistanceReports'] = $rows; 42 | return $this; 43 | } 44 | 45 | /** 46 | * @param array|object $rows 47 | * @return VehicleSummary 48 | */ 49 | public function setFuelReports($rows) 50 | { 51 | if (!is_object($rows) || !$rows instanceof FuelRowset) { 52 | $rows = new FuelRowset($rows); 53 | } 54 | 55 | $this->_properties['FuelReports'] = $rows; 56 | return $this; 57 | } 58 | 59 | /** 60 | * @param array|object $rows 61 | * @return VehicleSummary 62 | */ 63 | public function setTravelTimeReports($rows) 64 | { 65 | if (!is_object($rows) || !$rows instanceof TravelTimeRowset) { 66 | $rows = new TravelTimeRowset($rows); 67 | } 68 | 69 | $this->_properties['TravelTimeReports'] = $rows; 70 | return $this; 71 | } 72 | 73 | /** 74 | * @param array|object $rows 75 | * @return VehicleSummary 76 | */ 77 | public function setCO2Reports($rows) 78 | { 79 | if (!is_object($rows) || !$rows instanceof CO2Rowset) { 80 | $rows = new CO2Rowset($rows); 81 | } 82 | 83 | $this->_properties['CO2Reports'] = $rows; 84 | return $this; 85 | } 86 | 87 | /** 88 | * @param array|object $rows 89 | * @return VehicleSummary 90 | */ 91 | public function setIdleTimeReports($rows) 92 | { 93 | if (!is_object($rows) || !$rows instanceof IdleTimeRowset) { 94 | $rows = new IdleTimeRowset($rows); 95 | } 96 | 97 | $this->_properties['IdleTimeReports'] = $rows; 98 | return $this; 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /lib/Models/Task.php: -------------------------------------------------------------------------------- 1 | _properties['TaskMessages'] = $rows; 46 | return $this; 47 | } 48 | 49 | /** 50 | * @param array|object $position 51 | * @return Task 52 | */ 53 | public function setPosition($position) 54 | { 55 | if (!is_object($position) || !$position instanceof GeographicPosition) { 56 | $position = new GeographicPosition($position); 57 | } 58 | 59 | $this->_properties['Position'] = $position; 60 | return $this; 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /lib/Models/TaskMessage.php: -------------------------------------------------------------------------------- 1 | _properties['Position'] = $position; 55 | return $this; 56 | } 57 | 58 | /** 59 | * @param string|\DateTime $dateTime a DateTime object or date in string representation 60 | * @return TaskMessage 61 | */ 62 | public function setMessageSentAtUtc ($dateTime) 63 | { 64 | if (!$dateTime instanceof \DateTime) { 65 | $dateTime = new \DateTime($dateTime, new \DateTimeZone('UTC')); 66 | } 67 | $this->_properties['MessageSentAtUtc'] = $dateTime; 68 | 69 | return $this; 70 | } 71 | 72 | /** 73 | * convert the model to an array 74 | * @return array 75 | */ 76 | public function toArray() 77 | { 78 | $values = parent::toArray(); 79 | 80 | if (!empty($values['MessageSentAtUtc'])) { 81 | $values['MessageSentAtUtc'] = $values['MessageSentAtUtc']->format('c'); 82 | } 83 | 84 | return $values; 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /lib/Models/TaskMessageRowset.php: -------------------------------------------------------------------------------- 1 | _properties['RecordTimeStamp'] = $date; 43 | 44 | return $this; 45 | } 46 | 47 | /** 48 | * convert the model to an array 49 | * @return array 50 | */ 51 | public function toArray() 52 | { 53 | $values = parent::toArray(); 54 | 55 | if (!empty($values['RecordTimeStamp'])) { 56 | $values['RecordTimeStamp'] = $values['RecordTimeStamp']->format('c'); 57 | } 58 | 59 | return $values; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /lib/Models/TripGeoRowset.php: -------------------------------------------------------------------------------- 1 | getUsername()}'); 41 | \Automile\Sdk\Config::setPassword('{$this->getPassword()}'); 42 | \Automile\Sdk\Config::setApiClient('{$this->getAPIClientIdentifier()}'); 43 | \Automile\Sdk\Config::setApiSecret('{$this->getAPIClientSecret()}'); 44 | TXT; 45 | 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /lib/Models/Vehicle/BatteryEvent.php: -------------------------------------------------------------------------------- 1 | 90 || $lat < -90) { 39 | throw new InvalidArgumentException("Latitude should be in range [-90, 90]"); 40 | } 41 | 42 | $this->_properties['Latitude'] = $lat; 43 | 44 | return $this; 45 | } 46 | 47 | /** 48 | * @param float $lng 49 | * @return BatteryEvent 50 | * @throws InvalidArgumentException 51 | */ 52 | public function setLongitude($lng) 53 | { 54 | if ($lng > 180 || $lng < -180) { 55 | throw new InvalidArgumentException("Longitude should be in range [-180, 180]"); 56 | } 57 | 58 | $this->_properties['Longitude'] = $lng; 59 | 60 | return $this; 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /lib/Models/Vehicle/BatteryEventRowset.php: -------------------------------------------------------------------------------- 1 | _properties['DefaultTripType'] = $tripType; 51 | 52 | return $this; 53 | } 54 | 55 | /** 56 | * @see DeviceType 57 | * @param string $deviceType 58 | * @return CheckIn 59 | * @throws ModelException 60 | */ 61 | public function setUserDeviceType($deviceType) 62 | { 63 | if (!DeviceType::isValid($deviceType)) { 64 | throw new ModelException("Device Type value is out of range"); 65 | } 66 | 67 | $this->_properties['UserDeviceType'] = $deviceType; 68 | 69 | return $this; 70 | } 71 | 72 | /** 73 | * @param string|\DateTime $dateTime a DateTime object or date in string representation 74 | * @return CheckIn 75 | */ 76 | public function setCheckOutAtUtc($dateTime) 77 | { 78 | if (!$dateTime instanceof \DateTime) { 79 | $dateTime = new \DateTime($dateTime, new \DateTimeZone('UTC')); 80 | } 81 | $this->_properties['CheckOutAtUtc'] = $dateTime; 82 | 83 | return $this; 84 | } 85 | 86 | /** 87 | * @return \DateTime 88 | */ 89 | public function getCheckOutAtUtc() 90 | { 91 | return empty($this->_properties['CheckOutAtUtc']) ? null : $this->_properties['CheckOutAtUtc']; 92 | } 93 | 94 | /** 95 | * convert the model to an array 96 | * @return array 97 | */ 98 | public function toArray() 99 | { 100 | $values = parent::toArray(); 101 | 102 | if (!empty($values['CheckOutAtUtc'])) { 103 | $values['CheckOutAtUtc'] = $values['CheckOutAtUtc']->format('c'); 104 | } 105 | 106 | return $values; 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /lib/Models/Vehicle/Defect.php: -------------------------------------------------------------------------------- 1 | _properties['VehicleDefectStatus'] = $status; 55 | return $this; 56 | } 57 | 58 | /** 59 | * @param array|object $comments 60 | * @return Defect 61 | */ 62 | public function setVehicleDefectComments($comments) 63 | { 64 | if (!is_object($comments) || !$comments instanceof DefectCommentRowset) { 65 | $comments = new DefectCommentRowset($comments); 66 | } 67 | 68 | $this->_properties['VehicleDefectComments'] = $comments; 69 | return $this; 70 | } 71 | 72 | /** 73 | * @param array|object $attachments 74 | * @return Defect 75 | */ 76 | public function setVehicleDefectAttachments($attachments) 77 | { 78 | if (!is_object($attachments) || !$attachments instanceof DefectAttachmentRowset) { 79 | $attachments = new DefectAttachmentRowset($attachments); 80 | } 81 | 82 | $this->_properties['VehicleDefectAttachments'] = $attachments; 83 | return $this; 84 | } 85 | 86 | /** 87 | * @param string|\DateTime $dateTime a DateTime object or date in string representation 88 | * @return Defect 89 | */ 90 | public function setDefectDateUtc($dateTime) 91 | { 92 | if (!$dateTime instanceof \DateTime) { 93 | $dateTime = new \DateTime($dateTime, new \DateTimeZone('UTC')); 94 | } 95 | $this->_properties['DefectDateUtc'] = $dateTime; 96 | 97 | return $this; 98 | } 99 | 100 | /** 101 | * convert the model to an array 102 | * @return array 103 | */ 104 | public function toArray() 105 | { 106 | $values = parent::toArray(); 107 | 108 | if (!empty($values['DefectDateUtc'])) { 109 | $values['DefectDateUtc'] = $values['DefectDateUtc']->format('c'); 110 | } 111 | 112 | return $values; 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /lib/Models/Vehicle/DefectAttachment.php: -------------------------------------------------------------------------------- 1 | _properties['DTCEventDetails'] = $event; 39 | return $this; 40 | } 41 | 42 | /** 43 | * @param float $lat 44 | * @return DtcEvent 45 | * @throws InvalidArgumentException 46 | */ 47 | public function setLatitude($lat) 48 | { 49 | if ($lat > 90 || $lat < -90) { 50 | throw new InvalidArgumentException("Latitude should be in range [-90, 90]"); 51 | } 52 | 53 | $this->_properties['Latitude'] = $lat; 54 | 55 | return $this; 56 | } 57 | 58 | /** 59 | * @param float $lng 60 | * @return DtcEvent 61 | * @throws InvalidArgumentException 62 | */ 63 | public function setLongitude($lng) 64 | { 65 | if ($lng > 180 || $lng < -180) { 66 | throw new InvalidArgumentException("Longitude should be in range [-180, 180]"); 67 | } 68 | 69 | $this->_properties['Longitude'] = $lng; 70 | 71 | return $this; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /lib/Models/Vehicle/DtcEventDetail.php: -------------------------------------------------------------------------------- 1 | _properties['RecordTimeStamp'] = $date; 33 | 34 | return $this; 35 | } 36 | 37 | /** 38 | * convert the model to an array 39 | * @return array 40 | */ 41 | public function toArray() 42 | { 43 | $values = parent::toArray(); 44 | 45 | if (!empty($values['RecordTimeStamp'])) { 46 | $values['RecordTimeStamp'] = $values['RecordTimeStamp']->format('c'); 47 | } 48 | 49 | return $values; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /lib/Models/Vehicle/EngineCoolantTemperatureRowset.php: -------------------------------------------------------------------------------- 1 | _properties['RecordTimeStamp'] = $date; 33 | 34 | return $this; 35 | } 36 | 37 | /** 38 | * convert the model to an array 39 | * @return array 40 | */ 41 | public function toArray() 42 | { 43 | $values = parent::toArray(); 44 | 45 | if (!empty($values['RecordTimeStamp'])) { 46 | $values['RecordTimeStamp'] = $values['RecordTimeStamp']->format('c'); 47 | } 48 | 49 | return $values; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /lib/Models/Vehicle/FuelLevelInputRowset.php: -------------------------------------------------------------------------------- 1 | _properties['VehicleDefects'] = $defects; 47 | return $this; 48 | } 49 | 50 | /** 51 | * @param array|object $status 52 | * @return Inspection 53 | */ 54 | public function setInspectionStatus($status) 55 | { 56 | if (!is_object($status) || !$status instanceof InspectionStatusRowset) { 57 | $status = new InspectionStatusRowset($status); 58 | } 59 | 60 | $this->_properties['InspectionStatus'] = $status; 61 | return $this; 62 | } 63 | 64 | /** 65 | * @param string|\DateTime $dateTime a DateTime object or date in string representation 66 | * @return Inspection 67 | */ 68 | public function setInspectionDateUtc ($dateTime) 69 | { 70 | if (!$dateTime instanceof \DateTime) { 71 | $dateTime = new \DateTime($dateTime, new \DateTimeZone('UTC')); 72 | } 73 | $this->_properties['InspectionDateUtc'] = $dateTime; 74 | 75 | return $this; 76 | } 77 | 78 | /** 79 | * convert the model to an array 80 | * @return array 81 | */ 82 | public function toArray() 83 | { 84 | $values = parent::toArray(); 85 | 86 | if (!empty($values['InspectionDateUtc'])) { 87 | $values['InspectionDateUtc'] = $values['InspectionDateUtc']->format('c'); 88 | } 89 | 90 | return $values; 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /lib/Models/Vehicle/InspectionExport.php: -------------------------------------------------------------------------------- 1 | _properties['StatusDateUtc'] = $dateTime; 42 | 43 | return $this; 44 | } 45 | 46 | /** 47 | * convert the model to an array 48 | * @return array 49 | */ 50 | public function toArray() 51 | { 52 | $values = parent::toArray(); 53 | 54 | if (!empty($values['StatusDateUtc'])) { 55 | $values['StatusDateUtc'] = $values['StatusDateUtc']->format('c'); 56 | } 57 | 58 | return $values; 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /lib/Models/Vehicle/InspectionStatusRowset.php: -------------------------------------------------------------------------------- 1 | 90 || $lat < -90) { 46 | throw new InvalidArgumentException("Latitude should be in range [-90, 90]"); 47 | } 48 | 49 | $this->_properties['Latitude'] = $lat; 50 | 51 | return $this; 52 | } 53 | 54 | /** 55 | * @param float $lng 56 | * @return MilEvent 57 | * @throws InvalidArgumentException 58 | */ 59 | public function setLongitude($lng) 60 | { 61 | if ($lng > 180 || $lng < -180) { 62 | throw new InvalidArgumentException("Longitude should be in range [-180, 180]"); 63 | } 64 | 65 | $this->_properties['Longitude'] = $lng; 66 | 67 | return $this; 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /lib/Models/Vehicle/MilEventRowset.php: -------------------------------------------------------------------------------- 1 | _properties['RecordTimeStamp'] = $date; 33 | 34 | return $this; 35 | } 36 | 37 | /** 38 | * convert the model to an array 39 | * @return array 40 | */ 41 | public function toArray() 42 | { 43 | $values = parent::toArray(); 44 | 45 | if (!empty($values['RecordTimeStamp'])) { 46 | $values['RecordTimeStamp'] = $values['RecordTimeStamp']->format('c'); 47 | } 48 | 49 | return $values; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /lib/Models/Vehicle/RPMRowset.php: -------------------------------------------------------------------------------- 1 | _properties['RecordTimeStamp'] = $date; 33 | 34 | return $this; 35 | } 36 | 37 | /** 38 | * convert the model to an array 39 | * @return array 40 | */ 41 | public function toArray() 42 | { 43 | $values = parent::toArray(); 44 | 45 | if (!empty($values['RecordTimeStamp'])) { 46 | $values['RecordTimeStamp'] = $values['RecordTimeStamp']->format('c'); 47 | } 48 | 49 | return $values; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /lib/Models/Vehicle/SpeedRowset.php: -------------------------------------------------------------------------------- 1 | _httpClient = $client; 42 | $this->_request = $request; 43 | $this->_response = $response; 44 | } 45 | 46 | /** 47 | * request new OAuth token from the server 48 | * @param User $user 49 | * @return Token 50 | * @throws OAuthException 51 | */ 52 | public function createToken(User $user) 53 | { 54 | $this->_request->setHttpAuth($user->getAPIClientIdentifier(), $user->getAPIClientSecret()) 55 | ->setMethod(Config::METHOD_POST) 56 | ->setUri(Config::URI_OAUTH_TOKEN) 57 | ->setPostParam('grant_type', 'password') 58 | ->setPostParam('username', $user->getUsername()) 59 | ->setPostParam('password', $user->getPassword()); 60 | 61 | $isSuccessful = $this->_httpClient->send($this->_request, $this->_response); 62 | 63 | $body = $this->_response->getBody(); 64 | if ($isSuccessful) { 65 | if (!isset($body->access_token, $body->token_type, $body->expires_in, $body->refresh_token)) { 66 | throw new OAuthException('Invalid server response, access token was not created'); 67 | } 68 | 69 | $expiration = time() + $body->expires_in; 70 | return new Token($body->access_token, $body->refresh_token, $expiration, $body->token_type); 71 | } 72 | 73 | throw new OAuthException(empty($body->error) ? 'The token was not created' : $body->error); 74 | } 75 | 76 | /** 77 | * refresh OAuth token 78 | * @param User $user 79 | * @param Token $token old token that needs to be refreshed 80 | * @return Token 81 | * @throws OAuthException 82 | */ 83 | public function refreshToken(User $user, Token $token) 84 | { 85 | if (!$token->getRefreshToken()) { 86 | throw new OAuthException("Refresh token is not defined"); 87 | } 88 | 89 | $this->_request->setHttpAuth($user->getAPIClientIdentifier(), $user->getAPIClientSecret()) 90 | ->setUri(Config::URI_OAUTH_TOKEN) 91 | ->setPostParam('grant_type', 'refresh_token') 92 | ->setPostParam('refresh_token', $token->getRefreshToken()); 93 | 94 | $isSuccessful = $this->_httpClient->send($this->_request, $this->_response); 95 | 96 | $body = $this->_response->getBody(); 97 | if ($isSuccessful) { 98 | if (!isset($body->access_token, $body->token_type, $body->expires_in, $body->refresh_token)) { 99 | throw new OAuthException('Invalid server response, access token was not created'); 100 | } 101 | 102 | $expiration = time() + $body->expires_in; 103 | return new Token($body->access_token, $body->refresh_token, $expiration, $body->token_type); 104 | } 105 | 106 | throw new OAuthException(empty($body->error) ? 'The token was not created' : $body->error); 107 | } 108 | 109 | } -------------------------------------------------------------------------------- /lib/OAuth/OAuthException.php: -------------------------------------------------------------------------------- 1 | _accessToken = $accessToken; 43 | $this->_refreshToken = $refreshToken; 44 | $this->_expiration = $expiration; 45 | $this->_type = $type; 46 | } 47 | 48 | /** 49 | * @return bool 50 | * @throws OAuthException 51 | */ 52 | public function isExpired() 53 | { 54 | if (!$this->_expiration) { 55 | throw new OAuthException('Expiration date is undefined'); 56 | } 57 | 58 | return $this->_expiration <= time(); 59 | } 60 | 61 | /** 62 | * @return string 63 | */ 64 | public function getAccessToken() 65 | { 66 | return $this->_accessToken; 67 | } 68 | 69 | /** 70 | * @param string $accessToken 71 | * @return Token 72 | */ 73 | public function setAccessToken($accessToken) 74 | { 75 | $this->_accessToken = $accessToken; 76 | return $this; 77 | } 78 | 79 | /** 80 | * @return string 81 | */ 82 | public function getRefreshToken() 83 | { 84 | return $this->_refreshToken; 85 | } 86 | 87 | /** 88 | * @param string $refreshToken 89 | * @return Token 90 | */ 91 | public function setRefreshToken($refreshToken) 92 | { 93 | $this->_refreshToken = $refreshToken; 94 | return $this; 95 | } 96 | 97 | /** 98 | * @return string 99 | */ 100 | public function getExpiration() 101 | { 102 | return $this->_expiration; 103 | } 104 | 105 | /** 106 | * @param string $expiration 107 | * @return Token 108 | */ 109 | public function setExpiration($expiration) 110 | { 111 | $this->_expiration = $expiration; 112 | return $this; 113 | } 114 | 115 | /** 116 | * @return string 117 | */ 118 | public function getType() 119 | { 120 | return $this->_type; 121 | } 122 | 123 | /** 124 | * @param string $type 125 | * @return Token 126 | */ 127 | public function setType($type) 128 | { 129 | $this->_type = $type; 130 | return $this; 131 | } 132 | 133 | /** 134 | * key-value pairs of data to be stored 135 | * @return array 136 | */ 137 | public function getStorableData() 138 | { 139 | return [ 140 | 'accessToken' => $this->_accessToken, 141 | 'refreshToken' => $this->_refreshToken, 142 | 'expiration' => $this->_expiration, 143 | 'type' => $this->_type 144 | ]; 145 | } 146 | 147 | /** 148 | * create an object from the stored data 149 | * @param array $data key-value pairs that were previously stored 150 | * @return StorableInterface 151 | * @throws OAuthException 152 | */ 153 | public static function restoreFromStorage(array $data) 154 | { 155 | if (!isset($data['accessToken'], $data['type'], $data['expiration'], $data['refreshToken'])) { 156 | throw new OAuthException('Access token was not created, data is missing'); 157 | } 158 | 159 | return new self($data['accessToken'], $data['refreshToken'], $data['expiration'], $data['type']); 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /lib/Storage/Filesystem.php: -------------------------------------------------------------------------------- 1 | _path = $path; 23 | return $this; 24 | } 25 | 26 | /** 27 | * @param StorableInterface $storable 28 | * @return bool 29 | */ 30 | public function save(StorableInterface $storable) 31 | { 32 | $data = $storable->getStorableData(); 33 | if (is_array($data) && count($data)) { 34 | return $this->_saveData($data); 35 | } 36 | 37 | return false; 38 | } 39 | 40 | /** 41 | * @param string $storable 42 | * @return StorableInterface|null 43 | * @throws StorageException 44 | * @internal param string $storableClass class name of a storable implementation 45 | */ 46 | public function restore($storable) 47 | { 48 | $data = $this->_loadData(); 49 | if (!$data) { 50 | return null; 51 | } 52 | 53 | if (!class_exists($storable)) { 54 | throw new StorageException("Class '{$storable}' cannot be found"); 55 | } 56 | if (!method_exists($storable, 'restoreFromStorage')) { 57 | throw new StorageException("Class '{$storable}' should implement 'Storable' interface"); 58 | } 59 | return $storable::restoreFromStorage($data); 60 | } 61 | 62 | /** 63 | * @param array $data 64 | * @return bool 65 | * @throws StorageException 66 | */ 67 | private function _saveData(array $data) 68 | { 69 | if (!$this->_path) { 70 | throw new StorageException('File path is required'); 71 | } 72 | 73 | $data = json_encode($data); 74 | if (!$data) { 75 | throw new StorageException("The data cannot be correct encoded"); 76 | } 77 | 78 | $result = file_put_contents($this->_path, $data, LOCK_EX); 79 | if (false === $result) { 80 | throw new StorageException("Could not save data into the file '{$this->_path}'"); 81 | } 82 | 83 | return false !== $result; 84 | } 85 | 86 | /** 87 | * @return array 88 | * @throws StorageException 89 | */ 90 | private function _loadData() 91 | { 92 | if (!$this->_path) { 93 | throw new StorageException('File path is required'); 94 | } 95 | 96 | if (!file_exists($this->_path)) { 97 | return []; 98 | } 99 | 100 | $data = file_get_contents($this->_path); 101 | if (false === $data) { 102 | throw new StorageException("Could not read data from the file '{$this->_path}'"); 103 | } 104 | 105 | if (!$data) { 106 | return []; 107 | } 108 | 109 | $data = json_decode($data, true); 110 | if (!count($data)) { 111 | throw new StorageException("Count not encode content of the file '{$this->_path}'"); 112 | } 113 | 114 | return $data; 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /lib/Storage/StorableInterface.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | Automile PHP SDK - API Documentation 4 | 5 | docs/parser 6 | utf8 7 | 8 | php 9 | 10 | 11 | 12 | docs 13 | 14 | 15 |