Created
March 28, 2025 22:32
-
-
Save ibuilder/9543559dbe410a96a9b32974683c87e2 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Procore API Connector | |
* | |
* A simple PHP class to authenticate and interact with the Procore API | |
*/ | |
class ProcoreConnector { | |
private $clientId; | |
private $clientSecret; | |
private $redirectUri; | |
private $accessToken; | |
private $refreshToken; | |
private $tokenExpires; | |
private $baseUrl = 'https://api.procore.com'; | |
private $apiVersion = 'v1.0'; | |
/** | |
* Constructor | |
* | |
* @param string $clientId Your Procore API client ID | |
* @param string $clientSecret Your Procore API client secret | |
* @param string $redirectUri Your application's redirect URI | |
*/ | |
public function __construct($clientId, $clientSecret, $redirectUri) { | |
$this->clientId = $clientId; | |
$this->clientSecret = $clientSecret; | |
$this->redirectUri = $redirectUri; | |
// Check if we have a stored token | |
$this->loadTokenFromStorage(); | |
} | |
/** | |
* Get authorization URL for OAuth2 flow | |
* | |
* @return string The authorization URL | |
*/ | |
public function getAuthorizationUrl() { | |
return $this->baseUrl . '/oauth/authorize?' . http_build_query([ | |
'client_id' => $this->clientId, | |
'redirect_uri' => $this->redirectUri, | |
'response_type' => 'code', | |
'scope' => 'read write' | |
]); | |
} | |
/** | |
* Exchange authorization code for tokens | |
* | |
* @param string $code The authorization code | |
* @return bool True if successful | |
*/ | |
public function exchangeAuthorizationCode($code) { | |
$response = $this->makeRequest('POST', '/oauth/token', [ | |
'grant_type' => 'authorization_code', | |
'code' => $code, | |
'client_id' => $this->clientId, | |
'client_secret' => $this->clientSecret, | |
'redirect_uri' => $this->redirectUri | |
], false); | |
if (isset($response['access_token'])) { | |
$this->accessToken = $response['access_token']; | |
$this->refreshToken = $response['refresh_token'] ?? null; | |
$this->tokenExpires = time() + ($response['expires_in'] ?? 7200); | |
$this->saveTokenToStorage(); | |
return true; | |
} | |
return false; | |
} | |
/** | |
* Refresh the access token | |
* | |
* @return bool True if successful | |
*/ | |
private function refreshAccessToken() { | |
if (!$this->refreshToken) { | |
return false; | |
} | |
$response = $this->makeRequest('POST', '/oauth/token', [ | |
'grant_type' => 'refresh_token', | |
'refresh_token' => $this->refreshToken, | |
'client_id' => $this->clientId, | |
'client_secret' => $this->clientSecret | |
], false); | |
if (isset($response['access_token'])) { | |
$this->accessToken = $response['access_token']; | |
$this->refreshToken = $response['refresh_token'] ?? $this->refreshToken; | |
$this->tokenExpires = time() + ($response['expires_in'] ?? 7200); | |
$this->saveTokenToStorage(); | |
return true; | |
} | |
return false; | |
} | |
/** | |
* Check if token is expired and refresh if needed | |
* | |
* @return bool True if we have a valid token | |
*/ | |
private function ensureValidToken() { | |
if (!$this->accessToken) { | |
return false; | |
} | |
if ($this->tokenExpires <= time() + 60) { | |
return $this->refreshAccessToken(); | |
} | |
return true; | |
} | |
/** | |
* Save token information to storage | |
*/ | |
private function saveTokenToStorage() { | |
$tokenData = [ | |
'access_token' => $this->accessToken, | |
'refresh_token' => $this->refreshToken, | |
'token_expires' => $this->tokenExpires | |
]; | |
// Store token data (implement according to your needs) | |
// Example using file storage: | |
file_put_contents('procore_token.json', json_encode($tokenData)); | |
} | |
/** | |
* Load token information from storage | |
*/ | |
private function loadTokenFromStorage() { | |
// Retrieve token data (implement according to your needs) | |
// Example using file storage: | |
if (file_exists('procore_token.json')) { | |
$tokenData = json_decode(file_get_contents('procore_token.json'), true); | |
$this->accessToken = $tokenData['access_token'] ?? null; | |
$this->refreshToken = $tokenData['refresh_token'] ?? null; | |
$this->tokenExpires = $tokenData['token_expires'] ?? 0; | |
} | |
} | |
/** | |
* Make an API request to Procore | |
* | |
* @param string $method HTTP method (GET, POST, PUT, DELETE) | |
* @param string $endpoint API endpoint | |
* @param array $data Request data | |
* @param bool $requireAuth Whether authentication is required | |
* @return array Response data | |
* @throws Exception If request fails | |
*/ | |
private function makeRequest($method, $endpoint, $data = [], $requireAuth = true) { | |
$url = $this->baseUrl; | |
// Add version prefix for API endpoints | |
if ($endpoint !== '/oauth/token') { | |
$url .= '/rest/' . $this->apiVersion; | |
} | |
$url .= $endpoint; | |
$ch = curl_init($url); | |
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); | |
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); | |
$headers = ['Content-Type: application/json']; | |
if ($requireAuth) { | |
if (!$this->ensureValidToken()) { | |
throw new Exception('Authentication required but no valid token available'); | |
} | |
$headers[] = 'Authorization: Bearer ' . $this->accessToken; | |
} | |
if (!empty($data)) { | |
if ($endpoint === '/oauth/token') { | |
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data)); | |
$headers = ['Content-Type: application/x-www-form-urlencoded']; | |
} else { | |
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); | |
} | |
} | |
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); | |
$response = curl_exec($ch); | |
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); | |
if (curl_errno($ch)) { | |
throw new Exception('cURL error: ' . curl_error($ch)); | |
} | |
curl_close($ch); | |
$responseData = json_decode($response, true); | |
if ($httpCode >= 400) { | |
$errorMessage = isset($responseData['errors']) ? json_encode($responseData['errors']) : 'Unknown error'; | |
throw new Exception('API error (' . $httpCode . '): ' . $errorMessage); | |
} | |
return $responseData; | |
} | |
/** | |
* Get a list of projects | |
* | |
* @param array $params Optional query parameters | |
* @return array List of projects | |
*/ | |
public function getProjects($params = []) { | |
return $this->makeRequest('GET', '/projects?' . http_build_query($params)); | |
} | |
/** | |
* Get a specific project by ID | |
* | |
* @param int $projectId Project ID | |
* @return array Project details | |
*/ | |
public function getProject($projectId) { | |
return $this->makeRequest('GET', '/projects/' . $projectId); | |
} | |
/** | |
* Get a list of companies | |
* | |
* @param array $params Optional query parameters | |
* @return array List of companies | |
*/ | |
public function getCompanies($params = []) { | |
return $this->makeRequest('GET', '/companies?' . http_build_query($params)); | |
} | |
/** | |
* Get a list of users in a project | |
* | |
* @param int $projectId Project ID | |
* @param array $params Optional query parameters | |
* @return array List of users | |
*/ | |
public function getProjectUsers($projectId, $params = []) { | |
return $this->makeRequest('GET', '/projects/' . $projectId . '/users?' . http_build_query($params)); | |
} | |
/** | |
* Get a list of tasks in a project | |
* | |
* @param int $projectId Project ID | |
* @param array $params Optional query parameters | |
* @return array List of tasks | |
*/ | |
public function getProjectTasks($projectId, $params = []) { | |
return $this->makeRequest('GET', '/projects/' . $projectId . '/tasks?' . http_build_query($params)); | |
} | |
/** | |
* Create a custom GET request to any Procore API endpoint | |
* | |
* @param string $endpoint API endpoint | |
* @param array $params Query parameters | |
* @return array Response data | |
*/ | |
public function get($endpoint, $params = []) { | |
$queryString = !empty($params) ? '?' . http_build_query($params) : ''; | |
return $this->makeRequest('GET', $endpoint . $queryString); | |
} | |
/** | |
* Create a custom POST request to any Procore API endpoint | |
* | |
* @param string $endpoint API endpoint | |
* @param array $data Request data | |
* @return array Response data | |
*/ | |
public function post($endpoint, $data = []) { | |
return $this->makeRequest('POST', $endpoint, $data); | |
} | |
/** | |
* Create a custom PUT request to any Procore API endpoint | |
* | |
* @param string $endpoint API endpoint | |
* @param array $data Request data | |
* @return array Response data | |
*/ | |
public function put($endpoint, $data = []) { | |
return $this->makeRequest('PUT', $endpoint, $data); | |
} | |
/** | |
* Create a custom DELETE request to any Procore API endpoint | |
* | |
* @param string $endpoint API endpoint | |
* @return array Response data | |
*/ | |
public function delete($endpoint) { | |
return $this->makeRequest('DELETE', $endpoint); | |
} | |
} | |
/** | |
* Example usage: | |
*/ | |
/* | |
// Initialize the connector | |
$clientId = 'your_client_id'; | |
$clientSecret = 'your_client_secret'; | |
$redirectUri = 'https://your-app.com/callback'; | |
$procore = new ProcoreConnector($clientId, $clientSecret, $redirectUri); | |
// If you don't have an authorization code yet | |
$authUrl = $procore->getAuthorizationUrl(); | |
echo "Please visit this URL to authorize the application: " . $authUrl . "\n"; | |
// After authorization, exchange the code for tokens | |
if (isset($_GET['code'])) { | |
$code = $_GET['code']; | |
if ($procore->exchangeAuthorizationCode($code)) { | |
echo "Authentication successful!\n"; | |
} else { | |
echo "Authentication failed.\n"; | |
} | |
} | |
// Get a list of projects | |
try { | |
$projects = $procore->getProjects(); | |
echo "Projects:\n"; | |
foreach ($projects as $project) { | |
echo "- {$project['name']} (ID: {$project['id']})\n"; | |
} | |
} catch (Exception $e) { | |
echo "Error: " . $e->getMessage() . "\n"; | |
} | |
*/ | |
?> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment