Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like Object_Sync_Sf_Salesforce often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Object_Sync_Sf_Salesforce, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
15 | class Object_Sync_Sf_Salesforce { |
||
16 | |||
17 | public $response; |
||
18 | |||
19 | /** |
||
20 | * Constructor which initializes the Salesforce APIs. |
||
21 | * |
||
22 | * @param string $consumer_key |
||
23 | * Salesforce key to connect to your Salesforce instance. |
||
24 | * @param string $consumer_secret |
||
25 | * Salesforce secret to connect to your Salesforce instance. |
||
26 | * @param string $login_url |
||
27 | * Login URL for Salesforce auth requests - differs for production and sandbox |
||
28 | * @param string $callback_url |
||
29 | * WordPress URL where Salesforce should send you after authentication |
||
30 | * @param string $authorize_path |
||
31 | * Oauth path that Salesforce wants |
||
32 | * @param string $token_path |
||
33 | * Path Salesforce uses to give you a token |
||
34 | * @param string $rest_api_version |
||
35 | * What version of the Salesforce REST API to use |
||
36 | * @param object $wordpress |
||
37 | * Object for doing things to WordPress - retrieving data, cache, etc. |
||
38 | * @param string $slug |
||
39 | * Slug for this plugin. Can be used for file including, especially |
||
40 | * @param object $logging |
||
41 | * Logging object for this plugin. |
||
42 | * @param array $schedulable_classes |
||
43 | * array of classes that can have scheduled tasks specific to them |
||
44 | * @param string $option_prefix |
||
45 | * Option prefix for this plugin. Used for getting and setting options, actions, etc. |
||
46 | */ |
||
47 | public function __construct( $consumer_key, $consumer_secret, $login_url, $callback_url, $authorize_path, $token_path, $rest_api_version, $wordpress, $slug, $logging, $schedulable_classes, $option_prefix = '' ) { |
||
74 | |||
75 | /** |
||
76 | * Converts a 15-character case-sensitive Salesforce ID to 18-character |
||
77 | * case-insensitive ID. If input is not 15-characters, return input unaltered. |
||
78 | * |
||
79 | * @param string $sf_id_15 |
||
80 | * 15-character case-sensitive Salesforce ID |
||
81 | * @return string |
||
82 | * 18-character case-insensitive Salesforce ID |
||
83 | */ |
||
84 | public static function convert_id( $sf_id_15 ) { |
||
101 | |||
102 | /** |
||
103 | * Given a Salesforce ID, return the corresponding SObject name. (Based on |
||
104 | * keyPrefix from object definition, @see |
||
105 | * https://developer.salesforce.com/forums/?id=906F0000000901ZIAQ ) |
||
106 | * |
||
107 | * @param string $sf_id |
||
108 | * 15- or 18-character Salesforce ID |
||
109 | * @return string |
||
110 | * sObject name, e.g. "Account", "Contact", "my__Custom_Object__c" or FALSE |
||
111 | * if no match could be found. |
||
112 | * @throws Object_Sync_Sf_Exception |
||
113 | */ |
||
114 | public function get_sobject_type( $sf_id ) { |
||
128 | |||
129 | /** |
||
130 | * Determine if this SF instance is fully configured. |
||
131 | * |
||
132 | */ |
||
133 | public function is_authorized() { |
||
136 | |||
137 | /** |
||
138 | * Get REST API versions available on this Salesforce organization |
||
139 | * This is not an authenticated call, so it would not be a helpful test |
||
140 | */ |
||
141 | public function get_api_versions() { |
||
148 | |||
149 | /** |
||
150 | * Make a call to the Salesforce REST API. |
||
151 | * |
||
152 | * @param string $path |
||
153 | * Path to resource. |
||
154 | * @param array $params |
||
155 | * Parameters to provide. |
||
156 | * @param string $method |
||
157 | * Method to initiate the call, such as GET or POST. Defaults to GET. |
||
158 | * @param array $options |
||
159 | * Any method can supply options for the API call, and they'll be preserved as far as the curl request |
||
160 | * They get merged with the class options |
||
161 | * @param string $type |
||
162 | * Type of call. Defaults to 'rest' - currently we don't support other types. |
||
163 | * Other exammple in Drupal is 'apexrest' |
||
164 | * |
||
165 | * @return mixed |
||
166 | * The requested response. |
||
167 | * |
||
168 | * @throws Object_Sync_Sf_Exception |
||
169 | */ |
||
170 | public function api_call( $path, $params = array(), $method = 'GET', $options = array(), $type = 'rest' ) { |
||
218 | |||
219 | /** |
||
220 | * Private helper to issue an SF API request. |
||
221 | * This method is the only place where we read to or write from the cache |
||
222 | * |
||
223 | * @param string $path |
||
224 | * Path to resource. |
||
225 | * @param array $params |
||
226 | * Parameters to provide. |
||
227 | * @param string $method |
||
228 | * Method to initiate the call, such as GET or POST. Defaults to GET. |
||
229 | * @param array $options |
||
230 | * This is the options array from the api_call method |
||
231 | * This is where it gets merged with $this->options |
||
232 | * @param string $type |
||
233 | * Type of call. Defaults to 'rest' - currently we don't support other types |
||
234 | * Other exammple in Drupal is 'apexrest' |
||
235 | * |
||
236 | * @return array |
||
237 | * The requested data. |
||
238 | */ |
||
239 | protected function api_http_request( $path, $params, $method, $options = array(), $type = 'rest' ) { |
||
320 | |||
321 | /** |
||
322 | * Make the HTTP request. Wrapper around curl(). |
||
323 | * |
||
324 | * @param string $url |
||
325 | * Path to make request from. |
||
326 | * @param array $data |
||
327 | * The request body. |
||
328 | * @param array $headers |
||
329 | * Request headers to send as name => value. |
||
330 | * @param string $method |
||
331 | * Method to initiate the call, such as GET or POST. Defaults to GET. |
||
332 | * @param array $options |
||
333 | * This is the options array from the api_http_request method |
||
334 | * |
||
335 | * @return array |
||
336 | * Salesforce response object. |
||
337 | */ |
||
338 | protected function http_request( $url, $data, $headers = array(), $method = 'GET', $options = array() ) { |
||
502 | |||
503 | /** |
||
504 | * Get the API end point for a given type of the API. |
||
505 | * |
||
506 | * @param string $api_type |
||
507 | * E.g., rest, partner, enterprise. |
||
508 | * |
||
509 | * @return string |
||
510 | * Complete URL endpoint for API access. |
||
511 | */ |
||
512 | public function get_api_endpoint( $api_type = 'rest' ) { |
||
525 | |||
526 | /** |
||
527 | * Get the SF instance URL. Useful for linking to objects. |
||
528 | */ |
||
529 | public function get_instance_url() { |
||
532 | |||
533 | /** |
||
534 | * Set the SF instanc URL. |
||
535 | * |
||
536 | * @param string $url |
||
537 | * URL to set. |
||
538 | */ |
||
539 | protected function set_instance_url( $url ) { |
||
542 | |||
543 | /** |
||
544 | * Get the access token. |
||
545 | */ |
||
546 | public function get_access_token() { |
||
549 | |||
550 | /** |
||
551 | * Set the access token. |
||
552 | * |
||
553 | * It is stored in session. |
||
554 | * |
||
555 | * @param string $token |
||
556 | * Access token from Salesforce. |
||
557 | */ |
||
558 | protected function set_access_token( $token ) { |
||
561 | |||
562 | /** |
||
563 | * Get refresh token. |
||
564 | */ |
||
565 | protected function get_refresh_token() { |
||
568 | |||
569 | /** |
||
570 | * Set refresh token. |
||
571 | * |
||
572 | * @param string $token |
||
573 | * Refresh token from Salesforce. |
||
574 | */ |
||
575 | protected function set_refresh_token( $token ) { |
||
578 | |||
579 | /** |
||
580 | * Refresh access token based on the refresh token. Updates session variable. |
||
581 | * |
||
582 | * todo: figure out how to do this as part of the schedule class |
||
583 | * this is a scheduleable class and so we could add a method from this class to run every 24 hours, but it's unclear to me that we need it. salesforce seems to refresh itself as it needs to. |
||
584 | * but it could be a performance boost to do it at scheduleable intervals instead. |
||
585 | * |
||
586 | * @throws Object_Sync_Sf_Exception |
||
587 | */ |
||
588 | protected function refresh_token() { |
||
632 | |||
633 | /** |
||
634 | * Retrieve and store the Salesforce identity given an ID url. |
||
635 | * |
||
636 | * @param string $id |
||
637 | * Identity URL. |
||
638 | * |
||
639 | * @throws Object_Sync_Sf_Exception |
||
640 | */ |
||
641 | protected function set_identity( $id ) { |
||
654 | |||
655 | /** |
||
656 | * Return the Salesforce identity, which is stored in a variable. |
||
657 | * |
||
658 | * @return array |
||
659 | * Returns FALSE if no identity has been stored. |
||
660 | */ |
||
661 | public function get_identity() { |
||
664 | |||
665 | /** |
||
666 | * OAuth step 1: Redirect to Salesforce and request and authorization code. |
||
667 | */ |
||
668 | public function get_authorization_code() { |
||
679 | |||
680 | /** |
||
681 | * OAuth step 2: Exchange an authorization code for an access token. |
||
682 | * |
||
683 | * @param string $code |
||
684 | * Code from Salesforce. |
||
685 | */ |
||
686 | public function request_token( $code ) { |
||
726 | |||
727 | /* Core API calls */ |
||
728 | |||
729 | /** |
||
730 | * Available objects and their metadata for your organization's data. |
||
731 | * |
||
732 | * @param array $conditions |
||
733 | * Associative array of filters to apply to the returned objects. Filters |
||
734 | * are applied after the list is returned from Salesforce. |
||
735 | * @param bool $reset |
||
736 | * Whether to reset the cache and retrieve a fresh version from Salesforce. |
||
737 | * |
||
738 | * @return array |
||
739 | * Available objects and metadata. |
||
740 | * |
||
741 | * part of core API calls. this call does require authentication, and the basic url it becomes is like this: |
||
742 | * https://instance.salesforce.com/services/data/v#.0/sobjects |
||
743 | * |
||
744 | * updateable is really how the api spells it |
||
745 | */ |
||
746 | public function objects( |
||
773 | |||
774 | /** |
||
775 | * Use SOQL to get objects based on query string. |
||
776 | * |
||
777 | * @param string $query |
||
778 | * The SOQL query. |
||
779 | * @param array $options |
||
780 | * Allow for the query to have options based on what the user needs from it, ie caching, read/write, etc. |
||
781 | * @param boolean $all |
||
782 | * Whether this should get all results for the query |
||
783 | * @param boolean $explain |
||
784 | * If set, Salesforce will return feedback on the query performance |
||
785 | * |
||
786 | * @return array |
||
787 | * Array of Salesforce objects that match the query. |
||
788 | * |
||
789 | * part of core API calls |
||
790 | */ |
||
791 | public function query( $query, $options = array(), $all = false, $explain = false ) { |
||
808 | |||
809 | /** |
||
810 | * Retrieve all the metadata for an object. |
||
811 | * |
||
812 | * @param string $name |
||
813 | * Object type name, E.g., Contact, Account, etc. |
||
814 | * @param bool $reset |
||
815 | * Whether to reset the cache and retrieve a fresh version from Salesforce. |
||
816 | * |
||
817 | * @return array |
||
818 | * All the metadata for an object, including information about each field, |
||
819 | * URLs, and child relationships. |
||
820 | * |
||
821 | * part of core API calls |
||
822 | */ |
||
823 | public function object_describe( $name, $reset = false ) { |
||
848 | |||
849 | /** |
||
850 | * Create a new object of the given type. |
||
851 | * |
||
852 | * @param string $name |
||
853 | * Object type name, E.g., Contact, Account, etc. |
||
854 | * @param array $params |
||
855 | * Values of the fields to set for the object. |
||
856 | * |
||
857 | * @return array |
||
858 | * json: {"id":"00190000001pPvHAAU","success":true,"errors":[]} |
||
859 | * code: 201 |
||
860 | * data: |
||
861 | * "id" : "00190000001pPvHAAU", |
||
862 | * "success" : true |
||
863 | * "errors" : [ ], |
||
864 | * from_cache: |
||
865 | * cached: |
||
866 | * is_redo: |
||
867 | * |
||
868 | * part of core API calls |
||
869 | */ |
||
870 | View Code Duplication | public function object_create( $name, $params ) { |
|
877 | |||
878 | /** |
||
879 | * Create new records or update existing records. |
||
880 | * |
||
881 | * The new records or updated records are based on the value of the specified |
||
882 | * field. If the value is not unique, REST API returns a 300 response with |
||
883 | * the list of matching records. |
||
884 | * |
||
885 | * @param string $name |
||
886 | * Object type name, E.g., Contact, Account. |
||
887 | * @param string $key |
||
888 | * The field to check if this record should be created or updated. |
||
889 | * @param string $value |
||
890 | * The value for this record of the field specified for $key. |
||
891 | * @param array $params |
||
892 | * Values of the fields to set for the object. |
||
893 | * |
||
894 | * @return array |
||
895 | * json: {"id":"00190000001pPvHAAU","success":true,"errors":[]} |
||
896 | * code: 201 |
||
897 | * data: |
||
898 | * "id" : "00190000001pPvHAAU", |
||
899 | * "success" : true |
||
900 | * "errors" : [ ], |
||
901 | * from_cache: |
||
902 | * cached: |
||
903 | * is_redo: |
||
904 | * |
||
905 | * part of core API calls |
||
906 | */ |
||
907 | public function object_upsert( $name, $key, $value, $params ) { |
||
926 | |||
927 | /** |
||
928 | * Update an existing object. |
||
929 | * |
||
930 | * @param string $name |
||
931 | * Object type name, E.g., Contact, Account. |
||
932 | * @param string $id |
||
933 | * Salesforce id of the object. |
||
934 | * @param array $params |
||
935 | * Values of the fields to set for the object. |
||
936 | * |
||
937 | * part of core API calls |
||
938 | * |
||
939 | * @return array |
||
940 | * json: {"success":true,"body":""} |
||
941 | * code: 204 |
||
942 | * data: |
||
943 | success: 1 |
||
944 | body: |
||
945 | * from_cache: |
||
946 | * cached: |
||
947 | * is_redo: |
||
948 | */ |
||
949 | View Code Duplication | public function object_update( $name, $id, $params ) { |
|
956 | |||
957 | /** |
||
958 | * Return a full loaded Salesforce object. |
||
959 | * |
||
960 | * @param string $name |
||
961 | * Object type name, E.g., Contact, Account. |
||
962 | * @param string $id |
||
963 | * Salesforce id of the object. |
||
964 | * @param array $options |
||
965 | * Optional options to pass to the API call |
||
966 | * |
||
967 | * @return object |
||
968 | * Object of the requested Salesforce object. |
||
969 | * |
||
970 | * part of core API calls |
||
971 | */ |
||
972 | public function object_read( $name, $id, $options = array() ) { |
||
975 | |||
976 | /** |
||
977 | * Make a call to the Analytics API |
||
978 | * |
||
979 | * @param string $name |
||
980 | * Object type name, E.g., Report |
||
981 | * @param string $id |
||
982 | * Salesforce id of the object. |
||
983 | * @param string $route |
||
984 | * What comes after the ID? E.g. instances, ?includeDetails=True |
||
985 | * @param array $params |
||
986 | * Params to put with the request |
||
987 | * @param string $method |
||
988 | * GET or POST |
||
989 | * |
||
990 | * @return object |
||
991 | * Object of the requested Salesforce object. |
||
992 | * |
||
993 | * part of core API calls |
||
994 | */ |
||
995 | public function analytics_api( $name, $id, $route = '', $params = array(), $method = 'GET' ) { |
||
998 | |||
999 | /** |
||
1000 | * Run a specific Analytics report |
||
1001 | * |
||
1002 | * @param string $id |
||
1003 | * Salesforce id of the object. |
||
1004 | * @param bool $async |
||
1005 | * Whether the report is asynchronous |
||
1006 | * @param array $params |
||
1007 | * Params to put with the request |
||
1008 | * @param string $method |
||
1009 | * GET or POST |
||
1010 | * |
||
1011 | * @return object |
||
1012 | * Object of the requested Salesforce object. |
||
1013 | * |
||
1014 | * part of core API calls |
||
1015 | */ |
||
1016 | public function run_analytics_report( $id, $async = true, $clear_cache = false, $params = array(), $method = 'GET', $report_cache_expiration = '', $cache_instance = true, $instance_cache_expiration = '' ) { |
||
1099 | |||
1100 | /** |
||
1101 | * Return a full loaded Salesforce object from External ID. |
||
1102 | * |
||
1103 | * @param string $name |
||
1104 | * Object type name, E.g., Contact, Account. |
||
1105 | * @param string $field |
||
1106 | * Salesforce external id field name. |
||
1107 | * @param string $value |
||
1108 | * Value of external id. |
||
1109 | * @param array $options |
||
1110 | * Optional options to pass to the API call |
||
1111 | * |
||
1112 | * @return object |
||
1113 | * Object of the requested Salesforce object. |
||
1114 | * |
||
1115 | * part of core API calls |
||
1116 | */ |
||
1117 | public function object_readby_external_id( $name, $field, $value, $options = array() ) { |
||
1120 | |||
1121 | /** |
||
1122 | * Delete a Salesforce object. |
||
1123 | * |
||
1124 | * @param string $name |
||
1125 | * Object type name, E.g., Contact, Account. |
||
1126 | * @param string $id |
||
1127 | * Salesforce id of the object. |
||
1128 | * |
||
1129 | * @return array |
||
1130 | * |
||
1131 | * part of core API calls |
||
1132 | */ |
||
1133 | View Code Duplication | public function object_delete( $name, $id ) { |
|
1140 | |||
1141 | /** |
||
1142 | * Retrieves the list of individual objects that have been deleted within the |
||
1143 | * given timespan for a specified object type. |
||
1144 | * |
||
1145 | * @param string $type |
||
1146 | * Object type name, E.g., Contact, Account. |
||
1147 | * @param string $startDate |
||
1148 | * Start date to check for deleted objects (in ISO 8601 format). |
||
1149 | * @param string $endDate |
||
1150 | * End date to check for deleted objects (in ISO 8601 format). |
||
1151 | * @return GetDeletedResult |
||
1152 | */ |
||
1153 | public function get_deleted( $type, $start_date, $end_date ) { |
||
1159 | |||
1160 | |||
1161 | /** |
||
1162 | * Return a list of available resources for the configured API version. |
||
1163 | * |
||
1164 | * @return array |
||
1165 | * Associative array keyed by name with a URI value. |
||
1166 | * |
||
1167 | * part of core API calls |
||
1168 | */ |
||
1169 | public function list_resources() { |
||
1176 | |||
1177 | /** |
||
1178 | * Return a list of SFIDs for the given object, which have been created or |
||
1179 | * updated in the given timeframe. |
||
1180 | * |
||
1181 | * @param string $type |
||
1182 | * Object type name, E.g., Contact, Account. |
||
1183 | * |
||
1184 | * @param int $start |
||
1185 | * unix timestamp for older timeframe for updates. |
||
1186 | * Defaults to "-29 days" if empty. |
||
1187 | * |
||
1188 | * @param int $end |
||
1189 | * unix timestamp for end of timeframe for updates. |
||
1190 | * Defaults to now if empty |
||
1191 | * |
||
1192 | * @return array |
||
1193 | * return array has 2 indexes: |
||
1194 | * "ids": a list of SFIDs of those records which have been created or |
||
1195 | * updated in the given timeframe. |
||
1196 | * "latestDateCovered": ISO 8601 format timestamp (UTC) of the last date |
||
1197 | * covered in the request. |
||
1198 | * |
||
1199 | * @see https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_getupdated.htm |
||
1200 | * |
||
1201 | * part of core API calls |
||
1202 | */ |
||
1203 | public function get_updated( $type, $start = null, $end = null ) { |
||
1219 | |||
1220 | /** |
||
1221 | * Given a DeveloperName and SObject Name, return the SFID of the |
||
1222 | * corresponding RecordType. DeveloperName doesn't change between Salesforce |
||
1223 | * environments, so it's safer to rely on compared to SFID. |
||
1224 | * |
||
1225 | * @param string $name |
||
1226 | * Object type name, E.g., Contact, Account. |
||
1227 | * |
||
1228 | * @param string $devname |
||
1229 | * RecordType DeveloperName, e.g. Donation, Membership, etc. |
||
1230 | * |
||
1231 | * @return string SFID |
||
1232 | * The Salesforce ID of the given Record Type, or null. |
||
1233 | */ |
||
1234 | |||
1235 | public function get_record_type_id_by_developer_name( $name, $devname, $reset = false ) { |
||
1259 | |||
1260 | /** |
||
1261 | * If there is a WordPress setting for how long to keep the cache, return it and set the object property |
||
1262 | * Otherwise, return seconds in 24 hours |
||
1263 | * |
||
1264 | */ |
||
1265 | private function cache_expiration() { |
||
1269 | |||
1270 | } |
||
1271 | |||
1274 |
This check looks for type mismatches where the missing type is
false
. This is usually indicative of an error condtion.Consider the follow example
This function either returns a new
DateTime
object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returnedfalse
before passing on the value to another function or method that may not be able to handle afalse
.