XML-RPC'ye giriş. Programlama yarışmaları Diyagramda neler görülebilir?

Birkaç gün önce sitelerimin hosting üzerindeki yükünün önemli ölçüde arttığını fark ettim. Genellikle 100-120 "papağan" (CP) civarındayken, son birkaç günde 400-500 CP'ye yükseldi. Bunun iyi bir yanı yok çünkü hosting sahibi daha pahalı bir tarifeye geçebilir, hatta sitelere tamamen yakın erişime geçebilir, ben de araştırmaya başladım.

Ancak XML-RPC işlevselliğini koruyacak bir yöntem seçtim: XML-RPC Pingback'i Devre Dışı Bırak eklentisini yüklemek. Yalnızca "tehlikeli" pingback.ping ve pingback.extensions.getPingbacks yöntemlerini kaldırarak XML-RPC işlevselliğini bırakır. Eklenti yüklendikten sonra yalnızca etkinleştirilmesi gerekir; başka bir yapılandırmaya gerek yoktur.

Yol boyunca saldırganların erişimlerini engellemek için tüm saldırganların IP'lerini sitelerimin .htaccess dosyasına girdim. Dosyanın sonuna şunu ekledim:

Sipariş İzin Ver, Reddet Tüm Reddetlerden İzin Ver 5.196.5.116 37.59.120.214 92.222.35.159

Hepsi bu, artık xmlrpc.php kullanarak blogu daha sonraki saldırılara karşı güvenilir bir şekilde koruduk. Sitelerimiz, üçüncü taraf sitelere yapılan DDoS saldırılarının yanı sıra, isteklerle barındırmayı yüklemeyi durdurdu.

Cumartesi günü öğle saatlerinden itibaren yaklaşık 25 Wordpress sitesinin barındırıldığı sunucumda ciddi yavaşlamalar yaşanmaya başladı. Önceki saldırılardan ( , ) fark edilmeden kurtulmayı başardığım için ne olduğunu hemen anlamadım.

Bunu anladığımda, şifrelerin kaba kuvvetle uygulandığı ve XMLRPC'ye birçok istek yapıldığı ortaya çıktı.

Sonuç olarak hemen olmasa da hepsini kesmeyi başardık. İşte bundan nasıl kaçınabileceğinize dair üç basit püf noktası.

Bu teknikler büyük olasılıkla herkes tarafından biliniyor, ancak açıklamalarda bulamadığım birkaç hataya değindim - belki bu birisine zaman kazandırabilir.

1. Aramayı durdurun, Oturum Açma Girişimlerini Sınırla eklentisini yükleyin - yükleyin, çünkü diğer korumalar sunucuyu büyük ölçüde yavaşlatır, örneğin, Oturum Açma Güvenliği Çözümü eklentisini kullanırken, sunucu yarım saat sonra kapandı, eklenti veritabanını yoğun bir şekilde yüklüyor .

Ayarlarda “Proxy için” onay kutusunu açtığınızdan emin olun; aksi takdirde sunucunuzun IP'si herkes için belirlenecek ve herkesi otomatik olarak engelleyecektir.
GÜNCELLEME, teşekkür ederim, ayrıntılar yorumlarda aşağıda - "Proxy için" onay kutusunu yalnızca "Doğrudan bağlantı" etkinleştirildiğinde algılama çalışmıyorsa etkinleştirin

2. XML-RPC'yi devre dışı bırakın - XML-RPC'yi Devre Dışı Bırak eklentisi (etkinleştirilmesi kolaydır ve bu kadar).

3. wp-login.php'yi kapatın - siteye IP yoluyla erişirseniz eklenti çalışmaz ve seçiciler siteyi çökertmeye devam eder. Bunu önlemek için .htaccess'e şunu ekleyin:

Reddetme Siparişi Ver, Herkesten Reddetmeye İzin Ver

Wp-login dosyasını kopyalıyoruz, garip bir adla yeniden adlandırıyoruz, örneğin poletnormalny.php ve dosyanın içinde, tüm wp-login.php yazıtlarını poletnormalny.php olarak değiştirmek için otomatik düzeltmeyi kullanıyoruz.
İşte bu kadar, artık yönetici paneline yalnızca dosyanızı kullanarak erişebilirsiniz.

Bu 3 basit adımdan sonra siteler tekrar uçmaya başladı ve huzur geldi.

Aniden ilginç geldi

Seçeneklerden biri saldırıya uğrayıp uğramadığınızı görmektir. Bu, nginx günlüklerinde görülebilir (örneğin, Debian /var/log/nginx erişim.log dosyasının yolu buradadır).

XML-RPC teknolojisi, WordPress sisteminde geri pingler, geri izlemeler, yönetici paneline giriş yapmadan uzaktan site yönetimi vb. gibi çeşitli güzel özellikler için kullanılır. Maalesef saldırganlar bunu web sitelerine DDoS saldırıları gerçekleştirmek için kullanabilirler. Yani kendiniz için veya sipariş vermek için güzel, ilginç WP projeleri yaratırsınız ve aynı zamanda hiçbir şeyden şüphelenmeden bir DDoS botnetinin parçası olabilirsiniz. Kötü insanlar, on binlerce siteyi birbirine bağlayarak kurbanlarına güçlü bir saldırı oluşturur. Her ne kadar aynı zamanda siteniz de zarar görse de, çünkü... yük bulunduğu hostinge gider.

Bu tür kötü etkinliklerin kanıtı, aşağıdaki satırları içeren sunucu günlüklerinde (nginx'te erişim.log) bulunabilir:

103.238.80.27 - - "POST /wp-login.php HTTP/1.0" 200 5791 "-" "-"

Ancak XML-RPC güvenlik açığına dönelim. Görsel olarak sunucunuzdaki sitelerin yavaş açılması veya hiç yüklenememesi (502 Hatalı Ağ Geçidi hatası) şeklinde kendini gösterir. FASTVPS sunucumun teknik desteği tahminlerimi doğruladı ve şunları tavsiye etti:

  1. WordPress'i eklentilerle birlikte en son sürüme güncelleyin. Genel olarak takip ederseniz en son 4.2.3 sürümünün yüklenmesi gerektiğini okumuş olabilirsiniz. güvenlik eleştirileri nedeniyle (tıpkı önceki sürümlerde olduğu gibi). Kısacası güncellemek iyidir.
  1. XML-RPC Pingback'i Devre Dışı Bırak eklentisini yükleyin.

WordPress'te XML-RPC'yi devre dışı bırakma

Daha önce XML-RPC'yi etkinleştirme/devre dışı bırakma seçeneği sistem ayarlarında bir yerde bulunuyormuş gibi geliyordu, ancak şimdi onu orada bulamıyorum. Bu nedenle ondan kurtulmanın en kolay yolu uygun eklentiyi kullanmaktır.

XML-RPC Pingback'i Devre Dışı Bırak seçeneğini bulun ve indirin veya doğrudan sistem yöneticisi panelinden yükleyin. Ek bir şey yapılandırmanıza gerek yoktur, modül hemen çalışmaya başlar. XML-RPC arayüzünden pingback.ping ve pingback.extensions.getPingbacks yöntemlerini kaldırır. Ayrıca X-Pingback'i HTTP başlıklarından kaldırır.

Bloglardan birinde XML-RPC'yi devre dışı bırakmayı kaldırmak için birkaç seçenek daha buldum.

1. Şablonda XML-RPC'yi devre dışı bırakın.

Bunu yapmak için temanın Function.php dosyasına aşağıdaki satırı ekleyin:

Reddetme Siparişi Ver, Herkesten Reddetmeye İzin Ver

Şahsen son iki yöntemi kullanmadım çünkü... XML-RPC Pingback'i Devre Dışı Bırak eklentisini bağladım - yeterli olacağını düşünüyorum. Gereksiz kurulumlardan hoşlanmayanlar için alternatif seçenekler önerdim.

Belleğin verimli kullanımıyla iş parçacığı işlemenin organizasyonu

Elliot Rusty Harold
Yayınlanma tarihi: 10/11/2007

PHP 5, Genişletilebilir İşaretleme Dili'ni (XML) okumak için yeni bir sınıf olan XMLReader'ı tanıttı. Düz XML veya Belge Nesnesi Modelinden (DOM) farklı olarak XMLReader akış modunda çalışır. Yani belgeyi başından sonuna kadar okur. Belgenin içeriğiyle çalışmaya belgenin sonunu görmeden başlayabilirsiniz. Bu onu çok hızlı, verimli ve hafıza açısından verimli kılar. İşlenmesi gereken belgelerin boyutu ne kadar büyük olursa, bu da o kadar önemlidir.

libxml

Açıklanan XMLReader API, Gnome Projesinin C ve C++ için libxml kitaplığını temel almaktadır. Gerçekte XMLReader, libxml XmlTextReader API'sinin üzerinde yer alan ince bir PHP katmanıdır. XmlTextReader ayrıca .NET sınıfları XmlTextReader ve XmlReader'dan sonra modellenmiştir (ancak aynı kodu paylaşmamaktadır).

Basit XML API'sinden (SAX) farklı olarak XMLReader, itme ayrıştırıcısından çok çekme ayrıştırıcısıdır. Bu, programın kontrol altında olduğu anlamına gelir. Ayrıştırıcının onu gördüğünde ne gördüğünü size söylemesi yerine; ayrıştırıcıya belgenin bir sonraki bölümüne ne zaman geçeceğini söylersiniz. İçeriklere tepki vermek yerine içerik istiyorsunuz. Başka bir deyişle, bunu şu şekilde düşünebilirsiniz: XMLReader, Observer yapısının değil, Iterator yapısının bir uygulamasıdır.

Örnek görev

Basit bir örnekle başlayalım. XML-RPC isteklerini alan ve yanıtlar üreten bir PHP betiği yazdığınızı düşünün. Daha spesifik olarak, sorguların Liste 1'e benzediğini hayal edin. Kök belge öğesi, methodName ve params öğelerini içeren methodCall'dır. Yöntemin adı sqrt'dir. Params öğesi, karekökünü çıkarmak istediğiniz sayı olan bir double içeren bir param öğesi içerir. Ad alanları kullanılmaz.

Listeleme 1. XML-RPC isteği
kare 36.0

PHP betiğinin yapması gereken şey budur:

  1. Yöntemin adını kontrol edin ve sqrt değilse (bu betik tarafından yönetilebilen tek yöntem) bir hata yanıtı oluşturun.
  2. Argümanı bulun ve eksikse veya yanlış türdeyse bir hata sinyali oluşturun.
  3. Aksi takdirde karekökü hesaplayın.
  4. Sonucu Liste 2'de gösterilen biçimde döndürün.
Listeleme 2. XML-RPC yanıtı
6.0

Adım adım ele alalım.

Ayrıştırıcıyı başlatma ve belgeyi yükleme

İlk adım yeni bir ayrıştırıcı nesnesi oluşturmaktır. Bunu yapmak kolaydır:

$okuyucu = yeni XMLReader();
Gönderilen orijinal verilere bilgi ekleme

$HTTP_RAW_POST_DATA'nın boş olduğunu fark ederseniz, php.ini dosyanıza aşağıdaki satırı ekleyin:

Always_populate_raw_post_data = Açık

$istek = $HTTP_RAW_POST_DATA; $okuyucu->XML($istek);

Nereden aldığınıza bakılmaksızın herhangi bir dizeyi ayrıştırabilirsiniz. Örneğin bu, bir programdaki bir dize sabiti veya bir dosyanın içeriği olabilir. Open() işlevini kullanarak harici bir URL'den de veri yükleyebilirsiniz. Örneğin, aşağıdaki talimat Atom kanallarından birini ayrıştırmaya hazırlar:

$reader->XML("http://www.cafeaulait.org/today.atom");

Kaynak verilerinizi nereden alırsanız alın, okuyucu artık kuruludur ve analiz yapmaya hazırdır.

Bir belgeyi okumak

Read() işlevi ayrıştırıcıyı bir sonraki belirtece taşır. En basit yaklaşım, belge boyunca bir while döngüsünü yinelemektir:

while ($okuyucu->oku()) ( // kod işleniyor... )

İşiniz bittiğinde, kullandığı kaynakları boşaltmak için ayrıştırıcıyı kapatın ve onu aşağıdaki belge için yeniden yapılandırın:

$okuyucu->kapat();

Döngünün içinde ayrıştırıcı belirli bir düğüme yerleştirilir: bir öğenin başına, bir öğenin sonuna, bir metin düğümüne, bir yoruma vb. Aşağıdaki özellikler ayrıştırıcının o anda neye baktığını bilmenizi sağlar:

  • localName yerel, önceden tanımlanmamış ana bilgisayar adıdır.
  • ad - olası bir önceden tanımlanmış düğüm adı. Yorumlar gibi adları olmayan düğümler için bunlar DOM'da (Belge Nesne Modeli) olduğu gibi #comment , #text , #document vb.'dir.
  • namespaceURI, ana bilgisayar ad alanı için Tekdüzen Kaynak Tanımlayıcısıdır (URI).
  • nodeType, düğümün türünü temsil eden bir tamsayıdır; örneğin, bir öznitelik düğümü için 2 ve bir işleme ifadesi için 7.
  • önek, düğümün ad alanı önekidir.
  • değer düğümün metin içeriğidir.
  • hasValue - düğümün bir metin değeri varsa doğru, değilse yanlış.

Elbette tüm düğüm türleri bu özelliklerin hepsine sahip değildir. Örneğin, metin düğümleri, CDATA bölümleri, yorumlar, işleme ifadeleri, nitelikler, boşluk karakteri, belge türleri ve XML açıklamalarının anlamları vardır. Diğer düğüm türleri (özellikle öğeler ve belgeler) bunu yapmaz. Tipik olarak bir program, neyin görüntülendiğini belirlemek ve buna göre yanıt vermek için nodeType özelliğini kullanır. Liste 3, baktığı şeyin çıktısını almak için bu işlevleri kullanan basit bir while döngüsünü göstermektedir. Liste 4, giriş olarak Liste 1 verildiğinde bu programın çıktısını gösterir.

Liste 3. Ayrıştırıcının gördükleri
while ($okuyucu->oku()) ( echo $okuyucu->isim; if ($okuyucu->hasValue) ( ​​​echo ": " . $okuyucu->değer; ) echo "\n"; )
Liste 4. Liste 3'ten Çıktı
methodCall #text: methodName #text: sqrt methodName #text: params #text: param #text: value double #text: 10 double value #text: param #text: params #text: methodCall

Çoğu program o kadar evrensel değildir. Girdi verilerini belirli bir biçimde kabul ederler ve belirli bir şekilde işlerler. XML-RPC örneğinde, girdiden yalnızca bir parametrenin okunması gerekir: double öğesi ve bu öğeden yalnızca bir tane olması gerekir. Bunu yapmak için double adlı öğenin başlangıcını bulun:

if ($reader->name == "double" && $reader->nodeelementType == XMLReader::element) ( // ... )

Bu öğe aynı zamanda ayrıştırıcıyı bir sonraki düğüme taşıyarak okunabilen tek bir metin alt düğümüne de sahiptir:

if ($reader->name == "double" && $reader->nodeType == XMLReader::ELEMENT) ( $reader->read(); answer($reader->value); )

Burada, answer() işlevi bir XML-RPC yanıtı oluşturur ve bunu istemciye gönderir. Ancak bunu göstermeden önce söylemem gereken bir şey daha var. İstek belgesindeki double öğesinin yalnızca bir metin düğümü içerdiğinin garantisi yoktur. Birden fazla düğümün yanı sıra yorumlar ve ifadeler içerebilir. Örneğin, şöyle görünebilir:

6.0
İç İçe Öğeler

Bu devrede olası bir kusur var. Örneğin iç içe geçmiş çift öğeler, 61.2 bu algoritmayı bozabilir. Bu kötü bir XML-RPC'dir ve yakında tüm bu tür belgeleri reddetmek için RELAX NG doğrulamasını nasıl kullanacağınızı göreceksiniz. Benzer öğelerin iç içe yerleştirilmesine izin veren Genişletilebilir Köprü Metni İşaretleme Dili (XHTML) gibi belge türlerinde (örneğin, tablo içinde tablo), bitiş etiketinin olduğundan emin olmak için öğelerin derinliğini de izlemeniz gerekir. başlangıç ​​etiketine doğru şekilde eşlendi.

Soruna sürdürülebilir bir çözüm, metin düğümünün tüm çocuklarını double almak, onları birbirine zincirlemek ve ancak bundan sonra sonucu double'a dönüştürmek olacaktır. Her türlü yorumdan veya diğer olası metin dışı düğümlerden kaçınılmalıdır. Liste 5'in gösterdiği gibi biraz daha karmaşık ama çok da karmaşık değil.

Listeleme 5. Bir öğenin tüm metin içeriğini özetleyin
while ($reader->read()) ( if ($reader->nodeType == XMLReader::TEXT || $reader->nodeType == XMLReader::CDATA || $reader->nodeType == XMLReader::WHITESPACE || $reader->nodeType == XMLReader::SIGNIFICANT_WHITESPACE) ( $input .= $reader->value; ) else if ($reader->nodeType == XMLReader::END_ELEMENT && $reader->name == "double ") ( kırmak; ) )

Şimdilik belgenin içeriğinin geri kalanı göz ardı edilebilir. (Hata yönetimini daha sonra açıklamaya devam edeceğim.)

Yanıt oluşturma

Adından da anlaşılacağı gibi XMLReader salt okunurdur. İlgili XMLWriter sınıfı şu anda geliştirilme aşamasındadır ancak henüz hazır değildir. Neyse ki XML yazmak, onu okumaktan çok daha kolaydır. Öncelikle, başlık() işlevini kullanarak yanıtın ortam türünü ayarlamanız gerekir. XML-RPC için bu application/xml'dir. Örneğin:

başlık("İçerik türü: uygulama/xml");
Liste 6. XML eşleme
işlev yanıt($giriş) ( echo " " . sqrt($giriş) ." "; }

Hatta cevabın harf kısımlarını tıpkı HTML'de yaptığınız gibi doğrudan PHP sayfasına ekleyebilirsiniz. Bu teknoloji Liste 7'de gösterilmektedir.

Listeleme 7. Değişmez XML
fonksiyon cevap($giriş) ( ?> "

Hata işleme

Şimdiye kadar giriş belgesinin doğru biçimlendirildiği varsayılmıştı. Ancak bunu kimse garanti edemez. Herhangi bir XML ayrıştırıcı gibi, XMLReader da bir biçimlendirme hatasıyla karşılaştığında işlemeyi durdurmalıdır. Böyle bir durumda read() işlevi false değerini döndürür.

Teorik olarak ayrıştırıcı, karşılaştığı ilk hataya kadar verileri işleyebilir. Ancak küçük belgelerle yaptığım deneylerde neredeyse anında hatayla karşılaşıyor. Temel ayrıştırıcı, belgenin büyük bir bölümünü önceden ayrıştırır, önbelleğe alır ve ardından parça parça çıktısını alır. Bu nedenle, genellikle hataları bir ön aşamada tanımlar. Güvenlik açısından ilk tasarım hatasından önce içerik analizi yapabileceğiniz için sorumluluk almamak daha iyidir. Ayrıca ayrıştırıcı başarısız olmadan herhangi bir içerik göremeyeceğinizi varsaymayın. Yalnızca eksiksiz, doğru biçimlendirilmiş belgeleri kabul etmeniz gerekiyorsa, komut dosyasının belgenin sonuna kadar geri dönüşü olmayan bir şey yapmadığından emin olun.

Ayrıştırıcı bir biçimlendirme hatasıyla karşılaşırsa, read() işlevi buna benzer bir hata iletisi görüntüler (bir geliştirme sunucusunda olması gerektiği gibi ayrıntılı hata raporlaması yapılandırılmışsa):


Uyarı: XMLReader::read() [function.read]:< value>10 içinde /var/www/root.phpçevrimiçi 35

Raporu kullanıcıya sunulan HTML sayfasına kopyalamak istemeyebilirsiniz. Hata mesajını $php_errormsg ortam değişkeninde yakalamak daha iyidir. Bunu yapmak için php.ini dosyasında track_errors yapılandırma seçeneğini etkinleştirmeniz gerekir:

track_errors = Açık

Varsayılan olarak, php.ini'de açıkça belirtilen track_errors seçeneği devre dışıdır, bu nedenle bu satırı değiştirdiğinizden emin olun. Yukarıda gösterilen satırı php.ini dosyasının başına eklerseniz, aşağıdaki track_errors = Off satırı onun yerini alacaktır.

Bu program yalnızca eksiksiz, doğru biçimlendirilmiş girişe yanıt göndermelidir. (Aynı zamanda güvenilirdir, ancak bu konuya daha sonra değineceğim.) Bu nedenle, belge ayrıştırılana kadar (while döngüsünden çıkana kadar) beklemeniz gerekir. Şimdi $php_errormsg değerinin değişip değişmediğini kontrol edin. Değilse, belge doğru biçimlendirilmiş demektir ve bir XML-RPC yanıt mesajı gönderilecektir. Değişken ayarlandıysa bu, belgenin doğru biçimlendirilmediği ve bir XML-RPC arıza sinyali gönderileceği anlamına gelir. Negatif bir sayının karekökü istendiğinde de bir hata sinyali gönderilir. Liste 8'e bakın.

Liste 8. Doğru biçimlendirmenin kontrol edilmesi
// bir istek gönderiliyor $request = $HTTP_RAW_POST_DATA; error_reporting(E_ERROR | E_WARNING | E_PARSE); if (isset($php_errormsg)) unset(($php_errormsg); // bir okuyucu programı oluşturma (okuyucu) $reader = new XMLReader(); // $reader->setRelaxNGschema("request.rng"); $reader- > XML($request); $input = ""; while ($okuyucu->oku()) ( if ($okuyucu->isim == "double" && $okuyucu->nodeType == XMLReader::ELEMENT) ( while ($reader->read()) ( if ($reader->nodeType == XMLReader::TEXT || $reader->nodeType == XMLReader::CDATA || $reader->nodeType == XMLReader::WHITESPACE | | $reader->nodeType == XMLReader::SIGNIFICANT_WHITESPACE) ( $input .= $reader->value; ) else if ($reader->nodeType == XMLReader::END_ELEMENT && $reader->name == "double " ) ( break; ) ) break; ) ) // giriş bilgisinin doğru formatının kontrol edilmesi if (isset($php_errormsg)) error(21, $php_errormsg);< 0) fault(20, "Cannot take square root of negative number"); else respond($input);

Burada genel XML akışı işleme modelinin basitleştirilmiş bir versiyonunu bulabilirsiniz. Ayrıştırıcı, belge sona erdiğinde hangi eylemlerin gerçekleştirileceğine göre bir veri yapısı doldurur. Genellikle veri yapısı belgenin kendisinden daha basittir. Burada veri yapısı özellikle basittir: tek satır.

Doğrulama

libxml sürümü

XMLReader'ın bağlı olduğu kitaplık olan libxml'in ilk sürümlerinde ciddi RELAX NG kusurları vardı. En az 2.06.26 sürümünü kullandığınızdan emin olun. Mac OS X Tiger da dahil olmak üzere pek çok sistem, kusurlu daha önceki bir sürümü içerir.

Şu ana kadar verilerin gerçekten düşündüğüm yerde olup olmadığını kontrol etmeye pek önem vermedim. Bu kontrolü gerçekleştirmenin en kolay yolu belgeyi diyagramla karşılaştırmaktır. XMLReader RELAX NG şema açıklama dilini destekler; Liste 9, XML-RPC isteğinin bu özel biçimi için basit bir RELAX NG şemasını göstermektedir.

Liste 9. XML-RPC isteği
kare

Şema, setRelaxNGSchemaSource() kullanılarak bir dize değişmezi olarak doğrudan bir PHP betiğine eklenebilir veya setRelaxNGSchema() kullanılarak harici bir dosyadan veya URL'den okunabilir. Örneğin Liste 9'un içeriğinin sqrt.rng dosyasına yazıldığını varsayarsak şema şu şekilde yüklenir:

okuyucu->setRelaxNGSchema("sqrt.rng")

Yap önce belgeyi analiz etmeye başladığınızda. Ayrıştırıcı, okurken belgeyi şemayla karşılaştırır. Bir belgenin geçerli olup olmadığını kontrol etmek için isValid() işlevini çağırın; bu işlev, belge geçerliyse (bu noktada) true, değilse false değerini döndürür. Liste 10, tüm hata yönetimini içeren programın tamamını gösterir. Program her türlü geçerli girişi kabul etmeli, doğru değerleri döndürmeli ve tüm hatalı istekleri reddetmelidir. Ayrıca bir şeyler ters giderse XML-RPC hatası gönderen bir error() yöntemi de ekledim.

Liste 10. Tam XML-RPC karekök arka ucu
kare "; if (!isset($HTTP_RAW_POST_DATA)) ( error(22, "Lütfen daima_populate_raw_post_data = php.ini'de açık olduğundan emin olun"); ) else ( // istek gönderiliyor $request = $HTTP_RAW_POST_DATA; error_reporting(E_ERROR | E_WARNING | E_PARSE ); // okuyucu oluşturma $reader = new XMLReader(); $reader->setRelaxNGSchema("request.rng"); $reader->XML($request); if ($reader->name == "double" && $reader->nodeType == XMLReader::ELEMENT) ( while ($reader->read()) ( if ($reader->nodeType = = XMLReader::TEXT || $reader->nodeType == XMLReader::CDATA || $reader->nodeType == XMLReader::WHITESPACE || $reader->nodeType == XMLReader::SIGNIFICANT_WHITESPACE) ( $input .= $reader->value ; ) else if ($reader->nodeType == XMLReader::END_ELEMENT && $reader->name == "double") ( break; ) ) break; error(21, $php_errormsg); else if (! $reader-); >isValid()) error(19, "Geçersiz istek"); else if ($input)< 0) fault(20, "Cannot take square root of negative number"); else respond($input); $reader->kapalı(); ) işlev yanıt($giriş) ( ?> hata kodu " . $kod ." hataDizesi " . $mesaj ." "; }

Öznitellikler

Nitelikler normal analiz sırasında görünmez. Nitelikleri okumak için öğenin başında durmanız ve belirli bir niteliği isme veya numaraya göre sorgulamanız gerekir.

Geçerli öğede değerini bulmak istediğiniz özelliğin adını getAttribute() işlevine iletin. Örneğin, aşağıdaki yapı geçerli öğenin id niteliğini ister:

$id = $reader->getAttribute("id");

Öznitelik bir ad alanındaysa, örneğin xlink:href, getAttributeNS() öğesini çağırın ve yerel adı ve ad alanı URI'sini sırasıyla birinci ve ikinci bağımsız değişken olarak iletin (önek önemli değildir). Örneğin, bu ifade http://www.w3.org/1999/xlink/ ad alanındaki xlink:href özelliğinin değerini sorgular:

$href = $reader->getAttributeNS("href", "http://www.w3.org/1999/xlink/");

Öznitelik mevcut değilse, her iki yöntem de boş bir dize döndürecektir. (Bu yanlıştır çünkü null döndürmeleri gerekir. Bu uygulama, değeri boş dize olan nitelikler ile hiçbir değeri olmayan nitelikler arasında ayrım yapmayı zorlaştırır.)

Özellik sırası

XML belgelerinde niteliklerin sırası önemli değildir ve ayrıştırıcı tarafından korunmaz. Sadece kolaylık sağlamak amacıyla nitelikleri indekslemek için sayıları kullanır. Açılış etiketindeki ilk özelliğin özellik 1, ikincisinin özellik 2 vb. olacağının garantisi yoktur. Niteliklerin sırasına göre kod yazmayın.

Bir öğenin tüm niteliklerini bilmeniz gerekiyorsa ancak adları önceden bilinmiyorsa, öğenin okuma kısmı ayarlandığında moveToNextAttribute() öğesini çağırın. Ayrıştırıcı bir öznitelik düğümündeyse, öğeler için kullanılan özelliklerin aynısını kullanarak adını, ad alanını ve değerini okuyabilirsiniz. Örneğin, aşağıdaki kod parçacığı geçerli öğenin tüm niteliklerini yazdırır:

if ($reader->hasAttributes ve $reader->nodeType == XMLReader::ELEMENT) ( while ($reader->moveToNextAttribute()) ( echo $reader->name . "="" . $reader->value . ""\n"; ) echo "\n"; )

Bir XML API'si için çok sıra dışı olan XMLReader, nitelikleri baştan okumanıza olanak tanır. ya sondan eleman. Çift sayımı önlemek için, düğüm türünün XMLReader::ELEMENT olduğundan ve özniteliklere de sahip olabilen XMLReader::END_ELEMENT olmadığından emin olmak önemlidir.

Çözüm

XMLReader, PHP programcısının araç setine yararlı bir eklentidir. SimpleXML'den farklı olarak, yalnızca bazılarını değil tüm belgeleri işleyen tam bir XML ayrıştırıcısıdır. DOM'un aksine, kullanılabilir bellekten daha büyük belgeleri işleyebilir. SAX'tan farklı olarak program üzerinde kontrol sağlar. Eğer PHP programlarının XML girdisini kabul etmesi gerekiyorsa XMLReader'ı kullanmayı ciddi olarak düşünmelisiniz.

XML-RPC'ye giriş

İnternette kullanıcılara belirli bilgiler sağlayan birçok farklı kaynak vardır. Bu, sıradan statik sayfalar anlamına gelmez, örneğin bir veritabanından veya arşivlerden alınan veriler anlamına gelir. Bu, finansal verilerin (döviz kurları, menkul kıymet fiyatları verileri), hava durumu verilerinin veya haberler, makaleler, forumlardan gelen mesajlar gibi daha hacimli bilgilerin bir arşivi olabilir. Bu tür bilgiler sayfa ziyaretçisine örneğin bir form aracılığıyla, bir talebe yanıt olarak sunulabileceği gibi her seferinde dinamik olarak da oluşturulabilir. Ancak zorluk şu ki, bu tür bilgilere genellikle son kullanıcı (bir kişi) tarafından değil, bu verileri hesaplamaları veya diğer ihtiyaçları için kullanacak diğer sistemler ve programlar tarafından ihtiyaç duyulur.

Gerçek örnek: Bir bankacılık web sitesinin döviz fiyatlarını görüntüleyen bir sayfası. Sayfaya normal bir kullanıcı olarak bir tarayıcı aracılığıyla erişirseniz, tüm sayfa tasarımını, banner'ları, menüleri ve aramanın gerçek amacını - para birimi tekliflerini "çerçeveleyen" diğer bilgileri görürsünüz. Bu teklifleri çevrimiçi mağazanıza girmeniz gerekiyorsa, gerekli verileri manuel olarak seçip pano aracılığıyla web sitenize aktarmak dışında yapacak başka bir şey kalmaz. Ve bunu her gün yapmanız gerekecek. Gerçekten çıkış yolu yok mu?

Sorunu doğrudan çözerseniz, hemen bir çözüm ortaya çıkar: veriye ihtiyaç duyan bir program (web sitesindeki komut dosyası), "normal kullanıcı" olarak sunucudan bir sayfa alır, ortaya çıkan html kodunu ayrıştırır (ayrıştırır) ve ondan gerekli bilgiler. Bu, normal bir düzenli ifadeyle veya herhangi bir html ayrıştırıcısı kullanılarak yapılabilir. Yaklaşımın zorluğu etkisizliğinde yatmaktadır. İlk olarak, verilerin küçük bir kısmını almak için (para birimlerine ilişkin veriler kelimenin tam anlamıyla bir düzine veya iki karakterdir), en az birkaç on kilobayt olan sayfanın tamamını almanız gerekir. İkinci olarak, sayfa kodundaki herhangi bir değişiklikle (örneğin tasarım değiştiğinde veya başka bir şeyde), ayrıştırma algoritmamızın yeniden yapılması gerekecektir. Ve bu oldukça fazla kaynak gerektirecektir.

Bu nedenle, geliştiriciler bir karara vardılar - herhangi bir yerde bulunabilen programlar arasında şeffaf (protokol ve iletim ortamı düzeyinde) ve kolay veri alışverişine izin verecek, herhangi bir dilde yazılabilecek bir tür evrensel mekanizma geliştirmek gerekiyor ve herhangi bir işletim sistemi altında ve herhangi bir donanım platformunda çalıştırın. Böyle bir mekanizmaya artık yüksek sesle "Web hizmetleri", "SOAP", "hizmet odaklı mimari" deniyor. Veri alışverişi için açık ve zaman içinde test edilmiş standartlar kullanılır - mesajları iletmek için HTTP protokolü kullanılır (ancak diğer protokoller de kullanılabilir - örneğin SMTP). Verilerin kendisi (örneğimizde döviz kurları) platformlar arası formatta, XML belgeleri biçiminde paketlenmiş olarak iletilir. Bu amaçla özel bir standart icat edildi - SABUN.

Evet, artık web hizmetleri, SOAP ve XML herkesin ağzında, aktif olarak uygulanmaya başlıyor ve IBM ve Microsoft gibi büyük şirketler, web hizmetlerinin tam olarak uygulanmasına yardımcı olmak için tasarlanmış yeni ürünler piyasaya sürüyor.

Ancak! Örneğin, bankanın web sitesinden çevrimiçi mağaza motoruna aktarılması gereken döviz kurları ile ilgili böyle bir çözüm çok zor olacaktır. Sonuçta, SOAP standardının açıklaması tek başına bir buçuk bin sayfa kadar müstehcen bir yer kaplıyor ve hepsi bu değil. Pratik kullanım için, aynı zamanda üçüncü parti kütüphaneler ve uzantılarla nasıl çalışılacağını öğrenmeniz (yalnızca PHP 5.0'dan başlayarak, SOAP ile çalışmak için bir kütüphane içerir) ve kendi kodunuzun yüzlerce ve binlerce satırını yazmayı öğrenmeniz gerekecektir. Ve birkaç harf ve rakam elde etmek için yapılan tüm bunlar açıkçası çok hantal ve mantıksız.

Bu nedenle, bilgi alışverişi için başka bir alternatif standart olduğu söylenebilir - XML-RPC. UserLand Software Inc. tarafından Microsoft'un katılımıyla geliştirilmiş olup, İnternet üzerinden uygulamalar arasında birleşik veri aktarımı için tasarlanmıştır. Gerçek web hizmetlerinin tüm "kurumsal" özelliklerinin gerekli olmadığı basit hizmetler oluştururken SOAP'ın yerini alabilir.

XML-RPC kısaltması ne anlama geliyor? RPC, Uzaktan Prosedür Çağrısı anlamına gelir. Bu, bir uygulamanın (ister sunucudaki bir komut dosyası ister istemci bilgisayardaki normal bir uygulama olsun) başka bir bilgisayarda fiziksel olarak uygulanan ve yürütülen bir yöntemi şeffaf bir şekilde kullanabileceği anlamına gelir. Burada XML, iletilen verileri tanımlamak için evrensel bir format sağlamak amacıyla kullanılır. Aktarım olarak HTTP protokolü, mesajları iletmek için kullanılır; bu, herhangi bir ağ cihazı (yönlendiriciler, güvenlik duvarları, proxy sunucuları) aracılığıyla sorunsuz bir şekilde veri alışverişinde bulunmanıza olanak tanır.

Ve bu nedenle, kullanmak için şunlara sahip olmanız gerekir: bir veya daha fazla yöntem sağlayan bir XML-RPC sunucusu, doğru bir istek oluşturabilen ve sunucu yanıtını işleyebilen ve ayrıca başarılı işlem için gerekli sunucu parametrelerini bilen bir XML-RPC istemcisi - adres, yöntem adı ve geçirilen parametreler.

XML-RPC ile yapılan tüm çalışmalar "istek-yanıt" modunda gerçekleşir; bu, hem işlem kavramlarının hem de gecikmeli arama yapma yeteneğinin (sunucu kaydettiği zaman) bulunduğu SOAP standardı ile teknoloji arasındaki farklardan biridir. istek ve gelecekte belirli bir zamanda yanıt verir). Bu ek özellikler, güçlü kurumsal hizmetler için daha kullanışlıdır; sunucuların geliştirilmesini ve desteklenmesini önemli ölçüde karmaşıklaştırır ve istemci çözümü geliştiricilerine ek gereksinimler yükler.

XML-RPC ile çalışma prosedürü bir istek oluşturmakla başlar. Tipik bir istek şuna benzer:

POST /RPC2 HTTP/1.0
Kullanıcı Aracısı: eshop-test/1.1.1 (FreeBSD)
Ana bilgisayar: sunucu.localnet.com
İçerik Türü: metin/xml
İçerik uzunluğu: 172



Test metodu
Merhaba XML-RPC!


İlk satırlar standart HTTP POST istek başlığını oluşturur. Gerekli parametreler ana bilgisayar, metin/xml olması gereken veri türü (MIME türü) ve mesaj uzunluğunu içerir. Standart ayrıca Kullanıcı Aracısı alanının doldurulması gerektiğini de belirtir ancak isteğe bağlı bir değer içerebilir.

Daha sonra XML belgesinin olağan başlığı gelir. İsteğin kök öğesi yalnızca bir tane olabilir ve alt düğümler gibi düğümleri içeremez. Bu, bir isteğin sunucuda yalnızca bir yöntemi çağırabileceği anlamına gelir.

Astar Test metodu TestMetod adlı bir yöntemi çağırdığımızı belirtir. Gerekirse, burada yöntemi içeren programın veya modülün adını ve yolunu belirtebilirsiniz. XML-RPC spesifikasyonu, bir yöntemi belirtmek için kullanılabilecek karakter kümesine bazı kısıtlamalar getirse de, bunların nasıl yorumlanacağı tamamen sunucu uygulamasına bağlıdır.

Daha sonra iletilen parametreler ayarlanır. Bu bölüm bunun için kullanılmaktadır. İsteğe bağlı sayıda alt öğe içerebilir Etiket tarafından açıklanan parametreyi içerenler . Parametrelere ve veri türlerine biraz daha ileride bakacağız. Bizim versiyonumuzda, yönteme etiketin içine alınmış bir dize parametresi iletilir .

Tüm parametrelerin açıklamasını kapatma etiketleri takip eder. XML-RPC'deki istek ve yanıt normal XML belgeleridir, dolayısıyla tüm etiketlerin kapatılması gerekir. Ancak XML standardında mevcut olmalarına rağmen XML-RPC'de tek bir etiket yoktur.

Şimdi sunucunun cevabına bakalım. HTTP yanıt başlığı normaldir; istek başarıyla işlenirse sunucu bir HTTP/1.1 200 OK yanıtı döndürür. Tıpkı istekte olduğu gibi MIME türünü, mesaj uzunluğunu ve yanıt oluşturma tarihini doğru bir şekilde belirtmeniz gerekir.

Yanıt gövdesinin kendisi aşağıdaki gibidir:



doğru


Şimdi kök etiketi yerine etiket belirtildi Talep işlemenin sonuçlarını hemen içeren. Ne yazık ki, yanıt yöntem adını iletmiyor; bu nedenle, aynı anda farklı yöntemler çağrılırsa karışıklığı önlemek için onu istemci tarafında saklamalısınız.

İsteğiniz işlenirken bir hata oluştuysa, bunun yerine Yanıt öğeyi içerecektir Hatayı açıklayan bir yapının iç içe yerleştirileceği. Hata açıklaması sayısal bir hata kodu ve bir metin açıklaması içerir.

Şimdi XML-RPC'deki veri türlerine kısaca göz atalım. Toplamda 9 veri türü vardır; yedi basit tür ve 2 karmaşık tür. Her tür kendi etiketi veya etiket kümesiyle (karmaşık türler için) tanımlanır.

Basit türler:

Bütün sayılar- etiket veya ;

Boole türü- etiket , hem 0/1 hem de doğru/yanlış değerlerini alabilir;

ASCII dizesi- etiketle tanımlandı ve isteğe bağlı bir karakter dizisi içerebilir;

Kayan nokta sayıları- etiket , bir sayı işareti de içerebilir; kesirli kısım bir noktayla ayrılır;

tarih ve saat- etiketle tanımlandı ve iso8601 formatına uygun olmalıdır. Bu format, komut dosyalarında daha fazla işlem yapmak için biraz elverişsizdir, bu nedenle bir istek gönderilirken/alınırken her zaman dönüştürülür. Bu, kitaplık içindeki özel bir işlevle yapılabilir veya böyle bir işlev yoksa geliştiricinin tarihi manuel olarak dönüştürmesi gerekir.

Son basit tür ise base64 kodlu dize etiketiyle açıklanan . Bu tür evrenseldir; bu tür kodlama nedeniyle aktarılan verilerin hacmi artmasına rağmen, istemci ile sunucu arasında herhangi bir veri aktarımı için kullanılabilir. Ancak bu, protokolün metinsel yapısının ve özellikle XML formatının bir sonucudur.

Karmaşık türler yapılar ve dizilerle temsil edilir. Yapı kök eleman tarafından belirlenir isteğe bağlı sayıda öğe içerebilen , yapının her bir üyesini tanımlar. Bir yapı elemanı iki etiketle tanımlanır: birincisi, , üyenin adını açıklar, ikincisi, , üyenin değerini içerir (veri türünü açıklayan bir etiketle birlikte).

Dizilerin adı yoktur ve etiketiyle tanımlanır bir element içeren ve bir veya daha fazla alt öğe , belirli verilerin belirtildiği yer. Bir dizi, çok boyutlu dizileri tanımlamanıza olanak tanıyan diğer dizilerin yanı sıra herhangi bir sırayla başka türleri de içerebilir. Ayrıca bir dizi yapıyı da tanımlayabilirsiniz. Ancak dizinin bir adı olmaması bazı durumlarda kullanımını karmaşık hale getirir; karmaşık verileri aktarmak için bunların tekrar tekrar başka türlere paketlenmesi gerekir (örneğin, birkaç diziyi aktarmak için her diziyi ayrı ayrı bir yapıya paketleyebilirsiniz). ve ardından bu yapılardan bir dizi oluşturun).

Elbette birisi böyle bir veri türü listesinin çok zayıf olduğunu ve "genişletilmesine izin vermediğini" söyleyecektir. Evet, karmaşık nesneleri veya büyük miktarda veriyi aktarmanız gerekiyorsa SOAP kullanmak daha iyidir. Ve küçük, iddiasız uygulamalar için XML-RPC oldukça uygundur; üstelik çoğu zaman yetenekleri bile çok fazla olur! Dağıtım kolaylığı, hemen hemen her dil ve platform için çok sayıda kitaplık ve PHP'deki geniş destek göz önüne alındığında, XML-RPC'nin genellikle hiçbir rakibi yoktur. Her ne kadar evrensel bir çözüm olarak hemen tavsiye edilemese de, her özel durumda, koşullara göre karar verilmelidir.