├── 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 | user photo 25 |
26 |
27 | 28 | PHP Session 29 | 30 | User's Basic Info 31 | 32 |
33 |
34 |
35 |
36 | 40 |
41 |
42 |
43 | Click Here To Show A DEMO Stream Publish Box 44 |
45 |
46 |
47 | 48 |
49 | 50 |
51 | 52 |
53 |
54 |
55 | Click Here Iframe width=300,height=500 56 |
57 | Facebook has a maximum width for iframe. If you provide more than the width then facebook will set the predefined maximum width not your width. 58 |
59 | Click Here Iframe width=950,height=1800 60 | 61 |
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 | Download iPhone/iPad Dictionaries, Applications & Games 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 | --------------------------------------------------------------------------------