├── Readme.txt
├── fbmain.php
├── ajax.php
├── index.php
├── home.php
├── facebook.php
├── template.php
├── fb_ca_chain_bundle.crt
└── base_facebook.php
/Readme.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mahmudahsan/Graph-API---IFrame-Base-Facebook-Application-Development-PHP-SDK-3.0--Source/HEAD/Readme.txt
--------------------------------------------------------------------------------
/fbmain.php:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mahmudahsan/Graph-API---IFrame-Base-Facebook-Application-Development-PHP-SDK-3.0--Source/HEAD/fbmain.php
--------------------------------------------------------------------------------
/ajax.php:
--------------------------------------------------------------------------------
1 | api('/me/feed', 'post', array('message'=> $status, 'cb' => ''));
10 | } catch (FacebookApiException $e) {
11 | d($e);
12 | }
13 | echo "Status Update Successfull. ";
14 | exit;
15 | }
16 | }
17 | ?>
--------------------------------------------------------------------------------
/index.php:
--------------------------------------------------------------------------------
1 | 'fql.query',
13 | 'query' => $fql,
14 | 'callback' => ''
15 | );
16 | $fqlResult = $facebook->api($param);
17 | }
18 | catch(Exception $o){
19 | d($o);
20 | }
21 | }
22 | //set page to include default is home.php
23 | $page = isset($_REQUEST['page']) ? $_REQUEST['page'] : "home.php";
24 | include_once "template.php";
25 | ?>
--------------------------------------------------------------------------------
/home.php:
--------------------------------------------------------------------------------
1 |
15 |
16 |
23 |
24 |

25 |
26 |
27 |
28 | PHP Session
29 |
30 | User's Basic Info
31 |
32 |
33 |
34 |
42 |
45 |
46 |
53 |
54 |
62 |
--------------------------------------------------------------------------------
/facebook.php:
--------------------------------------------------------------------------------
1 | constructSessionVariableName($key);
58 | $_SESSION[$session_var_name] = $value;
59 | }
60 |
61 | protected function getPersistentData($key, $default = false) {
62 | if (!in_array($key, self::$kSupportedKeys)) {
63 | self::errorLog('Unsupported key passed to getPersistentData.');
64 | return $default;
65 | }
66 |
67 | $session_var_name = $this->constructSessionVariableName($key);
68 | return isset($_SESSION[$session_var_name]) ?
69 | $_SESSION[$session_var_name] : $default;
70 | }
71 |
72 | protected function clearPersistentData($key) {
73 | if (!in_array($key, self::$kSupportedKeys)) {
74 | self::errorLog('Unsupported key passed to clearPersistentData.');
75 | return;
76 | }
77 |
78 | $session_var_name = $this->constructSessionVariableName($key);
79 | unset($_SESSION[$session_var_name]);
80 | }
81 |
82 | protected function clearAllPersistentData() {
83 | foreach (self::$kSupportedKeys as $key) {
84 | $this->clearPersistentData($key);
85 | }
86 | }
87 |
88 | protected function constructSessionVariableName($key) {
89 | return implode('_', array('fb',
90 | $this->getAppId(),
91 | $key));
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/template.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IFrame Base Facebook Application Development Updated. Using PHP SDK 3.0 | Thinkdiff.net
6 |
7 |
8 |
9 |
19 |
100 |
101 |
102 |
103 |
104 |
113 |
114 |
115 |
116 | IFrame Base Facebook Application Development | Thinkdiff.net
117 | Tutorial: How to develop iframe base facebook application
118 |
119 | Home |
120 | Send Request/Send Invitation
121 |
122 |
123 |
129 |
130 |
--------------------------------------------------------------------------------
/fb_ca_chain_bundle.crt:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIFgjCCBGqgAwIBAgIQDKKbZcnESGaLDuEaVk6fQjANBgkqhkiG9w0BAQUFADBm
3 | MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
4 | d3cuZGlnaWNlcnQuY29tMSUwIwYDVQQDExxEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
5 | ZSBDQS0zMB4XDTEwMDExMzAwMDAwMFoXDTEzMDQxMTIzNTk1OVowaDELMAkGA1UE
6 | BhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExEjAQBgNVBAcTCVBhbG8gQWx0bzEX
7 | MBUGA1UEChMORmFjZWJvb2ssIEluYy4xFzAVBgNVBAMUDiouZmFjZWJvb2suY29t
8 | MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC9rzj7QIuLM3sdHu1HcI1VcR3g
9 | b5FExKNV646agxSle1aQ/sJev1mh/u91ynwqd2BQmM0brZ1Hc3QrfYyAaiGGgEkp
10 | xbhezyfeYhAyO0TKAYxPnm2cTjB5HICzk6xEIwFbA7SBJ2fSyW1CFhYZyo3tIBjj
11 | 19VjKyBfpRaPkzLmRwIDAQABo4ICrDCCAqgwHwYDVR0jBBgwFoAUUOpzidsp+xCP
12 | nuUBINTeeZlIg/cwHQYDVR0OBBYEFPp+tsFBozkjrHlEnZ9J4cFj2eM0MA4GA1Ud
13 | DwEB/wQEAwIFoDAMBgNVHRMBAf8EAjAAMF8GA1UdHwRYMFYwKaAnoCWGI2h0dHA6
14 | Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9jYTMtZmIuY3JsMCmgJ6AlhiNodHRwOi8vY3Js
15 | NC5kaWdpY2VydC5jb20vY2EzLWZiLmNybDCCAcYGA1UdIASCAb0wggG5MIIBtQYL
16 | YIZIAYb9bAEDAAEwggGkMDoGCCsGAQUFBwIBFi5odHRwOi8vd3d3LmRpZ2ljZXJ0
17 | LmNvbS9zc2wtY3BzLXJlcG9zaXRvcnkuaHRtMIIBZAYIKwYBBQUHAgIwggFWHoIB
18 | UgBBAG4AeQAgAHUAcwBlACAAbwBmACAAdABoAGkAcwAgAEMAZQByAHQAaQBmAGkA
19 | YwBhAHQAZQAgAGMAbwBuAHMAdABpAHQAdQB0AGUAcwAgAGEAYwBjAGUAcAB0AGEA
20 | bgBjAGUAIABvAGYAIAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAgAEMAUAAvAEMA
21 | UABTACAAYQBuAGQAIAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQAGEAcgB0AHkA
22 | IABBAGcAcgBlAGUAbQBlAG4AdAAgAHcAaABpAGMAaAAgAGwAaQBtAGkAdAAgAGwA
23 | aQBhAGIAaQBsAGkAdAB5ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBjAG8AcgBwAG8A
24 | cgBhAHQAZQBkACAAaABlAHIAZQBpAG4AIABiAHkAIAByAGUAZgBlAHIAZQBuAGMA
25 | ZQAuMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjANBgkqhkiG9w0BAQUF
26 | AAOCAQEACOkTIdxMy11+CKrbGNLBSg5xHaTvu/v1wbyn3dO/mf68pPfJnX6ShPYy
27 | 4XM4Vk0x4uaFaU4wAGke+nCKGi5dyg0Esg7nemLNKEJaFAJZ9enxZm334lSCeARy
28 | wlDtxULGOFRyGIZZPmbV2eNq5xdU/g3IuBEhL722mTpAye9FU/J8Wsnw54/gANyO
29 | Gzkewigua8ip8Lbs9Cht399yAfbfhUP1DrAm/xEcnHrzPr3cdCtOyJaM6SRPpRqH
30 | ITK5Nc06tat9lXVosSinT3KqydzxBYua9gCFFiR3x3DgZfvXkC6KDdUlDrNcJUub
31 | a1BHnLLP4mxTHL6faAXYd05IxNn/IA==
32 | -----END CERTIFICATE-----
33 | -----BEGIN CERTIFICATE-----
34 | MIIGVTCCBT2gAwIBAgIQCFH5WYFBRcq94CTiEsnCDjANBgkqhkiG9w0BAQUFADBs
35 | MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
36 | d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
37 | ZSBFViBSb290IENBMB4XDTA3MDQwMzAwMDAwMFoXDTIyMDQwMzAwMDAwMFowZjEL
38 | MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
39 | LmRpZ2ljZXJ0LmNvbTElMCMGA1UEAxMcRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
40 | Q0EtMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9hCikQH17+NDdR
41 | CPge+yLtYb4LDXBMUGMmdRW5QYiXtvCgFbsIYOBC6AUpEIc2iihlqO8xB3RtNpcv
42 | KEZmBMcqeSZ6mdWOw21PoF6tvD2Rwll7XjZswFPPAAgyPhBkWBATaccM7pxCUQD5
43 | BUTuJM56H+2MEb0SqPMV9Bx6MWkBG6fmXcCabH4JnudSREoQOiPkm7YDr6ictFuf
44 | 1EutkozOtREqqjcYjbTCuNhcBoz4/yO9NV7UfD5+gw6RlgWYw7If48hl66l7XaAs
45 | zPw82W3tzPpLQ4zJ1LilYRyyQLYoEt+5+F/+07LJ7z20Hkt8HEyZNp496+ynaF4d
46 | 32duXvsCAwEAAaOCAvcwggLzMA4GA1UdDwEB/wQEAwIBhjCCAcYGA1UdIASCAb0w
47 | ggG5MIIBtQYLYIZIAYb9bAEDAAIwggGkMDoGCCsGAQUFBwIBFi5odHRwOi8vd3d3
48 | LmRpZ2ljZXJ0LmNvbS9zc2wtY3BzLXJlcG9zaXRvcnkuaHRtMIIBZAYIKwYBBQUH
49 | AgIwggFWHoIBUgBBAG4AeQAgAHUAcwBlACAAbwBmACAAdABoAGkAcwAgAEMAZQBy
50 | AHQAaQBmAGkAYwBhAHQAZQAgAGMAbwBuAHMAdABpAHQAdQB0AGUAcwAgAGEAYwBj
51 | AGUAcAB0AGEAbgBjAGUAIABvAGYAIAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAg
52 | AEMAUAAvAEMAUABTACAAYQBuAGQAIAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQ
53 | AGEAcgB0AHkAIABBAGcAcgBlAGUAbQBlAG4AdAAgAHcAaABpAGMAaAAgAGwAaQBt
54 | AGkAdAAgAGwAaQBhAGIAaQBsAGkAdAB5ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBj
55 | AG8AcgBwAG8AcgBhAHQAZQBkACAAaABlAHIAZQBpAG4AIABiAHkAIAByAGUAZgBl
56 | AHIAZQBuAGMAZQAuMA8GA1UdEwEB/wQFMAMBAf8wNAYIKwYBBQUHAQEEKDAmMCQG
57 | CCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wgY8GA1UdHwSBhzCB
58 | hDBAoD6gPIY6aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0SGlnaEFz
59 | c3VyYW5jZUVWUm9vdENBLmNybDBAoD6gPIY6aHR0cDovL2NybDQuZGlnaWNlcnQu
60 | Y29tL0RpZ2lDZXJ0SGlnaEFzc3VyYW5jZUVWUm9vdENBLmNybDAfBgNVHSMEGDAW
61 | gBSxPsNpA/i/RwHUmCYaCALvY2QrwzAdBgNVHQ4EFgQUUOpzidsp+xCPnuUBINTe
62 | eZlIg/cwDQYJKoZIhvcNAQEFBQADggEBAF1PhPGoiNOjsrycbeUpSXfh59bcqdg1
63 | rslx3OXb3J0kIZCmz7cBHJvUV5eR13UWpRLXuT0uiT05aYrWNTf58SHEW0CtWakv
64 | XzoAKUMncQPkvTAyVab+hA4LmzgZLEN8rEO/dTHlIxxFVbdpCJG1z9fVsV7un5Tk
65 | 1nq5GMO41lJjHBC6iy9tXcwFOPRWBW3vnuzoYTYMFEuFFFoMg08iXFnLjIpx2vrF
66 | EIRYzwfu45DC9fkpx1ojcflZtGQriLCnNseaIGHr+k61rmsb5OPs4tk8QUmoIKRU
67 | 9ZKNu8BVIASm2LAXFszj0Mi0PeXZhMbT9m5teMl5Q+h6N/9cNUm/ocU=
68 | -----END CERTIFICATE-----
69 | -----BEGIN CERTIFICATE-----
70 | MIIEQjCCA6ugAwIBAgIEQoclDjANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC
71 | VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u
72 | ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc
73 | KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u
74 | ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEy
75 | MjIxNTI3MjdaFw0xNDA3MjIxNTU3MjdaMGwxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
76 | EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xKzApBgNV
77 | BAMTIkRpZ2lDZXJ0IEhpZ2ggQXNzdXJhbmNlIEVWIFJvb3QgQ0EwggEiMA0GCSqG
78 | SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGzOVz5vvUu+UtLTKm3+WBP8nNJUm2cSrD
79 | 1ZQ0Z6IKHLBfaaZAscS3so/QmKSpQVk609yU1jzbdDikSsxNJYL3SqVTEjju80lt
80 | cZF+Y7arpl/DpIT4T2JRvvjF7Ns4kuMG5QiRDMQoQVX7y1qJFX5x6DW/TXIJPb46
81 | OFBbdzEbjbPHJEWap6xtABRaBLe6E+tRCphBQSJOZWGHgUFQpnlcid4ZSlfVLuZd
82 | HFMsfpjNGgYWpGhz0DQEE1yhcdNafFXbXmThN4cwVgTlEbQpgBLxeTmIogIRfCdm
83 | t4i3ePLKCqg4qwpkwr9mXZWEwaElHoddGlALIBLMQbtuC1E4uEvLAgMBAAGjggET
84 | MIIBDzASBgNVHRMBAf8ECDAGAQH/AgEBMCcGA1UdJQQgMB4GCCsGAQUFBwMBBggr
85 | BgEFBQcDAgYIKwYBBQUHAwQwMwYIKwYBBQUHAQEEJzAlMCMGCCsGAQUFBzABhhdo
86 | dHRwOi8vb2NzcC5lbnRydXN0Lm5ldDAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8v
87 | Y3JsLmVudHJ1c3QubmV0L3NlcnZlcjEuY3JsMB0GA1UdDgQWBBSxPsNpA/i/RwHU
88 | mCYaCALvY2QrwzALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8BdiE1U9s/8KAGv7
89 | UISX8+1i0BowGQYJKoZIhvZ9B0EABAwwChsEVjcuMQMCAIEwDQYJKoZIhvcNAQEF
90 | BQADgYEAUuVY7HCc/9EvhaYzC1rAIo348LtGIiMduEl5Xa24G8tmJnDioD2GU06r
91 | 1kjLX/ktCdpdBgXadbjtdrZXTP59uN0AXlsdaTiFufsqVLPvkp5yMnqnuI3E2o6p
92 | NpAkoQSbB6kUCNnXcW26valgOjDLZFOnr241QiwdBAJAAE/rRa8=
93 | -----END CERTIFICATE-----
94 | -----BEGIN CERTIFICATE-----
95 | MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC
96 | VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u
97 | ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc
98 | KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u
99 | ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw05OTA1
100 | MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQswCQYDVQQGEwJVUzEUMBIGA1UE
101 | ChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5j
102 | b3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBF
103 | bnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUg
104 | U2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUA
105 | A4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQaO2f55M28Qpku0f1BBc/
106 | I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5gXpa0zf3
107 | wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OC
108 | AdcwggHTMBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHb
109 | oIHYpIHVMIHSMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5
110 | BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1p
111 | dHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1pdGVk
112 | MTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRp
113 | b24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNodHRwOi8vd3d3LmVu
114 | dHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAigA8xOTk5MDUyNTE2MDk0
115 | MFqBDzIwMTkwNTI1MTYwOTQwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8Bdi
116 | E1U9s/8KAGv7UISX8+1i0BowHQYDVR0OBBYEFPAXYhNVPbP/CgBr+1CEl/PtYtAa
117 | MAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EABAwwChsEVjQuMAMCBJAwDQYJKoZI
118 | hvcNAQEFBQADgYEAkNwwAvpkdMKnCqV8IY00F6j7Rw7/JXyNEwr75Ji174z4xRAN
119 | 95K+8cPV1ZVqBLssziY2ZcgxxufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9n9cd
120 | 2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI=
121 | -----END CERTIFICATE-----
122 |
--------------------------------------------------------------------------------
/base_facebook.php:
--------------------------------------------------------------------------------
1 |
29 | */
30 | class FacebookApiException extends Exception
31 | {
32 | /**
33 | * The result from the API server that represents the exception information.
34 | */
35 | protected $result;
36 |
37 | /**
38 | * Make a new API Exception with the given result.
39 | *
40 | * @param array $result The result from the API server
41 | */
42 | public function __construct($result) {
43 | $this->result = $result;
44 |
45 | $code = isset($result['error_code']) ? $result['error_code'] : 0;
46 |
47 | if (isset($result['error_description'])) {
48 | // OAuth 2.0 Draft 10 style
49 | $msg = $result['error_description'];
50 | } else if (isset($result['error']) && is_array($result['error'])) {
51 | // OAuth 2.0 Draft 00 style
52 | $msg = $result['error']['message'];
53 | } else if (isset($result['error_msg'])) {
54 | // Rest server style
55 | $msg = $result['error_msg'];
56 | } else {
57 | $msg = 'Unknown Error. Check getResult()';
58 | }
59 |
60 | parent::__construct($msg, $code);
61 | }
62 |
63 | /**
64 | * Return the associated result object returned by the API server.
65 | *
66 | * @return array The result from the API server
67 | */
68 | public function getResult() {
69 | return $this->result;
70 | }
71 |
72 | /**
73 | * Returns the associated type for the error. This will default to
74 | * 'Exception' when a type is not available.
75 | *
76 | * @return string
77 | */
78 | public function getType() {
79 | if (isset($this->result['error'])) {
80 | $error = $this->result['error'];
81 | if (is_string($error)) {
82 | // OAuth 2.0 Draft 10 style
83 | return $error;
84 | } else if (is_array($error)) {
85 | // OAuth 2.0 Draft 00 style
86 | if (isset($error['type'])) {
87 | return $error['type'];
88 | }
89 | }
90 | }
91 |
92 | return 'Exception';
93 | }
94 |
95 | /**
96 | * To make debugging easier.
97 | *
98 | * @return string The string representation of the error
99 | */
100 | public function __toString() {
101 | $str = $this->getType() . ': ';
102 | if ($this->code != 0) {
103 | $str .= $this->code . ': ';
104 | }
105 | return $str . $this->message;
106 | }
107 | }
108 |
109 | /**
110 | * Provides access to the Facebook Platform. This class provides
111 | * a majority of the functionality needed, but the class is abstract
112 | * because it is designed to be sub-classed. The subclass must
113 | * implement the four abstract methods listed at the bottom of
114 | * the file.
115 | *
116 | * @author Naitik Shah
117 | */
118 | abstract class BaseFacebook
119 | {
120 | /**
121 | * Version.
122 | */
123 | const VERSION = '3.1.1';
124 |
125 | /**
126 | * Default options for curl.
127 | */
128 | public static $CURL_OPTS = array(
129 | CURLOPT_CONNECTTIMEOUT => 10,
130 | CURLOPT_RETURNTRANSFER => true,
131 | CURLOPT_TIMEOUT => 60,
132 | CURLOPT_USERAGENT => 'facebook-php-3.1',
133 | );
134 |
135 | /**
136 | * List of query parameters that get automatically dropped when rebuilding
137 | * the current URL.
138 | */
139 | protected static $DROP_QUERY_PARAMS = array(
140 | 'code',
141 | 'state',
142 | 'signed_request',
143 | );
144 |
145 | /**
146 | * Maps aliases to Facebook domains.
147 | */
148 | public static $DOMAIN_MAP = array(
149 | 'api' => 'https://api.facebook.com/',
150 | 'api_video' => 'https://api-video.facebook.com/',
151 | 'api_read' => 'https://api-read.facebook.com/',
152 | 'graph' => 'https://graph.facebook.com/',
153 | 'www' => 'https://www.facebook.com/',
154 | );
155 |
156 | /**
157 | * The Application ID.
158 | *
159 | * @var string
160 | */
161 | protected $appId;
162 |
163 | /**
164 | * The Application API Secret.
165 | *
166 | * @var string
167 | */
168 | protected $apiSecret;
169 |
170 | /**
171 | * The ID of the Facebook user, or 0 if the user is logged out.
172 | *
173 | * @var integer
174 | */
175 | protected $user;
176 |
177 | /**
178 | * The data from the signed_request token.
179 | */
180 | protected $signedRequest;
181 |
182 | /**
183 | * A CSRF state variable to assist in the defense against CSRF attacks.
184 | */
185 | protected $state;
186 |
187 | /**
188 | * The OAuth access token received in exchange for a valid authorization
189 | * code. null means the access token has yet to be determined.
190 | *
191 | * @var string
192 | */
193 | protected $accessToken = null;
194 |
195 | /**
196 | * Indicates if the CURL based @ syntax for file uploads is enabled.
197 | *
198 | * @var boolean
199 | */
200 | protected $fileUploadSupport = false;
201 |
202 | /**
203 | * Initialize a Facebook Application.
204 | *
205 | * The configuration:
206 | * - appId: the application ID
207 | * - secret: the application secret
208 | * - fileUpload: (optional) boolean indicating if file uploads are enabled
209 | *
210 | * @param array $config The application configuration
211 | */
212 | public function __construct($config) {
213 | $this->setAppId($config['appId']);
214 | $this->setApiSecret($config['secret']);
215 | if (isset($config['fileUpload'])) {
216 | $this->setFileUploadSupport($config['fileUpload']);
217 | }
218 |
219 | $state = $this->getPersistentData('state');
220 | if (!empty($state)) {
221 | $this->state = $this->getPersistentData('state');
222 | }
223 | }
224 |
225 | /**
226 | * Set the Application ID.
227 | *
228 | * @param string $appId The Application ID
229 | * @return BaseFacebook
230 | */
231 | public function setAppId($appId) {
232 | $this->appId = $appId;
233 | return $this;
234 | }
235 |
236 | /**
237 | * Get the Application ID.
238 | *
239 | * @return string the Application ID
240 | */
241 | public function getAppId() {
242 | return $this->appId;
243 | }
244 |
245 | /**
246 | * Set the API Secret.
247 | *
248 | * @param string $apiSecret The API Secret
249 | * @return BaseFacebook
250 | */
251 | public function setApiSecret($apiSecret) {
252 | $this->apiSecret = $apiSecret;
253 | return $this;
254 | }
255 |
256 | /**
257 | * Get the API Secret.
258 | *
259 | * @return string the API Secret
260 | */
261 | public function getApiSecret() {
262 | return $this->apiSecret;
263 | }
264 |
265 | /**
266 | * Set the file upload support status.
267 | *
268 | * @param boolean $fileUploadSupport The file upload support status.
269 | * @return BaseFacebook
270 | */
271 | public function setFileUploadSupport($fileUploadSupport) {
272 | $this->fileUploadSupport = $fileUploadSupport;
273 | return $this;
274 | }
275 |
276 | /**
277 | * Get the file upload support status.
278 | *
279 | * @return boolean true if and only if the server supports file upload.
280 | */
281 | public function useFileUploadSupport() {
282 | return $this->fileUploadSupport;
283 | }
284 |
285 | /**
286 | * Sets the access token for api calls. Use this if you get
287 | * your access token by other means and just want the SDK
288 | * to use it.
289 | *
290 | * @param string $access_token an access token.
291 | * @return BaseFacebook
292 | */
293 | public function setAccessToken($access_token) {
294 | $this->accessToken = $access_token;
295 | return $this;
296 | }
297 |
298 | /**
299 | * Determines the access token that should be used for API calls.
300 | * The first time this is called, $this->accessToken is set equal
301 | * to either a valid user access token, or it's set to the application
302 | * access token if a valid user access token wasn't available. Subsequent
303 | * calls return whatever the first call returned.
304 | *
305 | * @return string The access token
306 | */
307 | public function getAccessToken() {
308 | if ($this->accessToken !== null) {
309 | // we've done this already and cached it. Just return.
310 | return $this->accessToken;
311 | }
312 |
313 | // first establish access token to be the application
314 | // access token, in case we navigate to the /oauth/access_token
315 | // endpoint, where SOME access token is required.
316 | $this->setAccessToken($this->getApplicationAccessToken());
317 | if ($user_access_token = $this->getUserAccessToken()) {
318 | $this->setAccessToken($user_access_token);
319 | }
320 |
321 | return $this->accessToken;
322 | }
323 |
324 | /**
325 | * Determines and returns the user access token, first using
326 | * the signed request if present, and then falling back on
327 | * the authorization code if present. The intent is to
328 | * return a valid user access token, or false if one is determined
329 | * to not be available.
330 | *
331 | * @return string A valid user access token, or false if one
332 | * could not be determined.
333 | */
334 | protected function getUserAccessToken() {
335 | // first, consider a signed request if it's supplied.
336 | // if there is a signed request, then it alone determines
337 | // the access token.
338 | $signed_request = $this->getSignedRequest();
339 | if ($signed_request) {
340 | // apps.facebook.com hands the access_token in the signed_request
341 | if (array_key_exists('oauth_token', $signed_request)) {
342 | $access_token = $signed_request['oauth_token'];
343 | $this->setPersistentData('access_token', $access_token);
344 | return $access_token;
345 | }
346 |
347 | // the JS SDK puts a code in with the redirect_uri of ''
348 | if (array_key_exists('code', $signed_request)) {
349 | $code = $signed_request['code'];
350 | $access_token = $this->getAccessTokenFromCode($code, '');
351 | if ($access_token) {
352 | $this->setPersistentData('code', $code);
353 | $this->setPersistentData('access_token', $access_token);
354 | return $access_token;
355 | }
356 | }
357 |
358 | // signed request states there's no access token, so anything
359 | // stored should be cleared.
360 | $this->clearAllPersistentData();
361 | return false; // respect the signed request's data, even
362 | // if there's an authorization code or something else
363 | }
364 |
365 | $code = $this->getCode();
366 | if ($code && $code != $this->getPersistentData('code')) {
367 | $access_token = $this->getAccessTokenFromCode($code);
368 | if ($access_token) {
369 | $this->setPersistentData('code', $code);
370 | $this->setPersistentData('access_token', $access_token);
371 | return $access_token;
372 | }
373 |
374 | // code was bogus, so everything based on it should be invalidated.
375 | $this->clearAllPersistentData();
376 | return false;
377 | }
378 |
379 | // as a fallback, just return whatever is in the persistent
380 | // store, knowing nothing explicit (signed request, authorization
381 | // code, etc.) was present to shadow it (or we saw a code in $_REQUEST,
382 | // but it's the same as what's in the persistent store)
383 | return $this->getPersistentData('access_token');
384 | }
385 |
386 | /**
387 | * Retrieve the signed request, either from a request parameter or,
388 | * if not present, from a cookie.
389 | *
390 | * @return string the signed request, if available, or null otherwise.
391 | */
392 | public function getSignedRequest() {
393 | if (!$this->signedRequest) {
394 | if (isset($_REQUEST['signed_request'])) {
395 | $this->signedRequest = $this->parseSignedRequest(
396 | $_REQUEST['signed_request']);
397 | } else if (isset($_COOKIE[$this->getSignedRequestCookieName()])) {
398 | $this->signedRequest = $this->parseSignedRequest(
399 | $_COOKIE[$this->getSignedRequestCookieName()]);
400 | }
401 | }
402 | return $this->signedRequest;
403 | }
404 |
405 | /**
406 | * Get the UID of the connected user, or 0
407 | * if the Facebook user is not connected.
408 | *
409 | * @return string the UID if available.
410 | */
411 | public function getUser() {
412 | if ($this->user !== null) {
413 | // we've already determined this and cached the value.
414 | return $this->user;
415 | }
416 |
417 | return $this->user = $this->getUserFromAvailableData();
418 | }
419 |
420 | /**
421 | * Determines the connected user by first examining any signed
422 | * requests, then considering an authorization code, and then
423 | * falling back to any persistent store storing the user.
424 | *
425 | * @return integer The id of the connected Facebook user,
426 | * or 0 if no such user exists.
427 | */
428 | protected function getUserFromAvailableData() {
429 | // if a signed request is supplied, then it solely determines
430 | // who the user is.
431 | $signed_request = $this->getSignedRequest();
432 | if ($signed_request) {
433 | if (array_key_exists('user_id', $signed_request)) {
434 | $user = $signed_request['user_id'];
435 | $this->setPersistentData('user_id', $signed_request['user_id']);
436 | return $user;
437 | }
438 |
439 | // if the signed request didn't present a user id, then invalidate
440 | // all entries in any persistent store.
441 | $this->clearAllPersistentData();
442 | return 0;
443 | }
444 |
445 | $user = $this->getPersistentData('user_id', $default = 0);
446 | $persisted_access_token = $this->getPersistentData('access_token');
447 |
448 | // use access_token to fetch user id if we have a user access_token, or if
449 | // the cached access token has changed.
450 | $access_token = $this->getAccessToken();
451 | if ($access_token &&
452 | $access_token != $this->getApplicationAccessToken() &&
453 | !($user && $persisted_access_token == $access_token)) {
454 | $user = $this->getUserFromAccessToken();
455 | if ($user) {
456 | $this->setPersistentData('user_id', $user);
457 | } else {
458 | $this->clearAllPersistentData();
459 | }
460 | }
461 |
462 | return $user;
463 | }
464 |
465 | /**
466 | * Get a Login URL for use with redirects. By default, full page redirect is
467 | * assumed. If you are using the generated URL with a window.open() call in
468 | * JavaScript, you can pass in display=popup as part of the $params.
469 | *
470 | * The parameters:
471 | * - redirect_uri: the url to go to after a successful login
472 | * - scope: comma separated list of requested extended perms
473 | *
474 | * @param array $params Provide custom parameters
475 | * @return string The URL for the login flow
476 | */
477 | public function getLoginUrl($params=array()) {
478 | $this->establishCSRFTokenState();
479 | $currentUrl = $this->getCurrentUrl();
480 |
481 | // if 'scope' is passed as an array, convert to comma separated list
482 | $scopeParams = isset($params['scope']) ? $params['scope'] : null;
483 | if ($scopeParams && is_array($scopeParams)) {
484 | $params['scope'] = implode(',', $scopeParams);
485 | }
486 |
487 | return $this->getUrl(
488 | 'www',
489 | 'dialog/oauth',
490 | array_merge(array(
491 | 'client_id' => $this->getAppId(),
492 | 'redirect_uri' => $currentUrl, // possibly overwritten
493 | 'state' => $this->state),
494 | $params));
495 | }
496 |
497 | /**
498 | * Get a Logout URL suitable for use with redirects.
499 | *
500 | * The parameters:
501 | * - next: the url to go to after a successful logout
502 | *
503 | * @param array $params Provide custom parameters
504 | * @return string The URL for the logout flow
505 | */
506 | public function getLogoutUrl($params=array()) {
507 | return $this->getUrl(
508 | 'www',
509 | 'logout.php',
510 | array_merge(array(
511 | 'next' => $this->getCurrentUrl(),
512 | 'access_token' => $this->getAccessToken(),
513 | ), $params)
514 | );
515 | }
516 |
517 | /**
518 | * Get a login status URL to fetch the status from Facebook.
519 | *
520 | * The parameters:
521 | * - ok_session: the URL to go to if a session is found
522 | * - no_session: the URL to go to if the user is not connected
523 | * - no_user: the URL to go to if the user is not signed into facebook
524 | *
525 | * @param array $params Provide custom parameters
526 | * @return string The URL for the logout flow
527 | */
528 | public function getLoginStatusUrl($params=array()) {
529 | return $this->getUrl(
530 | 'www',
531 | 'extern/login_status.php',
532 | array_merge(array(
533 | 'api_key' => $this->getAppId(),
534 | 'no_session' => $this->getCurrentUrl(),
535 | 'no_user' => $this->getCurrentUrl(),
536 | 'ok_session' => $this->getCurrentUrl(),
537 | 'session_version' => 3,
538 | ), $params)
539 | );
540 | }
541 |
542 | /**
543 | * Make an API call.
544 | *
545 | * @return mixed The decoded response
546 | */
547 | public function api(/* polymorphic */) {
548 | $args = func_get_args();
549 | if (is_array($args[0])) {
550 | return $this->_restserver($args[0]);
551 | } else {
552 | return call_user_func_array(array($this, '_graph'), $args);
553 | }
554 | }
555 |
556 | /**
557 | * Constructs and returns the name of the cookie that
558 | * potentially houses the signed request for the app user.
559 | * The cookie is not set by the BaseFacebook class, but
560 | * it may be set by the JavaScript SDK.
561 | *
562 | * @return string the name of the cookie that would house
563 | * the signed request value.
564 | */
565 | protected function getSignedRequestCookieName() {
566 | return 'fbsr_'.$this->getAppId();
567 | }
568 |
569 | /**
570 | * Get the authorization code from the query parameters, if it exists,
571 | * and otherwise return false to signal no authorization code was
572 | * discoverable.
573 | *
574 | * @return mixed The authorization code, or false if the authorization
575 | * code could not be determined.
576 | */
577 | protected function getCode() {
578 | if (isset($_REQUEST['code'])) {
579 | if ($this->state !== null &&
580 | isset($_REQUEST['state']) &&
581 | $this->state === $_REQUEST['state']) {
582 |
583 | // CSRF state has done its job, so clear it
584 | $this->state = null;
585 | $this->clearPersistentData('state');
586 | return $_REQUEST['code'];
587 | } else {
588 | self::errorLog('CSRF state token does not match one provided.');
589 | return false;
590 | }
591 | }
592 |
593 | return false;
594 | }
595 |
596 | /**
597 | * Retrieves the UID with the understanding that
598 | * $this->accessToken has already been set and is
599 | * seemingly legitimate. It relies on Facebook's Graph API
600 | * to retrieve user information and then extract
601 | * the user ID.
602 | *
603 | * @return integer Returns the UID of the Facebook user, or 0
604 | * if the Facebook user could not be determined.
605 | */
606 | protected function getUserFromAccessToken() {
607 | try {
608 | $user_info = $this->api('/me');
609 | return $user_info['id'];
610 | } catch (FacebookApiException $e) {
611 | return 0;
612 | }
613 | }
614 |
615 | /**
616 | * Returns the access token that should be used for logged out
617 | * users when no authorization code is available.
618 | *
619 | * @return string The application access token, useful for gathering
620 | * public information about users and applications.
621 | */
622 | protected function getApplicationAccessToken() {
623 | return $this->appId.'|'.$this->apiSecret;
624 | }
625 |
626 | /**
627 | * Lays down a CSRF state token for this process.
628 | *
629 | * @return void
630 | */
631 | protected function establishCSRFTokenState() {
632 | if ($this->state === null) {
633 | $this->state = md5(uniqid(mt_rand(), true));
634 | $this->setPersistentData('state', $this->state);
635 | }
636 | }
637 |
638 | /**
639 | * Retrieves an access token for the given authorization code
640 | * (previously generated from www.facebook.com on behalf of
641 | * a specific user). The authorization code is sent to graph.facebook.com
642 | * and a legitimate access token is generated provided the access token
643 | * and the user for which it was generated all match, and the user is
644 | * either logged in to Facebook or has granted an offline access permission.
645 | *
646 | * @param string $code An authorization code.
647 | * @return mixed An access token exchanged for the authorization code, or
648 | * false if an access token could not be generated.
649 | */
650 | protected function getAccessTokenFromCode($code, $redirect_uri = null) {
651 | if (empty($code)) {
652 | return false;
653 | }
654 |
655 | if ($redirect_uri === null) {
656 | $redirect_uri = $this->getCurrentUrl();
657 | }
658 |
659 | try {
660 | // need to circumvent json_decode by calling _oauthRequest
661 | // directly, since response isn't JSON format.
662 | $access_token_response =
663 | $this->_oauthRequest(
664 | $this->getUrl('graph', '/oauth/access_token'),
665 | $params = array('client_id' => $this->getAppId(),
666 | 'client_secret' => $this->getApiSecret(),
667 | 'redirect_uri' => $redirect_uri,
668 | 'code' => $code));
669 | } catch (FacebookApiException $e) {
670 | // most likely that user very recently revoked authorization.
671 | // In any event, we don't have an access token, so say so.
672 | return false;
673 | }
674 |
675 | if (empty($access_token_response)) {
676 | return false;
677 | }
678 |
679 | $response_params = array();
680 | parse_str($access_token_response, $response_params);
681 | if (!isset($response_params['access_token'])) {
682 | return false;
683 | }
684 |
685 | return $response_params['access_token'];
686 | }
687 |
688 | /**
689 | * Invoke the old restserver.php endpoint.
690 | *
691 | * @param array $params Method call object
692 | *
693 | * @return mixed The decoded response object
694 | * @throws FacebookApiException
695 | */
696 | protected function _restserver($params) {
697 | // generic application level parameters
698 | $params['api_key'] = $this->getAppId();
699 | $params['format'] = 'json-strings';
700 |
701 | $result = json_decode($this->_oauthRequest(
702 | $this->getApiUrl($params['method']),
703 | $params
704 | ), true);
705 |
706 | // results are returned, errors are thrown
707 | if (is_array($result) && isset($result['error_code'])) {
708 | throw new FacebookApiException($result);
709 | }
710 |
711 | return $result;
712 | }
713 |
714 | /**
715 | * Invoke the Graph API.
716 | *
717 | * @param string $path The path (required)
718 | * @param string $method The http method (default 'GET')
719 | * @param array $params The query/post data
720 | *
721 | * @return mixed The decoded response object
722 | * @throws FacebookApiException
723 | */
724 | protected function _graph($path, $method = 'GET', $params = array()) {
725 | if (is_array($method) && empty($params)) {
726 | $params = $method;
727 | $method = 'GET';
728 | }
729 | $params['method'] = $method; // method override as we always do a POST
730 |
731 | $result = json_decode($this->_oauthRequest(
732 | $this->getUrl('graph', $path),
733 | $params
734 | ), true);
735 |
736 | // results are returned, errors are thrown
737 | if (is_array($result) && isset($result['error'])) {
738 | $this->throwAPIException($result);
739 | }
740 |
741 | return $result;
742 | }
743 |
744 | /**
745 | * Make a OAuth Request.
746 | *
747 | * @param string $url The path (required)
748 | * @param array $params The query/post data
749 | *
750 | * @return string The decoded response object
751 | * @throws FacebookApiException
752 | */
753 | protected function _oauthRequest($url, $params) {
754 | if (!isset($params['access_token'])) {
755 | $params['access_token'] = $this->getAccessToken();
756 | }
757 |
758 | // json_encode all params values that are not strings
759 | foreach ($params as $key => $value) {
760 | if (!is_string($value)) {
761 | $params[$key] = json_encode($value);
762 | }
763 | }
764 |
765 | return $this->makeRequest($url, $params);
766 | }
767 |
768 | /**
769 | * Makes an HTTP request. This method can be overridden by subclasses if
770 | * developers want to do fancier things or use something other than curl to
771 | * make the request.
772 | *
773 | * @param string $url The URL to make the request to
774 | * @param array $params The parameters to use for the POST body
775 | * @param CurlHandler $ch Initialized curl handle
776 | *
777 | * @return string The response text
778 | */
779 | protected function makeRequest($url, $params, $ch=null) {
780 | if (!$ch) {
781 | $ch = curl_init();
782 | }
783 |
784 | $opts = self::$CURL_OPTS;
785 | if ($this->useFileUploadSupport()) {
786 | $opts[CURLOPT_POSTFIELDS] = $params;
787 | } else {
788 | $opts[CURLOPT_POSTFIELDS] = http_build_query($params, null, '&');
789 | }
790 | $opts[CURLOPT_URL] = $url;
791 |
792 | // disable the 'Expect: 100-continue' behaviour. This causes CURL to wait
793 | // for 2 seconds if the server does not support this header.
794 | if (isset($opts[CURLOPT_HTTPHEADER])) {
795 | $existing_headers = $opts[CURLOPT_HTTPHEADER];
796 | $existing_headers[] = 'Expect:';
797 | $opts[CURLOPT_HTTPHEADER] = $existing_headers;
798 | } else {
799 | $opts[CURLOPT_HTTPHEADER] = array('Expect:');
800 | }
801 |
802 | curl_setopt_array($ch, $opts);
803 | $result = curl_exec($ch);
804 |
805 | if (curl_errno($ch) == 60) { // CURLE_SSL_CACERT
806 | self::errorLog('Invalid or no certificate authority found, '.
807 | 'using bundled information');
808 | curl_setopt($ch, CURLOPT_CAINFO,
809 | dirname(__FILE__) . '/fb_ca_chain_bundle.crt');
810 | $result = curl_exec($ch);
811 | }
812 |
813 | if ($result === false) {
814 | $e = new FacebookApiException(array(
815 | 'error_code' => curl_errno($ch),
816 | 'error' => array(
817 | 'message' => curl_error($ch),
818 | 'type' => 'CurlException',
819 | ),
820 | ));
821 | curl_close($ch);
822 | throw $e;
823 | }
824 | curl_close($ch);
825 | return $result;
826 | }
827 |
828 | /**
829 | * Parses a signed_request and validates the signature.
830 | *
831 | * @param string $signed_request A signed token
832 | * @return array The payload inside it or null if the sig is wrong
833 | */
834 | protected function parseSignedRequest($signed_request) {
835 | list($encoded_sig, $payload) = explode('.', $signed_request, 2);
836 |
837 | // decode the data
838 | $sig = self::base64UrlDecode($encoded_sig);
839 | $data = json_decode(self::base64UrlDecode($payload), true);
840 |
841 | if (strtoupper($data['algorithm']) !== 'HMAC-SHA256') {
842 | self::errorLog('Unknown algorithm. Expected HMAC-SHA256');
843 | return null;
844 | }
845 |
846 | // check sig
847 | $expected_sig = hash_hmac('sha256', $payload,
848 | $this->getApiSecret(), $raw = true);
849 | if ($sig !== $expected_sig) {
850 | self::errorLog('Bad Signed JSON signature!');
851 | return null;
852 | }
853 |
854 | return $data;
855 | }
856 |
857 | /**
858 | * Build the URL for api given parameters.
859 | *
860 | * @param $method String the method name.
861 | * @return string The URL for the given parameters
862 | */
863 | protected function getApiUrl($method) {
864 | static $READ_ONLY_CALLS =
865 | array('admin.getallocation' => 1,
866 | 'admin.getappproperties' => 1,
867 | 'admin.getbannedusers' => 1,
868 | 'admin.getlivestreamvialink' => 1,
869 | 'admin.getmetrics' => 1,
870 | 'admin.getrestrictioninfo' => 1,
871 | 'application.getpublicinfo' => 1,
872 | 'auth.getapppublickey' => 1,
873 | 'auth.getsession' => 1,
874 | 'auth.getsignedpublicsessiondata' => 1,
875 | 'comments.get' => 1,
876 | 'connect.getunconnectedfriendscount' => 1,
877 | 'dashboard.getactivity' => 1,
878 | 'dashboard.getcount' => 1,
879 | 'dashboard.getglobalnews' => 1,
880 | 'dashboard.getnews' => 1,
881 | 'dashboard.multigetcount' => 1,
882 | 'dashboard.multigetnews' => 1,
883 | 'data.getcookies' => 1,
884 | 'events.get' => 1,
885 | 'events.getmembers' => 1,
886 | 'fbml.getcustomtags' => 1,
887 | 'feed.getappfriendstories' => 1,
888 | 'feed.getregisteredtemplatebundlebyid' => 1,
889 | 'feed.getregisteredtemplatebundles' => 1,
890 | 'fql.multiquery' => 1,
891 | 'fql.query' => 1,
892 | 'friends.arefriends' => 1,
893 | 'friends.get' => 1,
894 | 'friends.getappusers' => 1,
895 | 'friends.getlists' => 1,
896 | 'friends.getmutualfriends' => 1,
897 | 'gifts.get' => 1,
898 | 'groups.get' => 1,
899 | 'groups.getmembers' => 1,
900 | 'intl.gettranslations' => 1,
901 | 'links.get' => 1,
902 | 'notes.get' => 1,
903 | 'notifications.get' => 1,
904 | 'pages.getinfo' => 1,
905 | 'pages.isadmin' => 1,
906 | 'pages.isappadded' => 1,
907 | 'pages.isfan' => 1,
908 | 'permissions.checkavailableapiaccess' => 1,
909 | 'permissions.checkgrantedapiaccess' => 1,
910 | 'photos.get' => 1,
911 | 'photos.getalbums' => 1,
912 | 'photos.gettags' => 1,
913 | 'profile.getinfo' => 1,
914 | 'profile.getinfooptions' => 1,
915 | 'stream.get' => 1,
916 | 'stream.getcomments' => 1,
917 | 'stream.getfilters' => 1,
918 | 'users.getinfo' => 1,
919 | 'users.getloggedinuser' => 1,
920 | 'users.getstandardinfo' => 1,
921 | 'users.hasapppermission' => 1,
922 | 'users.isappuser' => 1,
923 | 'users.isverified' => 1,
924 | 'video.getuploadlimits' => 1);
925 | $name = 'api';
926 | if (isset($READ_ONLY_CALLS[strtolower($method)])) {
927 | $name = 'api_read';
928 | } else if (strtolower($method) == 'video.upload') {
929 | $name = 'api_video';
930 | }
931 | return self::getUrl($name, 'restserver.php');
932 | }
933 |
934 | /**
935 | * Build the URL for given domain alias, path and parameters.
936 | *
937 | * @param $name string The name of the domain
938 | * @param $path string Optional path (without a leading slash)
939 | * @param $params array Optional query parameters
940 | *
941 | * @return string The URL for the given parameters
942 | */
943 | protected function getUrl($name, $path='', $params=array()) {
944 | $url = self::$DOMAIN_MAP[$name];
945 | if ($path) {
946 | if ($path[0] === '/') {
947 | $path = substr($path, 1);
948 | }
949 | $url .= $path;
950 | }
951 | if ($params) {
952 | $url .= '?' . http_build_query($params, null, '&');
953 | }
954 |
955 | return $url;
956 | }
957 |
958 | /**
959 | * Returns the Current URL, stripping it of known FB parameters that should
960 | * not persist.
961 | *
962 | * @return string The current URL
963 | */
964 | protected function getCurrentUrl() {
965 | if (isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] == 'on' || $_SERVER['HTTPS'] == 1)
966 | || isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https'
967 | ) {
968 | $protocol = 'https://';
969 | }
970 | else {
971 | $protocol = 'http://';
972 | }
973 | $currentUrl = $protocol . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
974 | $parts = parse_url($currentUrl);
975 |
976 | $query = '';
977 | if (!empty($parts['query'])) {
978 | // drop known fb params
979 | $params = explode('&', $parts['query']);
980 | $retained_params = array();
981 | foreach ($params as $param) {
982 | if ($this->shouldRetainParam($param)) {
983 | $retained_params[] = $param;
984 | }
985 | }
986 |
987 | if (!empty($retained_params)) {
988 | $query = '?'.implode($retained_params, '&');
989 | }
990 | }
991 |
992 | // use port if non default
993 | $port =
994 | isset($parts['port']) &&
995 | (($protocol === 'http://' && $parts['port'] !== 80) ||
996 | ($protocol === 'https://' && $parts['port'] !== 443))
997 | ? ':' . $parts['port'] : '';
998 |
999 | // rebuild
1000 | return $protocol . $parts['host'] . $port . $parts['path'] . $query;
1001 | }
1002 |
1003 | /**
1004 | * Returns true if and only if the key or key/value pair should
1005 | * be retained as part of the query string. This amounts to
1006 | * a brute-force search of the very small list of Facebook-specific
1007 | * params that should be stripped out.
1008 | *
1009 | * @param string $param A key or key/value pair within a URL's query (e.g.
1010 | * 'foo=a', 'foo=', or 'foo'.
1011 | *
1012 | * @return boolean
1013 | */
1014 | protected function shouldRetainParam($param) {
1015 | foreach (self::$DROP_QUERY_PARAMS as $drop_query_param) {
1016 | if (strpos($param, $drop_query_param.'=') === 0) {
1017 | return false;
1018 | }
1019 | }
1020 |
1021 | return true;
1022 | }
1023 |
1024 | /**
1025 | * Analyzes the supplied result to see if it was thrown
1026 | * because the access token is no longer valid. If that is
1027 | * the case, then the persistent store is cleared.
1028 | *
1029 | * @param $result array A record storing the error message returned
1030 | * by a failed API call.
1031 | */
1032 | protected function throwAPIException($result) {
1033 | $e = new FacebookApiException($result);
1034 | switch ($e->getType()) {
1035 | // OAuth 2.0 Draft 00 style
1036 | case 'OAuthException':
1037 | // OAuth 2.0 Draft 10 style
1038 | case 'invalid_token':
1039 | $message = $e->getMessage();
1040 | if ((strpos($message, 'Error validating access token') !== false) ||
1041 | (strpos($message, 'Invalid OAuth access token') !== false)) {
1042 | $this->setAccessToken(null);
1043 | $this->user = 0;
1044 | $this->clearAllPersistentData();
1045 | }
1046 | }
1047 |
1048 | throw $e;
1049 | }
1050 |
1051 |
1052 | /**
1053 | * Prints to the error log if you aren't in command line mode.
1054 | *
1055 | * @param string $msg Log message
1056 | */
1057 | protected static function errorLog($msg) {
1058 | // disable error log if we are running in a CLI environment
1059 | // @codeCoverageIgnoreStart
1060 | if (php_sapi_name() != 'cli') {
1061 | error_log($msg);
1062 | }
1063 | // uncomment this if you want to see the errors on the page
1064 | // print 'error_log: '.$msg."\n";
1065 | // @codeCoverageIgnoreEnd
1066 | }
1067 |
1068 | /**
1069 | * Base64 encoding that doesn't need to be urlencode()ed.
1070 | * Exactly the same as base64_encode except it uses
1071 | * - instead of +
1072 | * _ instead of /
1073 | *
1074 | * @param string $input base64UrlEncoded string
1075 | * @return string
1076 | */
1077 | protected static function base64UrlDecode($input) {
1078 | return base64_decode(strtr($input, '-_', '+/'));
1079 | }
1080 |
1081 | /**
1082 | * Each of the following four methods should be overridden in
1083 | * a concrete subclass, as they are in the provided Facebook class.
1084 | * The Facebook class uses PHP sessions to provide a primitive
1085 | * persistent store, but another subclass--one that you implement--
1086 | * might use a database, memcache, or an in-memory cache.
1087 | *
1088 | * @see Facebook
1089 | */
1090 |
1091 | /**
1092 | * Stores the given ($key, $value) pair, so that future calls to
1093 | * getPersistentData($key) return $value. This call may be in another request.
1094 | *
1095 | * @param string $key
1096 | * @param array $value
1097 | *
1098 | * @return void
1099 | */
1100 | abstract protected function setPersistentData($key, $value);
1101 |
1102 | /**
1103 | * Get the data for $key, persisted by BaseFacebook::setPersistentData()
1104 | *
1105 | * @param string $key The key of the data to retrieve
1106 | * @param boolean $default The default value to return if $key is not found
1107 | *
1108 | * @return mixed
1109 | */
1110 | abstract protected function getPersistentData($key, $default = false);
1111 |
1112 | /**
1113 | * Clear the data with $key from the persistent storage
1114 | *
1115 | * @param string $key
1116 | * @return void
1117 | */
1118 | abstract protected function clearPersistentData($key);
1119 |
1120 | /**
1121 | * Clear all data from the persistent storage
1122 | *
1123 | * @return void
1124 | */
1125 | abstract protected function clearAllPersistentData();
1126 | }
1127 |
--------------------------------------------------------------------------------